Update MicrosoftAccount to use converged auth
-Updated MicrosoftAccountDefaults to use app model v2 & graph API -Updated MicrosoftAccountHelper to extract user info from the user object returned by the graph API -Updated MicrosoftAccountMiddlewareTests accordingly -Added the app model v2 client credentials to SocialSample/config.json -Configured SocialSample to use SSL Startup.cs - Sorted namespaces - Dropped openid scope from Microsoft-AccessToken project.json - Sorted dependencies MicrosoftAccountHelper.cs - Removed name claim transofmation - renamed GetName, GetFirstName & GetLastName
This commit is contained in:
parent
909c342711
commit
8645ca0dc5
|
|
@ -4,13 +4,14 @@
|
|||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:54540",
|
||||
"sslPort": 0
|
||||
"sslPort": 44318
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "https://localhost:44318/",
|
||||
"environmentVariables": {
|
||||
"ASPNET_ENV": "Development"
|
||||
}
|
||||
|
|
@ -18,10 +19,9 @@
|
|||
"web": {
|
||||
"commandName": "web",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "http://localhost:54540/",
|
||||
"launchUrl": "https://localhost:54541/",
|
||||
"environmentVariables": {
|
||||
"Hosting:Environment": "Development",
|
||||
"ASPNET_server.urls": "http://localhost:54540/"
|
||||
"Hosting:Environment": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
|
@ -15,12 +18,15 @@ using Microsoft.AspNetCore.Builder;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Authentication;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Filter;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace CookieSample
|
||||
namespace SocialSample
|
||||
{
|
||||
/* Note all servers must use the same address and port because these are pre-registered with the various providers. */
|
||||
public class Startup
|
||||
|
|
@ -45,6 +51,10 @@ namespace CookieSample
|
|||
{
|
||||
loggerfactory.AddConsole(LogLevel.Information);
|
||||
|
||||
//Configure SSL
|
||||
var serverCertificate = LoadCertificate();
|
||||
app.UseKestrelHttps(serverCertificate);
|
||||
|
||||
// Simple error page to avoid a repo dependency.
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
|
|
@ -63,6 +73,12 @@ namespace CookieSample
|
|||
}
|
||||
});
|
||||
|
||||
// Forward the scheme from IISPlatformHandler
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions()
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedProto,
|
||||
});
|
||||
|
||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||
{
|
||||
AutomaticAuthenticate = true,
|
||||
|
|
@ -105,7 +121,6 @@ namespace CookieSample
|
|||
Events = new OAuthEvents()
|
||||
{
|
||||
OnRemoteFailure = ctx =>
|
||||
|
||||
{
|
||||
ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message));
|
||||
ctx.HandleResponse();
|
||||
|
|
@ -132,45 +147,34 @@ namespace CookieSample
|
|||
}
|
||||
});
|
||||
|
||||
// You must first create an app with live.com and add it's ID and Secret to your config.json or user-secrets.
|
||||
/* https://account.live.com/developers/applications
|
||||
The MicrosoftAccount service has restrictions that prevent the use of http://localhost:54540/ for test applications.
|
||||
As such, here is how to change this sample to uses http://mssecsample.localhost.this:54540/ instead.
|
||||
|
||||
Edit the hosting.json file and add "server.urls": "http://mssecsample.localhost.this:54540/".
|
||||
|
||||
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 MsSecSample.localhost.this
|
||||
|
||||
[WebListener] 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://mssecsample.localhost.this:54540/ user=[domain\user]
|
||||
|
||||
The sample app can then be run via:
|
||||
dnx web
|
||||
/* Azure AD app model v2 has restrictions that prevent the use of plain HTTP for redirect URLs.
|
||||
Therefore, to authenticate through microsoft accounts, tryout the sample using the following URL:
|
||||
https://localhost:54541/
|
||||
*/
|
||||
//app.UseOAuthAuthentication(new OAuthOptions
|
||||
//{
|
||||
// AuthenticationScheme = "Microsoft-AccessToken",
|
||||
// DisplayName = "MicrosoftAccount-AccessToken - Requires project changes",
|
||||
// ClientId = Configuration["msa:clientid"],
|
||||
// ClientSecret = Configuration["msa:clientsecret"],
|
||||
// CallbackPath = new PathString("/signin-microsoft-token"),
|
||||
// AuthorizationEndpoint = MicrosoftAccountDefaults.AuthorizationEndpoint,
|
||||
// TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint,
|
||||
// Scope = { "wl.basic" },
|
||||
// SaveTokens = true
|
||||
//});
|
||||
// See config.json
|
||||
// https://apps.dev.microsoft.com/
|
||||
app.UseOAuthAuthentication(new OAuthOptions
|
||||
{
|
||||
AuthenticationScheme = "Microsoft-AccessToken",
|
||||
DisplayName = "MicrosoftAccount-AccessToken",
|
||||
ClientId = Configuration["msa:clientid"],
|
||||
ClientSecret = Configuration["msa:clientsecret"],
|
||||
CallbackPath = new PathString("/signin-microsoft-token"),
|
||||
AuthorizationEndpoint = MicrosoftAccountDefaults.AuthorizationEndpoint,
|
||||
TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint,
|
||||
Scope = { "https://graph.microsoft.com/user.read" },
|
||||
SaveTokens = true
|
||||
});
|
||||
|
||||
////// You must first create an app with live.com and add it's ID and Secret to your config.json or user-secrets.
|
||||
//app.UseMicrosoftAccountAuthentication(new MicrosoftAccountOptions
|
||||
//{
|
||||
// DisplayName = "MicrosoftAccount - Requires project changes",
|
||||
// ClientId = Configuration["msa:clientid"],
|
||||
// ClientSecret = Configuration["msa:clientsecret"],
|
||||
// Scope = { "wl.emails" }
|
||||
//});
|
||||
// See config.json
|
||||
// https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-app-registration/
|
||||
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountOptions
|
||||
{
|
||||
DisplayName = "MicrosoftAccount",
|
||||
ClientId = Configuration["msa:clientid"],
|
||||
ClientSecret = Configuration["msa:clientsecret"],
|
||||
SaveTokens = true
|
||||
});
|
||||
|
||||
// See config.json
|
||||
// https://github.com/settings/applications/
|
||||
|
|
@ -346,5 +350,23 @@ namespace CookieSample
|
|||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
private X509Certificate2 LoadCertificate()
|
||||
{
|
||||
var socialSampleAssembly = GetType().GetTypeInfo().Assembly;
|
||||
var embeddedFileProvider = new EmbeddedFileProvider(socialSampleAssembly, "SocialSample");
|
||||
var certificateFileInfo = embeddedFileProvider.GetFileInfo("compiler/resources/cert.pfx");
|
||||
using (var certificateStream = certificateFileInfo.CreateReadStream())
|
||||
{
|
||||
byte[] certificatePayload;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
certificateStream.CopyTo(memoryStream);
|
||||
certificatePayload = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
return new X509Certificate2(certificatePayload, "testPassword");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -6,5 +6,7 @@
|
|||
"github:clientid": "49e302895d8b09ea5656",
|
||||
"github:clientsecret": "98f1bf028608901e9df91d64ee61536fe562064b",
|
||||
"github-token:clientid": "8c0c5a572abe8fe89588",
|
||||
"github-token:clientsecret": "e1d95eaf03461d27acd6f49d4fc7bf19d6ac8cda"
|
||||
"github-token:clientsecret": "e1d95eaf03461d27acd6f49d4fc7bf19d6ac8cda",
|
||||
"msa:clientid": "e2105565-1f56-434a-ae61-9849ebaf606c",
|
||||
"msa:clientsecret": "pjqtt3RXrFwcfSJyQ0BeUez"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@
|
|||
"Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Authentication.Twitter": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.DataProtection": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.HttpOverrides": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.IISPlatformHandler": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Server.Kestrel.Https": "1.0.0-*",
|
||||
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-*",
|
||||
"Microsoft.Extensions.FileProviders.Embedded": "1.0.0-*",
|
||||
"Microsoft.Extensions.Logging.Console": "1.0.0-*",
|
||||
"Microsoft.NETCore.Platforms": "1.0.1-*"
|
||||
},
|
||||
|
|
@ -26,5 +29,9 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"userSecretsId": "aspnet5-SocialSample-20151210111056"
|
||||
}
|
||||
"userSecretsId": "aspnet5-SocialSample-20151210111056",
|
||||
"content": [
|
||||
"config.json",
|
||||
"project.json"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
|
|||
{
|
||||
public const string AuthenticationScheme = "Microsoft";
|
||||
|
||||
public static readonly string AuthorizationEndpoint = "https://login.live.com/oauth20_authorize.srf";
|
||||
public static readonly string AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
|
||||
|
||||
public static readonly string TokenEndpoint = "https://login.live.com/oauth20_token.srf";
|
||||
public static readonly string TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
|
||||
public static readonly string UserInformationEndpoint = "https://apis.live.net/v5.0/me";
|
||||
public static readonly string UserInformationEndpoint = "https://graph.microsoft.com/v1.0/me";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,13 +38,27 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
|
|||
identity.AddClaim(new Claim("urn:microsoftaccount:id", identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var name = MicrosoftAccountHelper.GetName(payload);
|
||||
var name = MicrosoftAccountHelper.GetDisplayName(payload);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim("urn:microsoftaccount:name", name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var givenName = MicrosoftAccountHelper.GetGivenName(payload);
|
||||
if (!string.IsNullOrEmpty(givenName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.GivenName, givenName, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim("urn:microsoftaccount:givenname", givenName, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var surname = MicrosoftAccountHelper.GetSurname(payload);
|
||||
if (!string.IsNullOrEmpty(surname))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Surname, surname, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim("urn:microsoftaccount:surname", surname, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var email = MicrosoftAccountHelper.GetEmail(payload);
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
|
|||
{
|
||||
/// <summary>
|
||||
/// Contains static methods that allow to extract user's information from a <see cref="JObject"/>
|
||||
/// instance retrieved from Google after a successful authentication process.
|
||||
/// instance retrieved from Microsoft after a successful authentication process.
|
||||
/// http://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/user
|
||||
/// </summary>
|
||||
public static class MicrosoftAccountHelper
|
||||
{
|
||||
|
|
@ -28,40 +29,40 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
|
|||
/// <summary>
|
||||
/// Gets the user's name.
|
||||
/// </summary>
|
||||
public static string GetName(JObject user)
|
||||
public static string GetDisplayName(JObject user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return user.Value<string>("name");
|
||||
return user.Value<string>("displayName");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's first name.
|
||||
/// Gets the user's given name.
|
||||
/// </summary>
|
||||
public static string GetFirstName(JObject user)
|
||||
public static string GetGivenName(JObject user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return user.Value<string>("first_name");
|
||||
return user.Value<string>("givenName");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's last name.
|
||||
/// Gets the user's surname.
|
||||
/// </summary>
|
||||
public static string GetLastName(JObject user)
|
||||
public static string GetSurname(JObject user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return user.Value<string>("last_name");
|
||||
return user.Value<string>("surname");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -74,7 +75,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
|
|||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return user.Value<JObject>("emails")?.Value<string>("preferred");
|
||||
return user.Value<string>("mail") ?? user.Value<string>("userPrincipalName");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
AuthorizationEndpoint = MicrosoftAccountDefaults.AuthorizationEndpoint;
|
||||
TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint;
|
||||
UserInformationEndpoint = MicrosoftAccountDefaults.UserInformationEndpoint;
|
||||
Scope.Add("wl.basic");
|
||||
Scope.Add("https://graph.microsoft.com/user.read");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
|
|||
var transaction = await server.SendAsync("http://example.com/challenge");
|
||||
Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode);
|
||||
var location = transaction.Response.Headers.Location.AbsoluteUri;
|
||||
Assert.Contains("https://login.live.com/oauth20_authorize.srf", location);
|
||||
Assert.Contains("https://login.microsoftonline.com/common/oauth2/v2.0/authorize", location);
|
||||
Assert.Contains("response_type=code", location);
|
||||
Assert.Contains("client_id=", location);
|
||||
Assert.Contains("redirect_uri=", location);
|
||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
|
|||
{
|
||||
Sender = req =>
|
||||
{
|
||||
if (req.RequestUri.AbsoluteUri == "https://login.live.com/oauth20_token.srf")
|
||||
if (req.RequestUri.AbsoluteUri == "https://login.microsoftonline.com/common/oauth2/v2.0/token")
|
||||
{
|
||||
return ReturnJsonResponse(new
|
||||
{
|
||||
|
|
@ -123,18 +123,15 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
|
|||
refresh_token = "Test Refresh Token"
|
||||
});
|
||||
}
|
||||
else if (req.RequestUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped) == "https://apis.live.net/v5.0/me")
|
||||
else if (req.RequestUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped) == "https://graph.microsoft.com/v1.0/me")
|
||||
{
|
||||
return ReturnJsonResponse(new
|
||||
{
|
||||
id = "Test User ID",
|
||||
name = "Test Name",
|
||||
first_name = "Test Given Name",
|
||||
last_name = "Test Family Name",
|
||||
emails = new
|
||||
{
|
||||
preferred = "Test email"
|
||||
}
|
||||
displayName = "Test Name",
|
||||
givenName = "Test Given Name",
|
||||
surname = "Test Family Name",
|
||||
mail = "Test email"
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue