Adding some mocks for Google, Facebook and Twitter for E2Evalidation
Social mock tests are not executed right now due to 2 issues: 1. Identity extensions related to Social are not in yet. 2. Helios currently does not support loading of environment based startup class.
This commit is contained in:
parent
7e77078160
commit
093caaae7e
|
|
@ -0,0 +1,32 @@
|
|||
using Microsoft.AspNet.Http.Security;
|
||||
using Microsoft.AspNet.Security;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MusicStore.Mocks.Common
|
||||
{
|
||||
public class CustomStateDataFormat : ISecureDataFormat<AuthenticationProperties>
|
||||
{
|
||||
private static string lastSavedAuthenticationProperties;
|
||||
|
||||
public string Protect(AuthenticationProperties data)
|
||||
{
|
||||
lastSavedAuthenticationProperties = Serialize(data);
|
||||
return "ValidStateData";
|
||||
}
|
||||
|
||||
public AuthenticationProperties Unprotect(string state)
|
||||
{
|
||||
return state == "ValidStateData" ? DeSerialize(lastSavedAuthenticationProperties) : null;
|
||||
}
|
||||
|
||||
private string Serialize(AuthenticationProperties data)
|
||||
{
|
||||
return JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
}
|
||||
|
||||
private AuthenticationProperties DeSerialize(string state)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<AuthenticationProperties>(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Net
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// https://github.com/aspnet/HttpAbstractions/issues/121 - Helpers implemented here until that.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<string, string> ParseQueryString(this Uri uri)
|
||||
{
|
||||
var queryParameters = Uri.UnescapeDataString(uri.Query.TrimStart('?')).Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var queryItemCollection = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (queryParameters != null && queryParameters.Length > 0)
|
||||
{
|
||||
foreach (var queryParameter in queryParameters)
|
||||
{
|
||||
var queryParameterParts = queryParameter.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var value = queryParameterParts.Length == 1 ? string.Empty : queryParameterParts[1];
|
||||
queryItemCollection.Add(queryParameterParts[0], value);
|
||||
}
|
||||
}
|
||||
|
||||
return queryItemCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Mocks.Common
|
||||
{
|
||||
internal class Helpers
|
||||
{
|
||||
internal static void ThrowIfConditionFailed(Func<bool> condition, string errorMessage)
|
||||
{
|
||||
if (!condition())
|
||||
{
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for FacebookMockBackChannelHttpHandler
|
||||
/// </summary>
|
||||
public class FacebookMockBackChannelHttpHandler : WebRequestHandler
|
||||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
var queryParameters = request.RequestUri.ParseQueryString();
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://graph.facebook.com/oauth/access_token"))
|
||||
{
|
||||
if (queryParameters["grant_type"] == "authorization_code")
|
||||
{
|
||||
if (queryParameters["code"] == "ValidCode")
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => queryParameters["redirect_uri"].EndsWith("signin-facebook"), "Redirect URI is not ending with /signin-facebook");
|
||||
Helpers.ThrowIfConditionFailed(() => queryParameters["client_id"] == "[AppId]", "Invalid client Id received");
|
||||
Helpers.ThrowIfConditionFailed(() => queryParameters["client_secret"] == "[AppSecret]", "Invalid client secret received");
|
||||
response.Content = new StringContent("access_token=ValidAccessToken&expires=100");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://graph.facebook.com/me"))
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => queryParameters["appsecret_proof"] != null, "appsecret_proof is null");
|
||||
if (queryParameters["access_token"] == "ValidAccessToken")
|
||||
{
|
||||
response.Content = new StringContent("{\"id\":\"Id\",\"name\":\"AspnetvnextTest AspnetvnextTest\",\"first_name\":\"AspnetvnextTest\",\"last_name\":\"AspnetvnextTest\",\"link\":\"https:\\/\\/www.facebook.com\\/myLink\",\"username\":\"AspnetvnextTest.AspnetvnextTest.7\",\"gender\":\"male\",\"email\":\"AspnetvnextTest\\u0040gmail.com\",\"timezone\":-7,\"locale\":\"en_US\",\"verified\":true,\"updated_time\":\"2013-08-06T20:38:48+0000\",\"CertValidatorInvoked\":\"ValidAccessToken\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Content = new StringContent("{\"error\":{\"message\":\"Invalid OAuth access token.\",\"type\":\"OAuthException\",\"code\":190}}");
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
using Microsoft.AspNet.Security.Facebook;
|
||||
using Microsoft.AspNet.Security.OAuth;
|
||||
using MusicStore.Mocks.Common;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MusicStore.Mocks.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for FacebookNotifications
|
||||
/// </summary>
|
||||
internal class FacebookNotifications
|
||||
{
|
||||
internal static async Task OnAuthenticated(FacebookAuthenticatedContext context)
|
||||
{
|
||||
if (context.Identity != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Email == "AspnetvnextTest@gmail.com", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Id == "Id", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Link == "https://www.facebook.com/myLink", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Name == "AspnetvnextTest AspnetvnextTest", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.UserName == "AspnetvnextTest.AspnetvnextTest.7", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.User.SelectToken("id").ToString() == context.Id, "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(100), "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "");
|
||||
context.Identity.AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static async Task OnReturnEndpoint(OAuthReturnEndpointContext context)
|
||||
{
|
||||
if (context.Identity != null && context.SignInAsAuthenticationType == "External")
|
||||
{
|
||||
//This way we will know all notifications were fired.
|
||||
var manageStoreClaim = context.Identity.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
context.Identity.RemoveClaim(manageStoreClaim);
|
||||
context.Identity.AddClaim(new Claim("ManageStore", "true"));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static void OnApplyRedirect(OAuthApplyRedirectContext context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.WebUtilities;
|
||||
|
||||
namespace MusicStore.Mocks.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for GoogleMockBackChannelHttpHandler
|
||||
/// </summary>
|
||||
public class GoogleMockBackChannelHttpHandler : WebRequestHandler
|
||||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://accounts.google.com/o/oauth2/token"))
|
||||
{
|
||||
var formData = FormHelpers.ParseForm(await request.Content.ReadAsStringAsync());
|
||||
if (formData["grant_type"] == "authorization_code")
|
||||
{
|
||||
if (formData["code"] == "ValidCode")
|
||||
{
|
||||
if (formData["redirect_uri"] != null && formData["redirect_uri"].EndsWith("signin-google") &&
|
||||
formData["client_id"] == "[ClientId]" && formData["client_secret"] == "[ClientSecret]")
|
||||
{
|
||||
response.Content = new StringContent("{\"access_token\":\"ValidAccessToken\",\"refresh_token\":\"ValidRefreshToken\",\"token_type\":\"Bearer\",\"expires_in\":\"1200\",\"id_token\":\"Token\"}", Encoding.UTF8, "application/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://www.googleapis.com/plus/v1/people/me"))
|
||||
{
|
||||
if (request.Headers.Authorization.Parameter == "ValidAccessToken")
|
||||
{
|
||||
response.Content = new StringContent("{ \"kind\": \"plus#person\",\n \"etag\": \"\\\"YFr-hUROXQN7IOa3dUHg9dQ8eq0/2hY18HdHEP8NLykSTVEiAhkKsBE\\\"\",\n \"gender\": \"male\",\n \"emails\": [\n {\n \"value\": \"AspnetvnextTest@gmail.com\",\n \"type\": \"account\"\n }\n ],\n \"objectType\": \"person\",\n \"id\": \"106790274378320830963\",\n \"displayName\": \"AspnetvnextTest AspnetvnextTest\",\n \"name\": {\n \"familyName\": \"AspnetvnextTest\",\n \"givenName\": \"FirstName\"\n },\n \"url\": \"https://plus.google.com/106790274378320830963\",\n \"image\": {\n \"url\": \"https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg?sz=50\"\n },\n \"isPlusUser\": true,\n \"language\": \"en\",\n \"circledByCount\": 0,\n \"verified\": false\n}\n", Encoding.UTF8, "application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Content = new StringContent("{\"error\":{\"message\":\"Invalid OAuth access token.\",\"type\":\"OAuthException\",\"code\":190}}");
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using Microsoft.AspNet.Security.Google;
|
||||
using Microsoft.AspNet.Security.OAuth;
|
||||
using MusicStore.Mocks.Common;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MusicStore.Mocks.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for GoogleNotifications
|
||||
/// </summary>
|
||||
internal class GoogleNotifications
|
||||
{
|
||||
internal static async Task OnAuthenticated(GoogleAuthenticatedContext context)
|
||||
{
|
||||
if (context.Identity != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "Access token is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.RefreshToken == "ValidRefreshToken", "Refresh token is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Email == "AspnetvnextTest@gmail.com", "Email is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Id == "106790274378320830963", "Id is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.FamilyName == "AspnetvnextTest", "FamilyName is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Name == "AspnetvnextTest AspnetvnextTest", "Name is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(1200), "ExpiresIn is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.User != null, "User object is not valid");
|
||||
context.Identity.AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static async Task OnReturnEndpoint(OAuthReturnEndpointContext context)
|
||||
{
|
||||
if (context.Identity != null && context.SignInAsAuthenticationType == "External")
|
||||
{
|
||||
//This way we will know all notifications were fired.
|
||||
var manageStoreClaim = context.Identity.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
context.Identity.RemoveClaim(manageStoreClaim);
|
||||
context.Identity.AddClaim(new Claim("ManageStore", "true"));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static void OnApplyRedirect(OAuthApplyRedirectContext context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Only for testing purposes. Not needed for production deployments.
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Diagnostics;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Security.Cookies;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Framework.ConfigurationModel;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using MusicStore.Models;
|
||||
using Microsoft.AspNet.Security.Facebook;
|
||||
using Microsoft.AspNet.Security.Google;
|
||||
using Microsoft.AspNet.Security.Twitter;
|
||||
using Microsoft.AspNet.Security.MicrosoftAccount;
|
||||
using Microsoft.AspNet.Security;
|
||||
using Microsoft.AspNet.MemoryCache;
|
||||
using MusicStore.Mocks.Common;
|
||||
using MusicStore.Mocks.Facebook;
|
||||
using MusicStore.Mocks.Twitter;
|
||||
using MusicStore.Mocks.Google;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
public class StartupSocialTesting
|
||||
{
|
||||
public void Configure(IBuilder app)
|
||||
{
|
||||
Console.WriteLine("Social Testing mode...");
|
||||
//Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources,
|
||||
//then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
|
||||
var configuration = new Configuration();
|
||||
configuration.AddJsonFile("LocalConfig.json");
|
||||
configuration.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
|
||||
|
||||
//Error page middleware displays a nice formatted HTML page for any unhandled exceptions in the request pipeline.
|
||||
//Note: ErrorPageOptions.ShowAll to be used only at development time. Not recommended for production.
|
||||
app.UseErrorPage(ErrorPageOptions.ShowAll);
|
||||
|
||||
app.SetDefaultSignInAsAuthenticationType("External");
|
||||
|
||||
app.UseServices(services =>
|
||||
{
|
||||
//If this type is present - we're on mono
|
||||
var runningOnMono = Type.GetType("Mono.Runtime") != null;
|
||||
|
||||
// Add EF services to the services container
|
||||
if (runningOnMono)
|
||||
{
|
||||
services.AddEntityFramework()
|
||||
.AddInMemoryStore();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddEntityFramework()
|
||||
.AddSqlServer();
|
||||
}
|
||||
|
||||
services.AddScoped<MusicStoreContext>();
|
||||
|
||||
// Configure DbContext
|
||||
services.SetupOptions<MusicStoreDbContextOptions>(options =>
|
||||
{
|
||||
options.DefaultAdminUserName = configuration.Get("DefaultAdminUsername");
|
||||
options.DefaultAdminPassword = configuration.Get("DefaultAdminPassword");
|
||||
if (runningOnMono)
|
||||
{
|
||||
options.UseInMemoryStore();
|
||||
}
|
||||
else
|
||||
{
|
||||
options.UseSqlServer(configuration.Get("Data:DefaultConnection:ConnectionString"));
|
||||
}
|
||||
});
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentitySqlServer<MusicStoreContext, ApplicationUser>()
|
||||
.AddAuthentication();
|
||||
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc();
|
||||
|
||||
//Add all SignalR related services to IoC.
|
||||
services.AddSignalR();
|
||||
|
||||
//Add InMemoryCache
|
||||
//Currently not able to AddSingleTon
|
||||
services.AddInstance<IMemoryCache>(new MemoryCache());
|
||||
});
|
||||
|
||||
//Configure SignalR
|
||||
app.UseSignalR();
|
||||
|
||||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||
{
|
||||
AuthenticationType = "External",
|
||||
AuthenticationMode = AuthenticationMode.Passive,
|
||||
ExpireTimeSpan = TimeSpan.FromMinutes(5)
|
||||
});
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||
{
|
||||
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
||||
LoginPath = new PathString("/Account/Login")
|
||||
});
|
||||
|
||||
app.UseTwoFactorSignInCookies();
|
||||
|
||||
var facebookOptions = new FacebookAuthenticationOptions()
|
||||
{
|
||||
AppId = "[AppId]",
|
||||
AppSecret = "[AppSecret]",
|
||||
Notifications = new FacebookAuthenticationNotifications()
|
||||
{
|
||||
OnAuthenticated = FacebookNotifications.OnAuthenticated,
|
||||
OnReturnEndpoint = FacebookNotifications.OnReturnEndpoint,
|
||||
OnApplyRedirect = FacebookNotifications.OnApplyRedirect
|
||||
},
|
||||
BackchannelHttpHandler = new FacebookMockBackChannelHttpHandler(),
|
||||
StateDataFormat = new CustomStateDataFormat()
|
||||
};
|
||||
|
||||
facebookOptions.Scope.Add("email");
|
||||
facebookOptions.Scope.Add("read_friendlists");
|
||||
facebookOptions.Scope.Add("user_checkins");
|
||||
|
||||
app.UseFacebookAuthentication(facebookOptions);
|
||||
|
||||
app.UseGoogleAuthentication(new GoogleAuthenticationOptions()
|
||||
{
|
||||
ClientId = "[ClientId]",
|
||||
ClientSecret = "[ClientSecret]",
|
||||
AccessType = "offline",
|
||||
Notifications = new GoogleAuthenticationNotifications()
|
||||
{
|
||||
OnAuthenticated = GoogleNotifications.OnAuthenticated,
|
||||
OnReturnEndpoint = GoogleNotifications.OnReturnEndpoint,
|
||||
OnApplyRedirect = GoogleNotifications.OnApplyRedirect
|
||||
},
|
||||
StateDataFormat = new CustomStateDataFormat(),
|
||||
BackchannelHttpHandler = new GoogleMockBackChannelHttpHandler()
|
||||
});
|
||||
|
||||
app.UseTwitterAuthentication(new TwitterAuthenticationOptions()
|
||||
{
|
||||
ConsumerKey = "[ConsumerKey]",
|
||||
ConsumerSecret = "[ConsumerSecret]",
|
||||
Notifications = new TwitterAuthenticationNotifications()
|
||||
{
|
||||
OnAuthenticated = TwitterNotifications.OnAuthenticated,
|
||||
OnReturnEndpoint = TwitterNotifications.OnReturnEndpoint,
|
||||
OnApplyRedirect = TwitterNotifications.OnApplyRedirect
|
||||
},
|
||||
StateDataFormat = new CustomTwitterStateDataFormat(),
|
||||
BackchannelHttpHandler = new TwitterMockBackChannelHttpHandler()
|
||||
});
|
||||
|
||||
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions()
|
||||
{
|
||||
Caption = "MicrosoftAccount - Requires project changes",
|
||||
ClientId = "[ClientId]",
|
||||
ClientSecret = "[ClientSecret]",
|
||||
});
|
||||
|
||||
// Add MVC to the request pipeline
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
name: "areaRoute",
|
||||
template: "{area:exists}/{controller}/{action}",
|
||||
defaults: new { action = "Index" });
|
||||
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
routes.MapRoute(
|
||||
name: "api",
|
||||
template: "{controller}/{id?}");
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using Microsoft.AspNet.Security;
|
||||
using Microsoft.AspNet.Security.Twitter.Messages;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MusicStore.Mocks.Twitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for CustomTwitterStateDataFormat
|
||||
/// </summary>
|
||||
public class CustomTwitterStateDataFormat : ISecureDataFormat<RequestToken>
|
||||
{
|
||||
private static string lastSavedRequestToken;
|
||||
|
||||
public string Protect(RequestToken data)
|
||||
{
|
||||
data.Token = "valid_oauth_token";
|
||||
lastSavedRequestToken = Serialize(data);
|
||||
return "valid_oauth_token";
|
||||
}
|
||||
|
||||
public RequestToken Unprotect(string state)
|
||||
{
|
||||
return state == "valid_oauth_token" ? DeSerialize(lastSavedRequestToken) : null;
|
||||
}
|
||||
|
||||
private string Serialize(RequestToken data)
|
||||
{
|
||||
return JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
}
|
||||
|
||||
private RequestToken DeSerialize(string state)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<RequestToken>(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.WebUtilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MusicStore.Mocks.Twitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for TwitterMockBackChannelHttpHandler
|
||||
/// </summary>
|
||||
public class TwitterMockBackChannelHttpHandler : WebRequestHandler
|
||||
{
|
||||
private static bool RequestTokenEndpointInvoked = false;
|
||||
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
Console.WriteLine(request.RequestUri.AbsoluteUri);
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://api.twitter.com/oauth/access_token"))
|
||||
{
|
||||
var formData = FormHelpers.ParseForm(await request.Content.ReadAsStringAsync());
|
||||
if (formData["oauth_verifier"] == "valid_oauth_verifier")
|
||||
{
|
||||
if (RequestTokenEndpointInvoked)
|
||||
{
|
||||
var response_Form_data = new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
new KeyValuePair<string, string>("oauth_token", "valid_oauth_token"),
|
||||
new KeyValuePair<string, string>("oauth_token_secret", "valid_oauth_token_secret"),
|
||||
new KeyValuePair<string, string>("user_id", "valid_user_id"),
|
||||
new KeyValuePair<string, string>("screen_name", "valid_screen_name"),
|
||||
};
|
||||
|
||||
response.Content = new FormUrlEncodedContent(response_Form_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.StatusCode = HttpStatusCode.InternalServerError;
|
||||
response.Content = new StringContent("RequestTokenEndpoint is not invoked");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://api.twitter.com/oauth/request_token"))
|
||||
{
|
||||
var response_Form_data = new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
new KeyValuePair<string, string>("oauth_callback_confirmed", "true"),
|
||||
new KeyValuePair<string, string>("oauth_token", "valid_oauth_token"),
|
||||
new KeyValuePair<string, string>("oauth_token_secret", "valid_oauth_token_secret")
|
||||
};
|
||||
|
||||
RequestTokenEndpointInvoked = true;
|
||||
response.Content = new FormUrlEncodedContent(response_Form_data);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using Microsoft.AspNet.Security.Twitter;
|
||||
using MusicStore.Mocks.Common;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MusicStore.Mocks.Twitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for TwitterNotifications
|
||||
/// </summary>
|
||||
internal class TwitterNotifications
|
||||
{
|
||||
internal static async Task OnAuthenticated(TwitterAuthenticatedContext context)
|
||||
{
|
||||
if (context.Identity != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.UserId == "valid_user_id", "UserId is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ScreenName == "valid_screen_name", "ScreenName is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "valid_oauth_token", "AccessToken is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessTokenSecret == "valid_oauth_token_secret", "AccessTokenSecret is not valid");
|
||||
context.Identity.AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static async Task OnReturnEndpoint(TwitterReturnEndpointContext context)
|
||||
{
|
||||
if (context.Identity != null && context.SignInAsAuthenticationType == "External")
|
||||
{
|
||||
//This way we will know all notifications were fired.
|
||||
var manageStoreClaim = context.Identity.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
context.Identity.RemoveClaim(manageStoreClaim);
|
||||
context.Identity.AddClaim(new Claim("ManageStore", "true"));
|
||||
}
|
||||
}
|
||||
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static void OnApplyRedirect(TwitterApplyRedirectContext context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Net
|
||||
{
|
||||
|
|
@ -16,5 +16,27 @@ namespace System.Net
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/aspnet/HttpAbstractions/issues/121 - Helpers implemented here until that.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<string, string> ParseQueryString(this Uri uri)
|
||||
{
|
||||
var queryParameters = Uri.UnescapeDataString(uri.Query.TrimStart('?')).Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var queryItemCollection = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (queryParameters != null && queryParameters.Length > 0)
|
||||
{
|
||||
foreach (var queryParameter in queryParameters)
|
||||
{
|
||||
var queryParameterParts = queryParameter.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var value = queryParameterParts.Length == 1 ? string.Empty : queryParameterParts[1];
|
||||
queryItemCollection.Add(queryParameterParts[0], value);
|
||||
}
|
||||
}
|
||||
|
||||
return queryItemCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace E2ETests
|
||||
{
|
||||
public partial class SmokeTests
|
||||
{
|
||||
private void LoginWithFacebook()
|
||||
{
|
||||
httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = false };
|
||||
httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(ApplicationBaseUrl) };
|
||||
|
||||
var response = httpClient.GetAsync("Account/Login").Result;
|
||||
ThrowIfResponseStatusNotOk(response);
|
||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
Console.WriteLine("Signing in with Facebook account");
|
||||
var formParameters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("provider", "Facebook"),
|
||||
new KeyValuePair<string, string>("returnUrl", "/"),
|
||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLogin")),
|
||||
};
|
||||
|
||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||
response = httpClient.PostAsync("Account/ExternalLogin", content).Result;
|
||||
Assert.Equal<string>("https://www.facebook.com/dialog/oauth", response.Headers.Location.AbsoluteUri.Replace(response.Headers.Location.Query, string.Empty));
|
||||
var queryItems = response.Headers.Location.ParseQueryString();
|
||||
Assert.Equal<string>("code", queryItems["response_type"]);
|
||||
Assert.Equal<string>("[AppId]", queryItems["client_id"]);
|
||||
Assert.Equal<string>(ApplicationBaseUrl + "signin-facebook", queryItems["redirect_uri"]);
|
||||
Assert.Equal<string>("email,read_friendlists,user_checkins", queryItems["scope"]);
|
||||
Assert.Equal<string>("ValidStateData", queryItems["state"]);
|
||||
Assert.Equal<string>("custom", queryItems["custom_redirect_uri"]);
|
||||
//Check for the correlation cookie
|
||||
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Correlation.Facebook"));
|
||||
|
||||
//This is just to generate a correlation cookie. Previous step would generate this cookie, but we have reset the handler now.
|
||||
httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = true };
|
||||
httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(ApplicationBaseUrl) };
|
||||
|
||||
response = httpClient.GetAsync("Account/Login").Result;
|
||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
formParameters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("provider", "Facebook"),
|
||||
new KeyValuePair<string, string>("returnUrl", "/"),
|
||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLogin")),
|
||||
};
|
||||
|
||||
content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||
response = httpClient.PostAsync("Account/ExternalLogin", content).Result;
|
||||
|
||||
//Post a message to the Facebook middleware
|
||||
response = httpClient.GetAsync("signin-facebook?code=ValidCode&state=ValidStateData").Result;
|
||||
//This should land us in ExternalLoginCallBack - this action is not implemented yet. We need to wait to complete automation.
|
||||
|
||||
//Correlation cookie not getting cleared after successful signin?
|
||||
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Correlation.Facebook"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace E2ETests
|
||||
{
|
||||
public partial class SmokeTests
|
||||
{
|
||||
private void LoginWithGoogle()
|
||||
{
|
||||
httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = false };
|
||||
httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(ApplicationBaseUrl) };
|
||||
|
||||
var response = httpClient.GetAsync("Account/Login").Result;
|
||||
ThrowIfResponseStatusNotOk(response);
|
||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
Console.WriteLine("Signing in with Google account");
|
||||
var formParameters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("provider", "Google"),
|
||||
new KeyValuePair<string, string>("returnUrl", "/"),
|
||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLogin")),
|
||||
};
|
||||
|
||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||
response = httpClient.PostAsync("Account/ExternalLogin", content).Result;
|
||||
Assert.Equal<string>("https://accounts.google.com/o/oauth2/auth", response.Headers.Location.AbsoluteUri.Replace(response.Headers.Location.Query, string.Empty));
|
||||
var queryItems = response.Headers.Location.ParseQueryString();
|
||||
Assert.Equal<string>("code", queryItems["response_type"]);
|
||||
Assert.Equal<string>("offline", queryItems["access_type"]);
|
||||
Assert.Equal<string>("[ClientId]", queryItems["client_id"]);
|
||||
Assert.Equal<string>(ApplicationBaseUrl + "signin-google", queryItems["redirect_uri"]);
|
||||
Assert.Equal<string>("openid profile email", queryItems["scope"]);
|
||||
Assert.Equal<string>("ValidStateData", queryItems["state"]);
|
||||
Assert.Equal<string>("custom", queryItems["custom_redirect_uri"]);
|
||||
//Check for the correlation cookie
|
||||
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Correlation.Google"));
|
||||
|
||||
//This is just to generate a correlation cookie. Previous step would generate this cookie, but we have reset the handler now.
|
||||
httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = true };
|
||||
httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(ApplicationBaseUrl) };
|
||||
|
||||
response = httpClient.GetAsync("Account/Login").Result;
|
||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
formParameters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("provider", "Google"),
|
||||
new KeyValuePair<string, string>("returnUrl", "/"),
|
||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLogin")),
|
||||
};
|
||||
|
||||
content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||
response = httpClient.PostAsync("Account/ExternalLogin", content).Result;
|
||||
|
||||
//Post a message to the Google middleware
|
||||
response = httpClient.GetAsync("signin-google?code=ValidCode&state=ValidStateData").Result;
|
||||
//This should land us in ExternalLoginCallBack - this action is not implemented yet. We need to wait to complete automation.
|
||||
|
||||
//Correlation cookie not getting cleared after successful signin?
|
||||
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Correlation.Google"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace E2ETests
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for TwitterLoginScenarios
|
||||
/// </summary>
|
||||
public partial class SmokeTests
|
||||
{
|
||||
private void LoginWithTwitter()
|
||||
{
|
||||
httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = false };
|
||||
httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(ApplicationBaseUrl) };
|
||||
|
||||
var response = httpClient.GetAsync("Account/Login").Result;
|
||||
ThrowIfResponseStatusNotOk(response);
|
||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
Console.WriteLine("Signing in with Twitter account");
|
||||
var formParameters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("provider", "Twitter"),
|
||||
new KeyValuePair<string, string>("returnUrl", "/"),
|
||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLogin")),
|
||||
};
|
||||
|
||||
var content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||
response = httpClient.PostAsync("Account/ExternalLogin", content).Result;
|
||||
Assert.Equal<string>("https://twitter.com/oauth/authenticate", response.Headers.Location.AbsoluteUri.Replace(response.Headers.Location.Query, string.Empty));
|
||||
var queryItems = response.Headers.Location.ParseQueryString();
|
||||
Assert.Equal<string>("custom", queryItems["custom_redirect_uri"]);
|
||||
Assert.Equal<string>("valid_oauth_token", queryItems["oauth_token"]);
|
||||
//Check for the correlation cookie
|
||||
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl))["__TwitterState"]);
|
||||
|
||||
//This is just to generate a correlation cookie. Previous step would generate this cookie, but we have reset the handler now.
|
||||
httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = true };
|
||||
httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(ApplicationBaseUrl) };
|
||||
|
||||
response = httpClient.GetAsync("Account/Login").Result;
|
||||
responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
formParameters = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("provider", "Twitter"),
|
||||
new KeyValuePair<string, string>("returnUrl", "/"),
|
||||
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLogin")),
|
||||
};
|
||||
|
||||
content = new FormUrlEncodedContent(formParameters.ToArray());
|
||||
response = httpClient.PostAsync("Account/ExternalLogin", content).Result;
|
||||
|
||||
//Post a message to the Facebook middleware
|
||||
response = httpClient.GetAsync("signin-twitter?oauth_token=valid_oauth_token&oauth_verifier=valid_oauth_verifier").Result;
|
||||
//This should land us in ExternalLoginCallBack - this action is not implemented yet. We need to wait to complete automation.
|
||||
|
||||
//Correlation cookie not getting cleared after successful signin?
|
||||
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl))["__TwitterState"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue