Adding some mocks for MicrosoftAccount

This commit is contained in:
Praburaj 2014-09-29 14:41:43 -07:00
parent 1d52705f8a
commit f16a0c0f19
15 changed files with 277 additions and 18 deletions

View File

@ -175,6 +175,7 @@ namespace MusicStore.Areas.Admin.Controllers
return RedirectToAction("Index");
}
#if TESTING
//
// GET: /StoreManager/GetAlbumIdFromName
// Note: Added for automated testing purpose. Application does not use this.
@ -190,5 +191,6 @@ namespace MusicStore.Areas.Admin.Controllers
return new ContentResult { Content = album.AlbumId.ToString(), ContentType = "text/plain" };
}
#endif
}
}

View File

@ -376,6 +376,16 @@ namespace MusicStore.Controllers
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user);
#if TESTING
//Just for automated testing adding a claim named 'ManageStore' - Not required for production
var manageClaim = info.ExternalIdentity.Claims.Where(c => c.Type == "ManageStore").FirstOrDefault();
if (manageClaim != null)
{
await UserManager.AddClaimAsync(user, manageClaim);
}
#endif
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user, info);

View File

@ -34,14 +34,14 @@ namespace MusicStore.Mocks.Facebook
internal static async Task OnReturnEndpoint(OAuthReturnEndpointContext context)
{
if (context.Identity != null && context.SignInAsAuthenticationType == "External")
if (context.Identity != null && context.SignInAsAuthenticationType == "Microsoft.AspNet.Identity.ExternalLogin")
{
//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"));
context.Identity.AddClaim(new Claim("ManageStore", "Allowed"));
}
}

View File

@ -33,14 +33,14 @@ namespace MusicStore.Mocks.Google
internal static async Task OnReturnEndpoint(OAuthReturnEndpointContext context)
{
if (context.Identity != null && context.SignInAsAuthenticationType == "External")
if (context.Identity != null && context.SignInAsAuthenticationType == "Microsoft.AspNet.Identity.ExternalLogin")
{
//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"));
context.Identity.AddClaim(new Claim("ManageStore", "Allowed"));
}
}

View File

@ -0,0 +1,49 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using Microsoft.AspNet.WebUtilities;
namespace MusicStore.Mocks.MicrosoftAccount
{
/// <summary>
/// Summary description for MicrosoftAccountMockBackChannelHandler
/// </summary>
public class MicrosoftAccountMockBackChannelHandler : HttpMessageHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = new HttpResponseMessage();
if (request.RequestUri.AbsoluteUri.StartsWith("https://login.live.com/oauth20_token.srf"))
{
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-microsoft") &&
formData["client_id"] == "[ClientId]" && formData["client_secret"] == "[ClientSecret]")
{
System.Console.WriteLine("Handler2");
response.Content = new StringContent("{\"token_type\":\"bearer\",\"expires_in\":3600,\"scope\":\"wl.basic\",\"access_token\":\"ValidAccessToken\",\"refresh_token\":\"ValidRefreshToken\",\"authentication_token\":\"ValidAuthenticationToken\"}");
}
}
}
}
else if (request.RequestUri.AbsoluteUri.StartsWith("https://apis.live.net/v5.0/me"))
{
if (request.Headers.Authorization.Parameter == "ValidAccessToken")
{
response.Content = new StringContent("{\r \"id\": \"fccf9a24999f4f4f\", \r \"name\": \"AspnetvnextTest AspnetvnextTest\", \r \"first_name\": \"AspnetvnextTest\", \r \"last_name\": \"AspnetvnextTest\", \r \"link\": \"https://profile.live.com/\", \r \"gender\": null, \r \"locale\": \"en_US\", \r \"updated_time\": \"2013-08-27T22:18:14+0000\"\r}");
}
else
{
response.Content = new StringContent("{\r \"error\": {\r \"code\": \"request_token_invalid\", \r \"message\": \"The access token isn't valid.\"\r }\r}", Encoding.UTF8, "text/javascript");
}
}
return response;
}
}
}

View File

@ -0,0 +1,56 @@
using Microsoft.AspNet.Security.MicrosoftAccount;
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.MicrosoftAccount
{
/// <summary>
/// Summary description for MicrosoftAccountNotifications
/// </summary>
internal class MicrosoftAccountNotifications
{
internal static async Task OnAuthenticated(MicrosoftAccountAuthenticatedContext 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.FirstName == "AspnetvnextTest", "Email is not valid");
Helpers.ThrowIfConditionFailed(() => context.LastName == "AspnetvnextTest", "Email is not valid");
Helpers.ThrowIfConditionFailed(() => context.Id == "fccf9a24999f4f4f", "Id is not valid");
Helpers.ThrowIfConditionFailed(() => context.Name == "AspnetvnextTest AspnetvnextTest", "Name is not valid");
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(3600), "ExpiresIn is not valid");
Helpers.ThrowIfConditionFailed(() => context.User != null, "User object is not valid");
Helpers.ThrowIfConditionFailed(() => context.Id == context.User.SelectToken("id").ToString(), "User id 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 == "Microsoft.AspNet.Identity.ExternalLogin")
{
//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", "Allowed"));
}
}
await Task.FromResult(0);
}
internal static void OnApplyRedirect(OAuthApplyRedirectContext context)
{
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
}
}
}

View File

@ -21,6 +21,7 @@ using MusicStore.Mocks.Twitter;
using MusicStore.Mocks.Google;
using Microsoft.Framework.Runtime;
using System.Threading.Tasks;
using MusicStore.Mocks.MicrosoftAccount;
namespace MusicStore
{
@ -170,12 +171,24 @@ namespace MusicStore
#endif
});
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions()
var microsoftAccountOptions = new MicrosoftAccountAuthenticationOptions()
{
Caption = "MicrosoftAccount - Requires project changes",
ClientId = "[ClientId]",
ClientSecret = "[ClientSecret]",
});
Notifications = new MicrosoftAccountAuthenticationNotifications()
{
OnAuthenticated = MicrosoftAccountNotifications.OnAuthenticated,
OnReturnEndpoint = MicrosoftAccountNotifications.OnReturnEndpoint,
OnApplyRedirect = MicrosoftAccountNotifications.OnApplyRedirect
},
BackchannelHttpHandler = new MicrosoftAccountMockBackChannelHandler(),
StateDataFormat = new CustomStateDataFormat()
};
microsoftAccountOptions.Scope.Add("wl.basic");
microsoftAccountOptions.Scope.Add("wl.signin");
app.UseMicrosoftAccountAuthentication(microsoftAccountOptions);
// Add MVC to the request pipeline
app.UseMvc(routes =>

View File

@ -27,14 +27,14 @@ namespace MusicStore.Mocks.Twitter
internal static async Task OnReturnEndpoint(TwitterReturnEndpointContext context)
{
if (context.Identity != null && context.SignInAsAuthenticationType == "External")
if (context.Identity != null && context.SignInAsAuthenticationType == "Microsoft.AspNet.Identity.ExternalLogin")
{
//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"));
context.Identity.AddClaim(new Claim("ManageStore", "Allowed"));
}
}

View File

@ -104,6 +104,21 @@ namespace MusicStore
ConsumerSecret = "jUBYkQuBFyqp7G3CUB9SW3AfflFr9z3oQBiNvumYy87Al0W4h8",
});
//The MicrosoftAccount service has restrictions that prevent the use of http://localhost:5001/ for test applications.
//As such, here is how to change this sample to uses http://ktesting.com:5001/ instead.
//Edit the Project.json file and replace http://localhost:5001/ with http://ktesting.com:5001/.
//From an admin command console first enter:
// notepad C:\Windows\System32\drivers\etc\hosts
//and add this to the file, save, and exit (and reboot?):
// 127.0.0.1 ktesting.com
//Then you can choose to run the app as admin (see below) or add the following ACL as admin:
// netsh http add urlacl url=http://ktesting:12345/ user=[domain\user]
//The sample app can then be run via:
// k web
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions()
{
Caption = "MicrosoftAccount - Requires project changes",

View File

@ -4,7 +4,7 @@
],
"description": "Music store application on K",
"version": "1.0.0-*",
"compilationOptions": { "warningsAsErrors": true, "define": [ "DEMO" ] },
"compilationOptions": { "warningsAsErrors": true, "define": [ "DEMO", "TESTING" ] },
"webroot": "wwwroot",
"dependencies": {
"Kestrel": "1.0.0-*",

View File

@ -79,10 +79,15 @@ namespace E2ETests
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
//Verify cookie sent
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
//https://github.com/aspnet/Identity/issues/210
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Console.WriteLine("Successfully signed in with user '{0}'", "AspnetvnextTest@test.com");
Console.WriteLine("Verifying if the middleware notifications were fired");
//Check for a non existing item
response = httpClient.GetAsync(string.Format("Admin/StoreManager/GetAlbumIdFromName?albumName={0}", "123")).Result;
//This action requires admin permissions. If notifications are fired this permission is granted
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Console.WriteLine("Middleware notifications were fired successfully");
}
}
}

View File

@ -80,10 +80,16 @@ namespace E2ETests
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
//Verify cookie sent
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
//https://github.com/aspnet/Identity/issues/210
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Console.WriteLine("Successfully signed in with user '{0}'", "AspnetvnextTest@gmail.com");
Console.WriteLine("Verifying if the middleware notifications were fired");
//Check for a non existing item
response = httpClient.GetAsync(string.Format("Admin/StoreManager/GetAlbumIdFromName?albumName={0}", "123")).Result;
//This action requires admin permissions. If notifications are fired this permission is granted
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Console.WriteLine("Middleware notifications were fired successfully");
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net;
using Xunit;
using Microsoft.AspNet.WebUtilities;
namespace E2ETests
{
public partial class SmokeTests
{
private void LoginWithMicrosoftAccount()
{
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 Microsoft account");
var formParameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("provider", "Microsoft"),
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://login.live.com/oauth20_authorize.srf", response.Headers.Location.AbsoluteUri.Replace(response.Headers.Location.Query, string.Empty));
var queryItems = QueryHelpers.ParseQuery(response.Headers.Location.Query);
Assert.Equal<string>("code", queryItems["response_type"]);
Assert.Equal<string>("[ClientId]", queryItems["client_id"]);
Assert.Equal<string>(ApplicationBaseUrl + "signin-microsoft", queryItems["redirect_uri"]);
Assert.Equal<string>("wl.basic wl.signin", 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.Microsoft"));
//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", "Microsoft"),
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 MicrosoftAccount middleware
response = httpClient.GetAsync("signin-microsoft?code=ValidCode&state=ValidStateData").Result;
ThrowIfResponseStatusNotOk(response);
responseContent = response.Content.ReadAsStringAsync().Result;
//Correlation cookie not getting cleared after successful signin?
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Correlation.Microsoft"));
Assert.Equal(ApplicationBaseUrl + "Account/ExternalLoginCallback?ReturnUrl=%2F", response.RequestMessage.RequestUri.AbsoluteUri);
formParameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Email", "microsoft@test.com"),
new KeyValuePair<string, string>("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/ExternalLoginConfirmation?ReturnUrl=%2F")),
};
content = new FormUrlEncodedContent(formParameters.ToArray());
response = httpClient.PostAsync("Account/ExternalLoginConfirmation", content).Result;
ThrowIfResponseStatusNotOk(response);
responseContent = response.Content.ReadAsStringAsync().Result;
Assert.Contains(string.Format("Hello {0}!", "microsoft@test.com"), responseContent, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
//Verify cookie sent
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Console.WriteLine("Successfully signed in with user '{0}'", "microsoft@test.com");
Console.WriteLine("Verifying if the middleware notifications were fired");
//Check for a non existing item
response = httpClient.GetAsync(string.Format("Admin/StoreManager/GetAlbumIdFromName?albumName={0}", "123")).Result;
//This action requires admin permissions. If notifications are fired this permission is granted
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Console.WriteLine("Middleware notifications were fired successfully");
}
}
}

View File

@ -145,6 +145,9 @@ namespace E2ETests
//Twitter login
LoginWithTwitter();
//MicrosoftAccountLogin
LoginWithMicrosoftAccount();
var testCompletionTime = DateTime.Now;
Console.WriteLine("[Time]: All tests completed in '{0}' seconds", (testCompletionTime - initializationCompleteTime).TotalSeconds);
Console.WriteLine("[Time]: Total time taken for this test variation '{0}' seconds", (testCompletionTime - testStartTime).TotalSeconds);

View File

@ -59,6 +59,7 @@ namespace E2ETests
responseContent = response.Content.ReadAsStringAsync().Result;
//Correlation cookie not getting cleared after successful signin?
//https://github.com/aspnet/Security/issues/69
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl))["__TwitterState"]);
Assert.Equal(ApplicationBaseUrl + "Account/ExternalLoginCallback?ReturnUrl=%2F", response.RequestMessage.RequestUri.AbsoluteUri);
//Twitter does not give back the email claim for some reason.
@ -79,10 +80,15 @@ namespace E2ETests
Assert.Contains("Log off", responseContent, StringComparison.OrdinalIgnoreCase);
//Verify cookie sent
Assert.NotNull(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application"));
//https://github.com/aspnet/Identity/issues/210
//Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Assert.Null(httpClientHandler.CookieContainer.GetCookies(new Uri(ApplicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.ExternalLogin"));
Console.WriteLine("Successfully signed in with user '{0}'", "twitter@test.com");
Console.WriteLine("Verifying if the middleware notifications were fired");
//Check for a non existing item
response = httpClient.GetAsync(string.Format("Admin/StoreManager/GetAlbumIdFromName?albumName={0}", "123")).Result;
//This action requires admin permissions. If notifications are fired this permission is granted
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Console.WriteLine("Middleware notifications were fired successfully");
}
}
}