Revisit OAuthAuthenticationHandler and add a new SaveTokensAsClaims option
This commit is contained in:
parent
6e67b1c9b1
commit
9bb8b61146
|
|
@ -1,3 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Authentication.Cookies;
|
||||
using Microsoft.AspNet.Builder;
|
||||
|
|
@ -25,10 +26,11 @@ namespace CookieSample
|
|||
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "bob") }));
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "bob") }, CookieAuthenticationDefaults.AuthenticationScheme));
|
||||
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);
|
||||
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync("Hello First timer");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
|
@ -27,7 +28,7 @@ namespace CookieSessionSample
|
|||
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
|
||||
{
|
||||
// Make a large identity
|
||||
var claims = new List<Claim>(1001);
|
||||
|
|
@ -36,7 +37,10 @@ namespace CookieSessionSample
|
|||
{
|
||||
claims.Add(new Claim(ClaimTypes.Role, "SomeRandomGroup" + i, ClaimValueTypes.String, "IssuedByBob", "OriginalIssuerJoe"));
|
||||
}
|
||||
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity(claims)));
|
||||
|
||||
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme)));
|
||||
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync("Hello First timer");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNet.Builder;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Authentication;
|
||||
|
|
@ -38,7 +39,7 @@ namespace OpenIdConnectSample
|
|||
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
|
||||
{
|
||||
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
|
|
@ -110,6 +111,7 @@ namespace CookieSample
|
|||
options.Caption = "MicrosoftAccount - Requires project changes";
|
||||
options.ClientId = "00000000480FF62E";
|
||||
options.ClientSecret = "bLw2JIvf8Y1TaToipPEqxTVlOeJwCUsr";
|
||||
options.Scope.Add("wl.emails");
|
||||
});
|
||||
|
||||
// https://github.com/settings/applications/
|
||||
|
|
@ -131,48 +133,53 @@ namespace CookieSample
|
|||
options.TokenEndpoint = "https://github.com/login/oauth/access_token";
|
||||
options.UserInformationEndpoint = "https://api.github.com/user";
|
||||
options.ClaimsIssuer = "OAuth2-Github";
|
||||
options.SaveTokensAsClaims = false;
|
||||
// Retrieving user information is unique to each provider.
|
||||
options.Notifications = new OAuthAuthenticationNotifications()
|
||||
options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
OnGetUserInformationAsync = async (context) =>
|
||||
OnAuthenticated = async notification =>
|
||||
{
|
||||
// Get the GitHub user
|
||||
var userRequest = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
|
||||
userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
|
||||
userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
var userResponse = await context.Backchannel.SendAsync(userRequest, context.HttpContext.RequestAborted);
|
||||
userResponse.EnsureSuccessStatusCode();
|
||||
var text = await userResponse.Content.ReadAsStringAsync();
|
||||
var user = JObject.Parse(text);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, notification.Options.UserInformationEndpoint);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", notification.AccessToken);
|
||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
var identity = new ClaimsIdentity(
|
||||
context.Options.AuthenticationScheme,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType);
|
||||
var response = await notification.Backchannel.SendAsync(request, notification.HttpContext.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
JToken value;
|
||||
var id = user.TryGetValue("id", out value) ? value.ToString() : null;
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
var user = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var identifier = user.Value<string>("id");
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id, ClaimValueTypes.String, context.Options.ClaimsIssuer));
|
||||
notification.Identity.AddClaim(new Claim(
|
||||
ClaimTypes.NameIdentifier, identifier,
|
||||
ClaimValueTypes.String, notification.Options.ClaimsIssuer));
|
||||
}
|
||||
var userName = user.TryGetValue("login", out value) ? value.ToString() : null;
|
||||
|
||||
var userName = user.Value<string>("login");
|
||||
if (!string.IsNullOrEmpty(userName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, context.Options.ClaimsIssuer));
|
||||
}
|
||||
var name = user.TryGetValue("name", out value) ? value.ToString() : null;
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
identity.AddClaim(new Claim("urn:github:name", name, ClaimValueTypes.String, context.Options.ClaimsIssuer));
|
||||
}
|
||||
var link = user.TryGetValue("url", out value) ? value.ToString() : null;
|
||||
if (!string.IsNullOrEmpty(link))
|
||||
{
|
||||
identity.AddClaim(new Claim("urn:github:url", link, ClaimValueTypes.String, context.Options.ClaimsIssuer));
|
||||
notification.Identity.AddClaim(new Claim(
|
||||
ClaimsIdentity.DefaultNameClaimType, userName,
|
||||
ClaimValueTypes.String, notification.Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
context.Principal = new ClaimsPrincipal(identity);
|
||||
var name = user.Value<string>("name");
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
notification.Identity.AddClaim(new Claim(
|
||||
"urn:github:name", name,
|
||||
ClaimValueTypes.String, notification.Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var link = user.Value<string>("url");
|
||||
if (!string.IsNullOrEmpty(link))
|
||||
{
|
||||
notification.Identity.AddClaim(new Claim(
|
||||
"urn:github:url", link,
|
||||
ClaimValueTypes.String, notification.Options.ClaimsIssuer));
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
@ -207,8 +214,8 @@ namespace CookieSample
|
|||
{
|
||||
signoutApp.Run(async context =>
|
||||
{
|
||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
await context.Response.WriteAsync("<html><body>");
|
||||
await context.Response.WriteAsync("You have been logged out. Goodbye " + context.User.Identity.Name + "<br>");
|
||||
await context.Response.WriteAsync("<a href=\"/\">Home</a>");
|
||||
|
|
@ -219,7 +226,7 @@ namespace CookieSample
|
|||
// Deny anonymous request beyond this point.
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
|
||||
{
|
||||
// The cookie middleware will intercept this 401 and redirect to /login
|
||||
await context.Authentication.ChallengeAsync();
|
||||
|
|
@ -233,7 +240,7 @@ namespace CookieSample
|
|||
{
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Response.WriteAsync("<html><body>");
|
||||
await context.Response.WriteAsync("Hello " + context.User.Identity.Name + "<br>");
|
||||
await context.Response.WriteAsync("Hello " + (context.User.Identity.Name ?? "anonymous") + "<br>");
|
||||
foreach (var claim in context.User.Claims)
|
||||
{
|
||||
await context.Response.WriteAsync(claim.Type + ": " + claim.Value + "<br>");
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.AspNet.Authentication.Facebook
|
||||
{
|
||||
internal class FacebookAuthenticationHandler : OAuthAuthenticationHandler<FacebookAuthenticationOptions, IFacebookAuthenticationNotifications>
|
||||
internal class FacebookAuthenticationHandler : OAuthAuthenticationHandler<FacebookAuthenticationOptions>
|
||||
{
|
||||
public FacebookAuthenticationHandler(HttpClient httpClient)
|
||||
: base(httpClient)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<TokenResponse> ExchangeCodeAsync(string code, string redirectUri)
|
||||
protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri)
|
||||
{
|
||||
var queryBuilder = new QueryBuilder()
|
||||
{
|
||||
|
|
@ -35,70 +35,78 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
{ "client_secret", Options.AppSecret },
|
||||
};
|
||||
|
||||
var tokenResponse = await Backchannel.GetAsync(Options.TokenEndpoint + queryBuilder.ToString(), Context.RequestAborted);
|
||||
tokenResponse.EnsureSuccessStatusCode();
|
||||
var oauthTokenResponse = await tokenResponse.Content.ReadAsStringAsync();
|
||||
var response = await Backchannel.GetAsync(Options.TokenEndpoint + queryBuilder.ToString(), Context.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var form = new FormCollection(FormReader.ReadForm(oauthTokenResponse));
|
||||
var response = new JObject();
|
||||
var form = new FormCollection(FormReader.ReadForm(await response.Content.ReadAsStringAsync()));
|
||||
var payload = new JObject();
|
||||
foreach (string key in form.Keys)
|
||||
{
|
||||
response.Add(string.Equals(key, "expires", StringComparison.OrdinalIgnoreCase) ? "expires_in" : key, form[key]);
|
||||
payload.Add(string.Equals(key, "expires", StringComparison.OrdinalIgnoreCase) ? "expires_in" : key, form[key]);
|
||||
}
|
||||
|
||||
// The refresh token is not available.
|
||||
return new TokenResponse(response);
|
||||
return new OAuthTokenResponse(payload);
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> GetUserInformationAsync(AuthenticationProperties properties, TokenResponse tokens)
|
||||
protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
|
||||
{
|
||||
var graphAddress = Options.UserInformationEndpoint + "?access_token=" + UrlEncoder.UrlEncode(tokens.AccessToken);
|
||||
var endpoint = Options.UserInformationEndpoint + "?access_token=" + UrlEncoder.UrlEncode(tokens.AccessToken);
|
||||
if (Options.SendAppSecretProof)
|
||||
{
|
||||
graphAddress += "&appsecret_proof=" + GenerateAppSecretProof(tokens.AccessToken);
|
||||
endpoint += "&appsecret_proof=" + GenerateAppSecretProof(tokens.AccessToken);
|
||||
}
|
||||
|
||||
var graphResponse = await Backchannel.GetAsync(graphAddress, Context.RequestAborted);
|
||||
graphResponse.EnsureSuccessStatusCode();
|
||||
var text = await graphResponse.Content.ReadAsStringAsync();
|
||||
var user = JObject.Parse(text);
|
||||
var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var context = new FacebookAuthenticatedContext(Context, Options, user, tokens);
|
||||
var identity = new ClaimsIdentity(
|
||||
Options.ClaimsIssuer,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType);
|
||||
if (!string.IsNullOrEmpty(context.Id))
|
||||
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var notification = new OAuthAuthenticatedContext(Context, Options, Backchannel, tokens, payload)
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
Properties = properties,
|
||||
Principal = new ClaimsPrincipal(identity)
|
||||
};
|
||||
|
||||
var identifier = FacebookAuthenticationHelper.GetId(payload);
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.UserName))
|
||||
|
||||
var userName = FacebookAuthenticationHelper.GetUserName(payload);
|
||||
if (!string.IsNullOrEmpty(userName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Email))
|
||||
|
||||
var email = FacebookAuthenticationHelper.GetEmail(payload);
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Name))
|
||||
|
||||
var name = FacebookAuthenticationHelper.GetName(payload);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
identity.AddClaim(new Claim("urn:facebook:name", context.Name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim("urn:facebook:name", name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
|
||||
// Many Facebook accounts do not set the UserName field. Fall back to the Name field instead.
|
||||
if (string.IsNullOrEmpty(context.UserName))
|
||||
if (string.IsNullOrEmpty(userName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim(identity.NameClaimType, name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Link))
|
||||
|
||||
var link = FacebookAuthenticationHelper.GetLink(payload);
|
||||
if (!string.IsNullOrEmpty(link))
|
||||
{
|
||||
identity.AddClaim(new Claim("urn:facebook:link", context.Link, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim("urn:facebook:link", link, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
context.Properties = properties;
|
||||
context.Principal = new ClaimsPrincipal(identity);
|
||||
|
||||
await Options.Notifications.Authenticated(context);
|
||||
await Options.Notifications.Authenticated(notification);
|
||||
|
||||
return new AuthenticationTicket(context.Principal, context.Properties, context.Options.AuthenticationScheme);
|
||||
return new AuthenticationTicket(notification.Principal, notification.Properties, notification.Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
private string GenerateAppSecretProof(string accessToken)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Framework.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains static methods that allow to extract user's information from a <see cref="JObject"/>
|
||||
/// instance retrieved from Facebook after a successful authentication process.
|
||||
/// </summary>
|
||||
public static class FacebookAuthenticationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Facebook user ID.
|
||||
/// </summary>
|
||||
public static string GetId([NotNull] JObject user) => user.Value<string>("id");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's name.
|
||||
/// </summary>
|
||||
public static string GetName([NotNull] JObject user) => user.Value<string>("name");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's link.
|
||||
/// </summary>
|
||||
public static string GetLink([NotNull] JObject user) => user.Value<string>("link");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Facebook username.
|
||||
/// </summary>
|
||||
public static string GetUserName([NotNull] JObject user) => user.Value<string>("username");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Facebook email.
|
||||
/// </summary>
|
||||
public static string GetEmail([NotNull] JObject user) => user.Value<string>("email");
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
/// <summary>
|
||||
/// An ASP.NET middleware for authenticating users using Facebook.
|
||||
/// </summary>
|
||||
public class FacebookAuthenticationMiddleware : OAuthAuthenticationMiddleware<FacebookAuthenticationOptions, IFacebookAuthenticationNotifications>
|
||||
public class FacebookAuthenticationMiddleware : OAuthAuthenticationMiddleware<FacebookAuthenticationOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FacebookAuthenticationMiddleware"/>.
|
||||
|
|
@ -43,11 +43,6 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AppSecret)));
|
||||
}
|
||||
|
||||
if (Options.Notifications == null)
|
||||
{
|
||||
Options.Notifications = new FacebookAuthenticationNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
/// <summary>
|
||||
/// Configuration options for <see cref="FacebookAuthenticationMiddleware"/>.
|
||||
/// </summary>
|
||||
public class FacebookAuthenticationOptions : OAuthAuthenticationOptions<IFacebookAuthenticationNotifications>
|
||||
public class FacebookAuthenticationOptions : OAuthAuthenticationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FacebookAuthenticationOptions"/>.
|
||||
|
|
@ -23,6 +24,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
AuthorizationEndpoint = FacebookAuthenticationDefaults.AuthorizationEndpoint;
|
||||
TokenEndpoint = FacebookAuthenticationDefaults.TokenEndpoint;
|
||||
UserInformationEndpoint = FacebookAuthenticationDefaults.UserInformationEndpoint;
|
||||
SaveTokensAsClaims = false;
|
||||
}
|
||||
|
||||
// Facebook uses a non-standard term for this field.
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// </summary>
|
||||
public class FacebookAuthenticatedContext : OAuthAuthenticatedContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FacebookAuthenticatedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP environment.</param>
|
||||
/// <param name="user">The JSON-serialized user.</param>
|
||||
/// <param name="tokens">The Facebook Access token.</param>
|
||||
public FacebookAuthenticatedContext(HttpContext context, OAuthAuthenticationOptions options, JObject user, TokenResponse tokens)
|
||||
: base(context, options, user, tokens)
|
||||
{
|
||||
Id = TryGetValue(user, "id");
|
||||
Name = TryGetValue(user, "name");
|
||||
Link = TryGetValue(user, "link");
|
||||
UserName = TryGetValue(user, "username");
|
||||
Email = TryGetValue(user, "email");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Facebook user ID.
|
||||
/// </summary>
|
||||
public string Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's link.
|
||||
/// </summary>
|
||||
public string Link { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Facebook username.
|
||||
/// </summary>
|
||||
public string UserName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Facebook email.
|
||||
/// </summary>
|
||||
public string Email { get; private set; }
|
||||
|
||||
private static string TryGetValue(JObject user, string propertyName)
|
||||
{
|
||||
JToken value;
|
||||
return user.TryGetValue(propertyName, out value) ? value.ToString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// The default <see cref="IFacebookAuthenticationNotifications"/> implementation.
|
||||
/// </summary>
|
||||
public class FacebookAuthenticationNotifications : OAuthAuthenticationNotifications, IFacebookAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FacebookAuthenticationNotifications"/>.
|
||||
/// </summary>
|
||||
public FacebookAuthenticationNotifications()
|
||||
{
|
||||
OnAuthenticated = context => Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||
/// </summary>
|
||||
public Func<FacebookAuthenticatedContext, Task> OnAuthenticated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever Facebook succesfully authenticates a user.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task Authenticated(FacebookAuthenticatedContext context)
|
||||
{
|
||||
return OnAuthenticated(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies callback methods which the <see cref="FacebookAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process.
|
||||
/// </summary>
|
||||
public interface IFacebookAuthenticationNotifications : IOAuthAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when Facebook succesfully authenticates a user.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task Authenticated(FacebookAuthenticatedContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,65 +14,69 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.AspNet.Authentication.Google
|
||||
{
|
||||
internal class GoogleAuthenticationHandler : OAuthAuthenticationHandler<GoogleAuthenticationOptions, IGoogleAuthenticationNotifications>
|
||||
internal class GoogleAuthenticationHandler : OAuthAuthenticationHandler<GoogleAuthenticationOptions>
|
||||
{
|
||||
public GoogleAuthenticationHandler(HttpClient httpClient)
|
||||
: base(httpClient)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> GetUserInformationAsync(AuthenticationProperties properties, TokenResponse tokens)
|
||||
protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
|
||||
{
|
||||
// Get the Google user
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
|
||||
var graphResponse = await Backchannel.SendAsync(request, Context.RequestAborted);
|
||||
graphResponse.EnsureSuccessStatusCode();
|
||||
var text = await graphResponse.Content.ReadAsStringAsync();
|
||||
var user = JObject.Parse(text);
|
||||
|
||||
var context = new GoogleAuthenticatedContext(Context, Options, user, tokens);
|
||||
var identity = new ClaimsIdentity(
|
||||
Options.ClaimsIssuer,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType);
|
||||
var response = await Backchannel.SendAsync(request, Context.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
if (!string.IsNullOrEmpty(context.Id))
|
||||
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var notification = new OAuthAuthenticatedContext(Context, Options, Backchannel, tokens, payload)
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.GivenName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.GivenName, context.GivenName,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.FamilyName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Surname, context.FamilyName,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Name))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, context.Name, ClaimValueTypes.String,
|
||||
Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Email))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, ClaimValueTypes.String,
|
||||
Options.ClaimsIssuer));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.Profile))
|
||||
{
|
||||
identity.AddClaim(new Claim("urn:google:profile", context.Profile, ClaimValueTypes.String,
|
||||
Options.ClaimsIssuer));
|
||||
}
|
||||
context.Properties = properties;
|
||||
context.Principal = new ClaimsPrincipal(identity);
|
||||
Properties = properties,
|
||||
Principal = new ClaimsPrincipal(identity)
|
||||
};
|
||||
|
||||
await Options.Notifications.Authenticated(context);
|
||||
var identifier = GoogleAuthenticationHelper.GetId(payload);
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
return new AuthenticationTicket(context.Principal, context.Properties, context.Options.AuthenticationScheme);
|
||||
var givenName = GoogleAuthenticationHelper.GetGivenName(payload);
|
||||
if (!string.IsNullOrEmpty(givenName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.GivenName, givenName, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var familyName = GoogleAuthenticationHelper.GetFamilyName(payload);
|
||||
if (!string.IsNullOrEmpty(familyName))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Surname, familyName, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var name = GoogleAuthenticationHelper.GetName(payload);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, name, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var email = GoogleAuthenticationHelper.GetEmail(payload);
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
var profile = GoogleAuthenticationHelper.GetProfile(payload);
|
||||
if (!string.IsNullOrEmpty(profile))
|
||||
{
|
||||
identity.AddClaim(new Claim("urn:google:profile", profile, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
await Options.Notifications.Authenticated(notification);
|
||||
|
||||
return new AuthenticationTicket(notification.Principal, notification.Properties, notification.Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
// TODO: Abstract this properties override pattern into the base class?
|
||||
|
|
|
|||
|
|
@ -1,74 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// Contains static methods that allow to extract user's information from a <see cref="JObject"/>
|
||||
/// instance retrieved from Google after a successful authentication process.
|
||||
/// </summary>
|
||||
public class GoogleAuthenticatedContext : OAuthAuthenticatedContext
|
||||
public static class GoogleAuthenticationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GoogleAuthenticatedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP environment.</param>
|
||||
/// <param name="user">The JSON-serialized Google user info.</param>
|
||||
/// <param name="tokens">Google OAuth 2.0 access token, refresh token, etc.</param>
|
||||
public GoogleAuthenticatedContext(HttpContext context, OAuthAuthenticationOptions options, JObject user, TokenResponse tokens)
|
||||
: base(context, options, user, tokens)
|
||||
{
|
||||
Id = TryGetValue(user, "id");
|
||||
Name = TryGetValue(user, "displayName");
|
||||
GivenName = TryGetValue(user, "name", "givenName");
|
||||
FamilyName = TryGetValue(user, "name", "familyName");
|
||||
Profile = TryGetValue(user, "url");
|
||||
Email = TryGetFirstValue(user, "emails", "value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Google user ID.
|
||||
/// </summary>
|
||||
public string Id { get; private set; }
|
||||
public static string GetId([NotNull] JObject user) => user.Value<string>("id");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
public static string GetName([NotNull] JObject user) => user.Value<string>("displayName");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's given name.
|
||||
/// </summary>
|
||||
public string GivenName { get; set; }
|
||||
public static string GetGivenName([NotNull] JObject user) => TryGetValue(user, "name", "givenName");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's family name.
|
||||
/// </summary>
|
||||
public string FamilyName { get; set; }
|
||||
public static string GetFamilyName([NotNull] JObject user) => TryGetValue(user, "name", "familyName");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's profile link.
|
||||
/// </summary>
|
||||
public string Profile { get; private set; }
|
||||
public static string GetProfile([NotNull] JObject user) => user.Value<string>("url");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's email.
|
||||
/// </summary>
|
||||
public string Email { get; private set; }
|
||||
|
||||
private static string TryGetValue(JObject user, string propertyName)
|
||||
{
|
||||
JToken value;
|
||||
return user.TryGetValue(propertyName, out value) ? value.ToString() : null;
|
||||
}
|
||||
public static string GetEmail([NotNull] JObject user) => TryGetFirstValue(user, "emails", "value");
|
||||
|
||||
// Get the given subProperty from a property.
|
||||
private static string TryGetValue(JObject user, string propertyName, string subProperty)
|
||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
/// An ASP.NET middleware for authenticating users using Google OAuth 2.0.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
||||
public class GoogleAuthenticationMiddleware : OAuthAuthenticationMiddleware<GoogleAuthenticationOptions, IGoogleAuthenticationNotifications>
|
||||
public class GoogleAuthenticationMiddleware : OAuthAuthenticationMiddleware<GoogleAuthenticationOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GoogleAuthenticationMiddleware"/>.
|
||||
|
|
@ -35,11 +35,6 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
ConfigureOptions<GoogleAuthenticationOptions> configureOptions = null)
|
||||
: base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions)
|
||||
{
|
||||
if (Options.Notifications == null)
|
||||
{
|
||||
Options.Notifications = new GoogleAuthenticationNotifications();
|
||||
}
|
||||
|
||||
if (Options.Scope.Count == 0)
|
||||
{
|
||||
// Google OAuth 2.0 asks for non-empty scope. If user didn't set it, set default scope to
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
/// <summary>
|
||||
/// Configuration options for <see cref="GoogleAuthenticationMiddleware"/>.
|
||||
/// </summary>
|
||||
public class GoogleAuthenticationOptions : OAuthAuthenticationOptions<IGoogleAuthenticationNotifications>
|
||||
public class GoogleAuthenticationOptions : OAuthAuthenticationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GoogleAuthenticationOptions"/>.
|
||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
AuthorizationEndpoint = GoogleAuthenticationDefaults.AuthorizationEndpoint;
|
||||
TokenEndpoint = GoogleAuthenticationDefaults.TokenEndpoint;
|
||||
UserInformationEndpoint = GoogleAuthenticationDefaults.UserInformationEndpoint;
|
||||
SaveTokensAsClaims = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// The default <see cref="IGoogleAuthenticationNotifications"/> implementation.
|
||||
/// </summary>
|
||||
public class GoogleAuthenticationNotifications : OAuthAuthenticationNotifications, IGoogleAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GoogleAuthenticationNotifications"/>.
|
||||
/// </summary>
|
||||
public GoogleAuthenticationNotifications()
|
||||
{
|
||||
OnAuthenticated = context => Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||
/// </summary>
|
||||
public Func<GoogleAuthenticatedContext, Task> OnAuthenticated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever Google succesfully authenticates a user.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task Authenticated(GoogleAuthenticatedContext context)
|
||||
{
|
||||
return OnAuthenticated(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies callback methods which the <see cref="GoogleAuthenticationMiddleware" /> invokes to enable developer control over the authentication process.
|
||||
/// </summary>
|
||||
public interface IGoogleAuthenticationNotifications : IOAuthAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked whenever Google succesfully authenticates a user.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task Authenticated(GoogleAuthenticatedContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,45 +11,52 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||
{
|
||||
internal class MicrosoftAccountAuthenticationHandler : OAuthAuthenticationHandler<MicrosoftAccountAuthenticationOptions, IMicrosoftAccountAuthenticationNotifications>
|
||||
internal class MicrosoftAccountAuthenticationHandler : OAuthAuthenticationHandler<MicrosoftAccountAuthenticationOptions>
|
||||
{
|
||||
public MicrosoftAccountAuthenticationHandler(HttpClient httpClient)
|
||||
: base(httpClient)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> GetUserInformationAsync(AuthenticationProperties properties, TokenResponse tokens)
|
||||
protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
|
||||
var graphResponse = await Backchannel.SendAsync(request, Context.RequestAborted);
|
||||
graphResponse.EnsureSuccessStatusCode();
|
||||
var accountString = await graphResponse.Content.ReadAsStringAsync();
|
||||
var accountInformation = JObject.Parse(accountString);
|
||||
|
||||
var context = new MicrosoftAccountAuthenticatedContext(Context, Options, accountInformation, tokens);
|
||||
context.Properties = properties;
|
||||
var identity = new ClaimsIdentity(
|
||||
new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, context.Id, ClaimValueTypes.String, Options.ClaimsIssuer),
|
||||
new Claim(ClaimTypes.Name, context.Name, ClaimValueTypes.String, Options.ClaimsIssuer),
|
||||
new Claim("urn:microsoftaccount:id", context.Id, ClaimValueTypes.String, Options.ClaimsIssuer),
|
||||
new Claim("urn:microsoftaccount:name", context.Name, ClaimValueTypes.String, Options.ClaimsIssuer)
|
||||
},
|
||||
Options.ClaimsIssuer,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType);
|
||||
var response = await Backchannel.SendAsync(request, Context.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(context.Email))
|
||||
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var notification = new OAuthAuthenticatedContext(Context, Options, Backchannel, tokens, payload)
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
Properties = properties,
|
||||
Principal = new ClaimsPrincipal(identity)
|
||||
};
|
||||
|
||||
var identifier = MicrosoftAccountAuthenticationHelper.GetId(payload);
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
identity.AddClaim(new Claim("urn:microsoftaccount:id", identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
context.Principal = new ClaimsPrincipal(identity);
|
||||
|
||||
await Options.Notifications.Authenticated(context);
|
||||
var name = MicrosoftAccountAuthenticationHelper.GetName(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));
|
||||
}
|
||||
|
||||
return new AuthenticationTicket(context.Principal, context.Properties, context.Options.AuthenticationScheme);
|
||||
var email = MicrosoftAccountAuthenticationHelper.GetEmail(payload);
|
||||
if (!string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
await Options.Notifications.Authenticated(notification);
|
||||
|
||||
return new AuthenticationTicket(notification.Principal, notification.Properties, notification.Options.AuthenticationScheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Framework.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.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.
|
||||
/// </summary>
|
||||
public static class MicrosoftAccountAuthenticationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Microsoft Account user ID.
|
||||
/// </summary>
|
||||
public static string GetId([NotNull] JObject user) => user.Value<string>("id");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's name.
|
||||
/// </summary>
|
||||
public static string GetName([NotNull] JObject user) => user.Value<string>("name");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's first name.
|
||||
/// </summary>
|
||||
public static string GetFirstName([NotNull] JObject user) => user.Value<string>("first_name");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's last name.
|
||||
/// </summary>
|
||||
public static string GetLastName([NotNull] JObject user) => user.Value<string>("last_name");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's email address.
|
||||
/// </summary>
|
||||
public static string GetEmail([NotNull] JObject user) => user.Value<JObject>("emails")
|
||||
?.Value<string>("preferred");
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
|||
/// <summary>
|
||||
/// An ASP.NET middleware for authenticating users using the Microsoft Account service.
|
||||
/// </summary>
|
||||
public class MicrosoftAccountAuthenticationMiddleware : OAuthAuthenticationMiddleware<MicrosoftAccountAuthenticationOptions, IMicrosoftAccountAuthenticationNotifications>
|
||||
public class MicrosoftAccountAuthenticationMiddleware : OAuthAuthenticationMiddleware<MicrosoftAccountAuthenticationOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="MicrosoftAccountAuthenticationMiddleware"/>.
|
||||
|
|
@ -33,10 +33,6 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
|||
ConfigureOptions<MicrosoftAccountAuthenticationOptions> configureOptions = null)
|
||||
: base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions)
|
||||
{
|
||||
if (Options.Notifications == null)
|
||||
{
|
||||
Options.Notifications = new MicrosoftAccountAuthenticationNotifications();
|
||||
}
|
||||
if (Options.Scope.Count == 0)
|
||||
{
|
||||
// LiveID requires a scope string, so if the user didn't set one we go for the least possible.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
|||
/// <summary>
|
||||
/// Configuration options for <see cref="MicrosoftAccountAuthenticationMiddleware"/>.
|
||||
/// </summary>
|
||||
public class MicrosoftAccountAuthenticationOptions : OAuthAuthenticationOptions<IMicrosoftAccountAuthenticationNotifications>
|
||||
public class MicrosoftAccountAuthenticationOptions : OAuthAuthenticationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="MicrosoftAccountAuthenticationOptions"/>.
|
||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
|||
AuthorizationEndpoint = MicrosoftAccountAuthenticationDefaults.AuthorizationEndpoint;
|
||||
TokenEndpoint = MicrosoftAccountAuthenticationDefaults.TokenEndpoint;
|
||||
UserInformationEndpoint = MicrosoftAccountAuthenticationDefaults.UserInformationEndpoint;
|
||||
SaveTokensAsClaims = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies callback methods which the <see cref="MicrosoftAccountAuthenticationMiddleware"/> invokes to enable developer control over the authentication process.
|
||||
/// </summary>
|
||||
public interface IMicrosoftAccountAuthenticationNotifications : IOAuthAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked whenever Microsoft succesfully authenticates a user.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task Authenticated(MicrosoftAccountAuthenticatedContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// </summary>
|
||||
public class MicrosoftAccountAuthenticatedContext : OAuthAuthenticatedContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="MicrosoftAccountAuthenticatedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP environment.</param>
|
||||
/// <param name="user">The JSON-serialized user.</param>
|
||||
/// <param name="tokens">The access token provided by the Microsoft authentication service.</param>
|
||||
public MicrosoftAccountAuthenticatedContext(HttpContext context, OAuthAuthenticationOptions options, [NotNull] JObject user, TokenResponse tokens)
|
||||
: base(context, options, user, tokens)
|
||||
{
|
||||
IDictionary<string, JToken> userAsDictionary = user;
|
||||
|
||||
JToken userId = User["id"];
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ArgumentException(Resources.Exception_MissingId, nameof(user));
|
||||
}
|
||||
|
||||
Id = userId.ToString();
|
||||
Name = PropertyValueIfExists("name", userAsDictionary);
|
||||
FirstName = PropertyValueIfExists("first_name", userAsDictionary);
|
||||
LastName = PropertyValueIfExists("last_name", userAsDictionary);
|
||||
|
||||
if (userAsDictionary.ContainsKey("emails"))
|
||||
{
|
||||
JToken emailsNode = user["emails"];
|
||||
foreach (var childAsProperty in emailsNode.OfType<JProperty>().Where(childAsProperty => childAsProperty.Name == "preferred"))
|
||||
{
|
||||
Email = childAsProperty.Value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft Account user ID.
|
||||
/// </summary>
|
||||
public string Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's first name.
|
||||
/// </summary>
|
||||
public string FirstName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's last name.
|
||||
/// </summary>
|
||||
public string LastName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's email address.
|
||||
/// </summary>
|
||||
public string Email { get; private set; }
|
||||
|
||||
private static string PropertyValueIfExists(string property, IDictionary<string, JToken> dictionary)
|
||||
{
|
||||
return dictionary.ContainsKey(property) ? dictionary[property].ToString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||
{
|
||||
/// <summary>
|
||||
/// Default <see cref="IMicrosoftAccountAuthenticationNotifications"/> implementation.
|
||||
/// </summary>
|
||||
public class MicrosoftAccountAuthenticationNotifications : OAuthAuthenticationNotifications, IMicrosoftAccountAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="MicrosoftAccountAuthenticationNotifications"/>
|
||||
/// </summary>
|
||||
public MicrosoftAccountAuthenticationNotifications()
|
||||
{
|
||||
OnAuthenticated = context => Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||
/// </summary>
|
||||
public Func<MicrosoftAccountAuthenticatedContext, Task> OnAuthenticated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever Microsoft succesfully authenticates a user
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task Authenticated(MicrosoftAccountAuthenticatedContext context)
|
||||
{
|
||||
return OnAuthenticated(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
|
|
@ -12,21 +14,21 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
{
|
||||
/// <summary>
|
||||
/// Invoked after the provider successfully authenticates a user. This can be used to retrieve user information.
|
||||
/// This notification may not be invoked by sub-classes of OAuthAuthenticationHandler if they override GetUserInformationAsync.
|
||||
/// This notification may not be invoked by sub-classes of OAuthAuthenticationHandler if they override CreateTicketAsync.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task GetUserInformationAsync(OAuthGetUserInformationContext context);
|
||||
Task Authenticated(OAuthAuthenticatedContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// Invoked prior to the <see cref="ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
Task ReturnEndpoint(OAuthReturnEndpointContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a Challenge causes a redirect to authorize endpoint in the Microsoft middleware.
|
||||
/// Called when a Challenge causes a redirect to the authorize endpoint.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
|
||||
void ApplyRedirect(OAuthApplyRedirectContext context);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
|
|
@ -20,54 +22,97 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// Initializes a new <see cref="OAuthAuthenticatedContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP environment.</param>
|
||||
/// <param name="user">The JSON-serialized user.</param>
|
||||
/// <param name="options">The options used by the authentication middleware.</param>
|
||||
/// <param name="backchannel">The HTTP client used by the authentication middleware</param>
|
||||
/// <param name="tokens">The tokens returned from the token endpoint.</param>
|
||||
public OAuthAuthenticatedContext(HttpContext context, OAuthAuthenticationOptions options, JObject user,
|
||||
TokenResponse tokens)
|
||||
: base(context, options)
|
||||
public OAuthAuthenticatedContext(
|
||||
[NotNull] HttpContext context,
|
||||
[NotNull] OAuthAuthenticationOptions options,
|
||||
[NotNull] HttpClient backchannel,
|
||||
[NotNull] OAuthTokenResponse tokens)
|
||||
: this(context, options, backchannel, tokens, user: new JObject())
|
||||
{
|
||||
User = user;
|
||||
AccessToken = tokens.AccessToken;
|
||||
TokenType = tokens.TokenType;
|
||||
RefreshToken = tokens.RefreshToken;
|
||||
|
||||
int expiresInValue;
|
||||
if (Int32.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out expiresInValue))
|
||||
{
|
||||
ExpiresIn = TimeSpan.FromSeconds(expiresInValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the JSON-serialized user.
|
||||
/// Initializes a new <see cref="OAuthAuthenticatedContext"/>.
|
||||
/// </summary>
|
||||
public JObject User { get; protected set; }
|
||||
/// <param name="context">The HTTP environment.</param>
|
||||
/// <param name="options">The options used by the authentication middleware.</param>
|
||||
/// <param name="backchannel">The HTTP client used by the authentication middleware</param>
|
||||
/// <param name="tokens">The tokens returned from the token endpoint.</param>
|
||||
/// <param name="user">The JSON-serialized user.</param>
|
||||
public OAuthAuthenticatedContext(
|
||||
[NotNull] HttpContext context,
|
||||
[NotNull] OAuthAuthenticationOptions options,
|
||||
[NotNull] HttpClient backchannel,
|
||||
[NotNull] OAuthTokenResponse tokens,
|
||||
[NotNull] JObject user)
|
||||
: base(context, options)
|
||||
{
|
||||
TokenResponse = tokens;
|
||||
Backchannel = backchannel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the JSON-serialized user or an empty
|
||||
/// <see cref="JObject"/> if it is not available.
|
||||
/// </summary>
|
||||
public JObject User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the token response returned by the authentication service.
|
||||
/// </summary>
|
||||
public OAuthTokenResponse TokenResponse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token provided by the authentication service.
|
||||
/// </summary>
|
||||
public string AccessToken { get; protected set; }
|
||||
public string AccessToken => TokenResponse.AccessToken;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token type provided by the authentication service.
|
||||
/// </summary>
|
||||
public string TokenType { get; protected set; }
|
||||
public string TokenType => TokenResponse.TokenType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the refresh token provided by the authentication service.
|
||||
/// </summary>
|
||||
public string RefreshToken { get; protected set; }
|
||||
public string RefreshToken => TokenResponse.RefreshToken;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token expiration time.
|
||||
/// </summary>
|
||||
public TimeSpan? ExpiresIn { get; protected set; }
|
||||
public TimeSpan? ExpiresIn
|
||||
{
|
||||
get
|
||||
{
|
||||
int value;
|
||||
if (int.TryParse(TokenResponse.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
|
||||
{
|
||||
return TimeSpan.FromSeconds(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the backchannel used to communicate with the provider.
|
||||
/// </summary>
|
||||
public HttpClient Backchannel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ClaimsIdentity"/> representing the user.
|
||||
/// Gets the <see cref="ClaimsPrincipal"/> representing the user.
|
||||
/// </summary>
|
||||
public ClaimsPrincipal Principal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main identity exposed by <see cref="Principal"/>.
|
||||
/// This property returns <c>null</c> when <see cref="Principal"/> is <c>null</c>.
|
||||
/// </summary>
|
||||
public ClaimsIdentity Identity => Principal?.Identity as ClaimsIdentity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a property bag for common authentication properties.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
|
|
@ -11,58 +13,39 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// </summary>
|
||||
public class OAuthAuthenticationNotifications : IOAuthAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="OAuthAuthenticationNotifications"/>
|
||||
/// </summary>
|
||||
public OAuthAuthenticationNotifications()
|
||||
{
|
||||
OnGetUserInformationAsync = OAuthAuthenticationDefaults.DefaultOnGetUserInformationAsync;
|
||||
OnReturnEndpoint = context => Task.FromResult(0);
|
||||
OnApplyRedirect = context => context.Response.Redirect(context.RedirectUri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||
/// </summary>
|
||||
public Func<OAuthGetUserInformationContext, Task> OnGetUserInformationAsync { get; set; }
|
||||
public Func<OAuthAuthenticatedContext, Task> OnAuthenticated { get; set; } = context => Task.FromResult(0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
|
||||
/// </summary>
|
||||
public Func<OAuthReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
|
||||
public Func<OAuthReturnEndpointContext, Task> OnReturnEndpoint { get; set; } = context => Task.FromResult(0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked.
|
||||
/// </summary>
|
||||
public Action<OAuthApplyRedirectContext> OnApplyRedirect { get; set; }
|
||||
public Action<OAuthApplyRedirectContext> OnApplyRedirect { get; set; } = context => context.Response.Redirect(context.RedirectUri);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after the provider successfully authenticates a user.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="ClaimsIdentity"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task GetUserInformationAsync(OAuthGetUserInformationContext context)
|
||||
{
|
||||
return OnGetUserInformationAsync(context);
|
||||
}
|
||||
public virtual Task Authenticated(OAuthAuthenticatedContext context) => OnAuthenticated(context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// Invoked prior to the <see cref="ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/></param>
|
||||
/// <param name="context">Contains information about the login session as well as the user <see cref="ClaimsIdentity"/></param>
|
||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||
public virtual Task ReturnEndpoint(OAuthReturnEndpointContext context)
|
||||
{
|
||||
return OnReturnEndpoint(context);
|
||||
}
|
||||
public virtual Task ReturnEndpoint(OAuthReturnEndpointContext context) => OnReturnEndpoint(context);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a Challenge causes a redirect to authorize endpoint in the OAuth middleware.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
|
||||
public virtual void ApplyRedirect(OAuthApplyRedirectContext context)
|
||||
{
|
||||
OnApplyRedirect(context);
|
||||
}
|
||||
public virtual void ApplyRedirect(OAuthApplyRedirectContext context) => OnApplyRedirect(context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||
/// </summary>
|
||||
public class OAuthGetUserInformationContext : BaseContext<OAuthAuthenticationOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="OAuthGetUserInformationContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP environment.</param>
|
||||
/// <param name="user">The JSON-serialized user.</param>
|
||||
/// <param name="tokens">The tokens returned from the token endpoint.</param>
|
||||
public OAuthGetUserInformationContext(HttpContext context, OAuthAuthenticationOptions options, HttpClient backchannel, TokenResponse tokens)
|
||||
: base(context, options)
|
||||
{
|
||||
AccessToken = tokens.AccessToken;
|
||||
TokenType = tokens.TokenType;
|
||||
RefreshToken = tokens.RefreshToken;
|
||||
Backchannel = backchannel;
|
||||
|
||||
int expiresInValue;
|
||||
if (Int32.TryParse(tokens.ExpiresIn, NumberStyles.Integer, CultureInfo.InvariantCulture, out expiresInValue))
|
||||
{
|
||||
ExpiresIn = TimeSpan.FromSeconds(expiresInValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token provided by the authentication service.
|
||||
/// </summary>
|
||||
public string AccessToken { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token type provided by the authentication service.
|
||||
/// </summary>
|
||||
public string TokenType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the refresh token provided by the authentication service.
|
||||
/// </summary>
|
||||
public string RefreshToken { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token expiration time.
|
||||
/// </summary>
|
||||
public TimeSpan? ExpiresIn { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the backchannel used to communicate with the provider.
|
||||
/// </summary>
|
||||
public HttpClient Backchannel { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ClaimsPrincipal"/> representing the user.
|
||||
/// </summary>
|
||||
public ClaimsPrincipal Principal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a property bag for common authentication properties.
|
||||
/// </summary>
|
||||
public AuthenticationProperties Properties { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
public static class OAuthAuthenticationDefaults
|
||||
{
|
||||
public static readonly Func<OAuthGetUserInformationContext, Task> DefaultOnGetUserInformationAsync = context =>
|
||||
{
|
||||
// If the developer doesn't specify a user-info callback, just give them the tokens.
|
||||
var identity = new ClaimsIdentity(
|
||||
context.Options.AuthenticationScheme,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType);
|
||||
|
||||
identity.AddClaim(new Claim("access_token", context.AccessToken, ClaimValueTypes.String, context.Options.AuthenticationScheme));
|
||||
if (!string.IsNullOrEmpty(context.RefreshToken))
|
||||
{
|
||||
identity.AddClaim(new Claim("refresh_token", context.RefreshToken, ClaimValueTypes.String, context.Options.AuthenticationScheme));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(context.TokenType))
|
||||
{
|
||||
identity.AddClaim(new Claim("token_type", context.TokenType, ClaimValueTypes.String, context.Options.AuthenticationScheme));
|
||||
}
|
||||
if (context.ExpiresIn.HasValue)
|
||||
{
|
||||
identity.AddClaim(new Claim("expires_in", context.ExpiresIn.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture),
|
||||
ClaimValueTypes.String, context.Options.AuthenticationScheme));
|
||||
}
|
||||
context.Principal = new ClaimsPrincipal(identity);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -19,10 +19,10 @@ namespace Microsoft.AspNet.Builder
|
|||
/// <param name="app">The <see cref="IApplicationBuilder"/> passed to the configure method.</param>
|
||||
/// <param name="options">The middleware configuration options.</param>
|
||||
/// <returns>The updated <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseOAuthAuthentication([NotNull] this IApplicationBuilder app, [NotNull] string authenticationScheme, Action<OAuthAuthenticationOptions<IOAuthAuthenticationNotifications>> configureOptions = null)
|
||||
public static IApplicationBuilder UseOAuthAuthentication([NotNull] this IApplicationBuilder app, [NotNull] string authenticationScheme, Action<OAuthAuthenticationOptions> configureOptions = null)
|
||||
{
|
||||
return app.UseMiddleware<OAuthAuthenticationMiddleware<OAuthAuthenticationOptions<IOAuthAuthenticationNotifications>, IOAuthAuthenticationNotifications>>(
|
||||
new ConfigureOptions<OAuthAuthenticationOptions<IOAuthAuthenticationNotifications>>(options =>
|
||||
return app.UseMiddleware<OAuthAuthenticationMiddleware<OAuthAuthenticationOptions>>(
|
||||
new ConfigureOptions<OAuthAuthenticationOptions>(options =>
|
||||
{
|
||||
options.AuthenticationScheme = authenticationScheme;
|
||||
options.Caption = authenticationScheme;
|
||||
|
|
@ -30,10 +30,6 @@ namespace Microsoft.AspNet.Builder
|
|||
{
|
||||
configureOptions(options);
|
||||
}
|
||||
if (options.Notifications == null)
|
||||
{
|
||||
options.Notifications = new OAuthAuthenticationNotifications();
|
||||
}
|
||||
})
|
||||
{
|
||||
Name = authenticationScheme,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.DataHandler.Encoder;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
|
@ -19,9 +20,7 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
public class OAuthAuthenticationHandler<TOptions, TNotifications> : AuthenticationHandler<TOptions>
|
||||
where TOptions : OAuthAuthenticationOptions<TNotifications>
|
||||
where TNotifications : IOAuthAuthenticationNotifications
|
||||
public class OAuthAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : OAuthAuthenticationOptions
|
||||
{
|
||||
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
|
||||
|
||||
|
|
@ -124,7 +123,33 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
return await GetUserInformationAsync(properties, tokens);
|
||||
var identity = new ClaimsIdentity(Options.ClaimsIssuer);
|
||||
|
||||
if (Options.SaveTokensAsClaims)
|
||||
{
|
||||
identity.AddClaim(new Claim("access_token", tokens.AccessToken,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
|
||||
if (!string.IsNullOrEmpty(tokens.RefreshToken))
|
||||
{
|
||||
identity.AddClaim(new Claim("refresh_token", tokens.RefreshToken,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tokens.TokenType))
|
||||
{
|
||||
identity.AddClaim(new Claim("token_type", tokens.TokenType,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tokens.ExpiresIn))
|
||||
{
|
||||
identity.AddClaim(new Claim("expires_in", tokens.ExpiresIn,
|
||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
}
|
||||
|
||||
return await CreateTicketAsync(identity, properties, tokens);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -133,7 +158,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<TokenResponse> ExchangeCodeAsync(string code, string redirectUri)
|
||||
protected virtual async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri)
|
||||
{
|
||||
var tokenRequestParameters = new Dictionary<string, string>()
|
||||
{
|
||||
|
|
@ -151,20 +176,27 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
requestMessage.Content = requestContent;
|
||||
var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var oauthTokenResponse = await response.Content.ReadAsStringAsync();
|
||||
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||
|
||||
var oauth2Token = JObject.Parse(oauthTokenResponse);
|
||||
return new TokenResponse(oauth2Token);
|
||||
return new OAuthTokenResponse(payload);
|
||||
}
|
||||
|
||||
protected virtual async Task<AuthenticationTicket> GetUserInformationAsync(AuthenticationProperties properties, TokenResponse tokens)
|
||||
protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
|
||||
{
|
||||
var context = new OAuthGetUserInformationContext(Context, Options, Backchannel, tokens)
|
||||
var notification = new OAuthAuthenticatedContext(Context, Options, Backchannel, tokens)
|
||||
{
|
||||
Properties = properties,
|
||||
Principal = new ClaimsPrincipal(identity),
|
||||
Properties = properties
|
||||
};
|
||||
await Options.Notifications.GetUserInformationAsync(context);
|
||||
return new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme);
|
||||
|
||||
await Options.Notifications.Authenticated(notification);
|
||||
|
||||
if (notification.Principal?.Identity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AuthenticationTicket(notification.Principal, notification.Properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
protected override Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// An ASP.NET middleware for authenticating users using OAuth services.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
||||
public class OAuthAuthenticationMiddleware<TOptions, TNotifications> : AuthenticationMiddleware<TOptions>
|
||||
where TOptions : OAuthAuthenticationOptions<TNotifications>, new()
|
||||
where TNotifications : IOAuthAuthenticationNotifications
|
||||
public class OAuthAuthenticationMiddleware<TOptions> : AuthenticationMiddleware<TOptions> where TOptions : OAuthAuthenticationOptions, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="OAuthAuthenticationMiddleware"/>.
|
||||
|
|
@ -58,12 +56,12 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
|
||||
if (string.IsNullOrWhiteSpace(Options.AuthorizationEndpoint))
|
||||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "AuthorizationEndpoint"));
|
||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AuthorizationEndpoint)));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Options.TokenEndpoint))
|
||||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "TokenEndpoint"));
|
||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.TokenEndpoint)));
|
||||
}
|
||||
|
||||
if (Options.StateDataFormat == null)
|
||||
|
|
@ -82,10 +80,6 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
{
|
||||
Options.SignInScheme = externalOptions.Options.SignInScheme;
|
||||
}
|
||||
if (string.IsNullOrEmpty(Options.SignInScheme))
|
||||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "SignInScheme"));
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpClient Backchannel { get; private set; }
|
||||
|
|
@ -96,11 +90,11 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// <returns>An <see cref="AuthenticationHandler"/> configured with the <see cref="OAuthAuthenticationOptions"/> supplied to the constructor.</returns>
|
||||
protected override AuthenticationHandler<TOptions> CreateHandler()
|
||||
{
|
||||
return new OAuthAuthenticationHandler<TOptions, TNotifications>(Backchannel);
|
||||
return new OAuthAuthenticationHandler<TOptions>(Backchannel);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
|
||||
private static HttpMessageHandler ResolveHttpMessageHandler(OAuthAuthenticationOptions<TNotifications> options)
|
||||
private static HttpMessageHandler ResolveHttpMessageHandler(OAuthAuthenticationOptions options)
|
||||
{
|
||||
HttpMessageHandler handler = options.BackchannelHttpHandler ??
|
||||
#if DNX451
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
|
|
@ -14,15 +16,6 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// </summary>
|
||||
public class OAuthAuthenticationOptions : AuthenticationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="OAuthAuthenticationOptions"/>.
|
||||
/// </summary>
|
||||
public OAuthAuthenticationOptions()
|
||||
{
|
||||
Scope = new List<string>();
|
||||
BackchannelTimeout = TimeSpan.FromSeconds(60);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the provider-assigned client id.
|
||||
/// </summary>
|
||||
|
|
@ -46,7 +39,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// <summary>
|
||||
/// Gets or sets the URI the middleware will access to obtain the user information.
|
||||
/// This value is not used in the default implementation, it is for use in custom implementations of
|
||||
/// IOAuthAuthenticationNotifications.GetUserInformationAsync or OAuthAuthenticationHandler.GetUserInformationAsync.
|
||||
/// IOAuthAuthenticationNotifications.Authenticated or OAuthAuthenticationHandler.CreateTicketAsync.
|
||||
/// </summary>
|
||||
public string UserInformationEndpoint { get; set; }
|
||||
|
||||
|
|
@ -62,6 +55,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// validating the subject name and if the signing chain is a trusted party.</remarks>
|
||||
public ICertificateValidator BackchannelCertificateValidator { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets the text that the user can display on a sign in user interface.
|
||||
/// </summary>
|
||||
|
|
@ -77,7 +71,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// <value>
|
||||
/// The back channel timeout.
|
||||
/// </value>
|
||||
public TimeSpan BackchannelTimeout { get; set; }
|
||||
public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromSeconds(60);
|
||||
|
||||
/// <summary>
|
||||
/// The HttpMessageHandler used to communicate with the auth provider.
|
||||
|
|
@ -86,10 +80,15 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// </summary>
|
||||
public HttpMessageHandler BackchannelHttpHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IOAuthAuthenticationNotifications"/> used to handle authentication events.
|
||||
/// </summary>
|
||||
public IOAuthAuthenticationNotifications Notifications { get; [param: NotNull] set; } = new OAuthAuthenticationNotifications();
|
||||
|
||||
/// <summary>
|
||||
/// A list of permissions to request.
|
||||
/// </summary>
|
||||
public IList<string> Scope { get; private set; }
|
||||
public IList<string> Scope { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The request path within the application's base path where the user-agent will be returned.
|
||||
|
|
@ -109,5 +108,13 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
/// Gets or sets the type used to secure data handled by the middleware.
|
||||
/// </summary>
|
||||
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines whether access and refresh tokens should be stored in the
|
||||
/// <see cref="ClaimsPrincipal"/> after a successful authentication.
|
||||
/// You can set this property to <c>false</c> to reduce the size of the final
|
||||
/// authentication cookie. Note that social providers set this property to <c>false</c> by default.
|
||||
/// </summary>
|
||||
public bool SaveTokensAsClaims { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration options for <see cref="OAuthAuthenticationMiddleware"/>.
|
||||
/// </summary>
|
||||
public class OAuthAuthenticationOptions<TNotifications> : OAuthAuthenticationOptions where TNotifications : IOAuthAuthenticationNotifications
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IOAuthAuthenticationNotifications"/> used to handle authentication events.
|
||||
/// </summary>
|
||||
public TNotifications Notifications { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,9 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Microsoft.AspNet.Authentication.OAuth
|
||||
{
|
||||
public class TokenResponse
|
||||
public class OAuthTokenResponse
|
||||
{
|
||||
public TokenResponse(JObject response)
|
||||
public OAuthTokenResponse(JObject response)
|
||||
{
|
||||
Response = response;
|
||||
AccessToken = response.Value<string>("access_token");
|
||||
|
|
@ -82,24 +82,6 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
var accessToken = await ObtainAccessTokenAsync(Options.ConsumerKey, Options.ConsumerSecret, requestToken, oauthVerifier);
|
||||
|
||||
var context = new TwitterAuthenticatedContext(Context, accessToken.UserId, accessToken.ScreenName, accessToken.Token, accessToken.TokenSecret);
|
||||
|
||||
context.Principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, accessToken.UserId, "http://www.w3.org/2001/XMLSchema#string", Options.ClaimsIssuer),
|
||||
new Claim(ClaimTypes.Name, accessToken.ScreenName, "http://www.w3.org/2001/XMLSchema#string", Options.ClaimsIssuer),
|
||||
new Claim("urn:twitter:userid", accessToken.UserId, "http://www.w3.org/2001/XMLSchema#string", Options.ClaimsIssuer),
|
||||
new Claim("urn:twitter:screenname", accessToken.ScreenName, "http://www.w3.org/2001/XMLSchema#string", Options.ClaimsIssuer)
|
||||
},
|
||||
Options.ClaimsIssuer,
|
||||
ClaimsIdentity.DefaultNameClaimType,
|
||||
ClaimsIdentity.DefaultRoleClaimType));
|
||||
context.Properties = requestToken.Properties;
|
||||
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
|
|
@ -108,9 +90,23 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
|
||||
Response.Cookies.Delete(StateCookie, cookieOptions);
|
||||
|
||||
await Options.Notifications.Authenticated(context);
|
||||
var accessToken = await ObtainAccessTokenAsync(Options.ConsumerKey, Options.ConsumerSecret, requestToken, oauthVerifier);
|
||||
|
||||
var identity = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, accessToken.UserId, ClaimValueTypes.String, Options.ClaimsIssuer),
|
||||
new Claim(ClaimTypes.Name, accessToken.ScreenName, ClaimValueTypes.String, Options.ClaimsIssuer),
|
||||
new Claim("urn:twitter:userid", accessToken.UserId, ClaimValueTypes.String, Options.ClaimsIssuer),
|
||||
new Claim("urn:twitter:screenname", accessToken.ScreenName, ClaimValueTypes.String, Options.ClaimsIssuer)
|
||||
},
|
||||
Options.ClaimsIssuer);
|
||||
|
||||
return new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme);
|
||||
if (Options.SaveTokensAsClaims)
|
||||
{
|
||||
identity.AddClaim(new Claim("access_token", accessToken.Token, ClaimValueTypes.String, Options.ClaimsIssuer));
|
||||
}
|
||||
|
||||
return await CreateTicketAsync(identity, properties, accessToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -118,6 +114,25 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, AccessToken token)
|
||||
{
|
||||
var notification = new TwitterAuthenticatedContext(Context, token.UserId, token.ScreenName, token.Token, token.TokenSecret)
|
||||
{
|
||||
Principal = new ClaimsPrincipal(identity),
|
||||
Properties = properties
|
||||
};
|
||||
|
||||
await Options.Notifications.Authenticated(notification);
|
||||
|
||||
if (notification.Principal?.Identity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AuthenticationTicket(notification.Principal, notification.Properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
protected override async Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||
{
|
||||
var properties = new AuthenticationProperties(context.Properties);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Authentication.Twitter.Messages;
|
||||
|
||||
|
|
@ -102,8 +103,16 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
public ISecureDataFormat<RequestToken> StateDataFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ITwitterAuthenticationProvider"/> used to handle authentication events.
|
||||
/// Gets or sets the <see cref="ITwitterAuthenticationNotifications"/> used to handle authentication events.
|
||||
/// </summary>
|
||||
public ITwitterAuthenticationNotifications Notifications { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines whether access tokens should be stored in the
|
||||
/// <see cref="ClaimsPrincipal"/> after a successful authentication.
|
||||
/// This property is set to <c>false</c> by default to reduce
|
||||
/// the size of the final authentication cookie.
|
||||
/// </summary>
|
||||
public bool SaveTokensAsClaims { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
|
|
@ -33,7 +34,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
{
|
||||
options.AppId = "Test App Id";
|
||||
options.AppSecret = "Test App Secret";
|
||||
options.Notifications = new FacebookAuthenticationNotifications
|
||||
options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
OnApplyRedirect = context =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Security.Claims;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.DataHandler;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
|
@ -198,7 +199,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
options.Notifications = new GoogleAuthenticationNotifications
|
||||
options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
OnApplyRedirect = context =>
|
||||
{
|
||||
|
|
@ -414,14 +415,14 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
return null;
|
||||
}
|
||||
};
|
||||
options.Notifications = new GoogleAuthenticationNotifications()
|
||||
options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
OnAuthenticated = context =>
|
||||
{
|
||||
var refreshToken = context.RefreshToken;
|
||||
context.Principal.AddIdentity(new ClaimsIdentity(new Claim[] { new Claim("RefreshToken", refreshToken, ClaimValueTypes.String, "Google") }, "Google"));
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
{
|
||||
var refreshToken = context.RefreshToken;
|
||||
context.Principal.AddIdentity(new ClaimsIdentity(new Claim[] { new Claim("RefreshToken", refreshToken, ClaimValueTypes.String, "Google") }, "Google"));
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
});
|
||||
var properties = new AuthenticationProperties();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.DataHandler;
|
||||
using Microsoft.AspNet.Authentication.MicrosoftAccount;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
|||
{
|
||||
options.ClientId = "Test Client Id";
|
||||
options.ClientSecret = "Test Client Secret";
|
||||
options.Notifications = new MicrosoftAccountAuthenticationNotifications
|
||||
options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
OnApplyRedirect = context =>
|
||||
{
|
||||
|
|
@ -143,7 +144,7 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
|||
return null;
|
||||
}
|
||||
};
|
||||
options.Notifications = new MicrosoftAccountAuthenticationNotifications
|
||||
options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
OnAuthenticated = context =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue