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 System.Security.Claims;
|
||||||
using Microsoft.AspNet.Authentication.Cookies;
|
using Microsoft.AspNet.Authentication.Cookies;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
|
|
@ -25,10 +26,11 @@ namespace CookieSample
|
||||||
|
|
||||||
app.Run(async context =>
|
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);
|
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);
|
||||||
|
|
||||||
context.Response.ContentType = "text/plain";
|
context.Response.ContentType = "text/plain";
|
||||||
await context.Response.WriteAsync("Hello First timer");
|
await context.Response.WriteAsync("Hello First timer");
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -27,7 +28,7 @@ namespace CookieSessionSample
|
||||||
|
|
||||||
app.Run(async context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
|
||||||
{
|
{
|
||||||
// Make a large identity
|
// Make a large identity
|
||||||
var claims = new List<Claim>(1001);
|
var claims = new List<Claim>(1001);
|
||||||
|
|
@ -36,7 +37,10 @@ namespace CookieSessionSample
|
||||||
{
|
{
|
||||||
claims.Add(new Claim(ClaimTypes.Role, "SomeRandomGroup" + i, ClaimValueTypes.String, "IssuedByBob", "OriginalIssuerJoe"));
|
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";
|
context.Response.ContentType = "text/plain";
|
||||||
await context.Response.WriteAsync("Hello First timer");
|
await context.Response.WriteAsync("Hello First timer");
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.AspNet.Builder;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
using Microsoft.AspNet.Authentication;
|
using Microsoft.AspNet.Authentication;
|
||||||
|
|
@ -38,7 +39,7 @@ namespace OpenIdConnectSample
|
||||||
|
|
||||||
app.Run(async context =>
|
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 = "/" });
|
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
@ -110,6 +111,7 @@ namespace CookieSample
|
||||||
options.Caption = "MicrosoftAccount - Requires project changes";
|
options.Caption = "MicrosoftAccount - Requires project changes";
|
||||||
options.ClientId = "00000000480FF62E";
|
options.ClientId = "00000000480FF62E";
|
||||||
options.ClientSecret = "bLw2JIvf8Y1TaToipPEqxTVlOeJwCUsr";
|
options.ClientSecret = "bLw2JIvf8Y1TaToipPEqxTVlOeJwCUsr";
|
||||||
|
options.Scope.Add("wl.emails");
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://github.com/settings/applications/
|
// https://github.com/settings/applications/
|
||||||
|
|
@ -131,48 +133,53 @@ namespace CookieSample
|
||||||
options.TokenEndpoint = "https://github.com/login/oauth/access_token";
|
options.TokenEndpoint = "https://github.com/login/oauth/access_token";
|
||||||
options.UserInformationEndpoint = "https://api.github.com/user";
|
options.UserInformationEndpoint = "https://api.github.com/user";
|
||||||
options.ClaimsIssuer = "OAuth2-Github";
|
options.ClaimsIssuer = "OAuth2-Github";
|
||||||
|
options.SaveTokensAsClaims = false;
|
||||||
// Retrieving user information is unique to each provider.
|
// 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
|
// Get the GitHub user
|
||||||
var userRequest = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
|
var request = new HttpRequestMessage(HttpMethod.Get, notification.Options.UserInformationEndpoint);
|
||||||
userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", notification.AccessToken);
|
||||||
userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.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 identity = new ClaimsIdentity(
|
var response = await notification.Backchannel.SendAsync(request, notification.HttpContext.RequestAborted);
|
||||||
context.Options.AuthenticationScheme,
|
response.EnsureSuccessStatusCode();
|
||||||
ClaimsIdentity.DefaultNameClaimType,
|
|
||||||
ClaimsIdentity.DefaultRoleClaimType);
|
|
||||||
|
|
||||||
JToken value;
|
var user = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||||
var id = user.TryGetValue("id", out value) ? value.ToString() : null;
|
|
||||||
if (!string.IsNullOrEmpty(id))
|
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))
|
if (!string.IsNullOrEmpty(userName))
|
||||||
{
|
{
|
||||||
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, context.Options.ClaimsIssuer));
|
notification.Identity.AddClaim(new Claim(
|
||||||
}
|
ClaimsIdentity.DefaultNameClaimType, userName,
|
||||||
var name = user.TryGetValue("name", out value) ? value.ToString() : null;
|
ClaimValueTypes.String, notification.Options.ClaimsIssuer));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 =>
|
signoutApp.Run(async context =>
|
||||||
{
|
{
|
||||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
|
||||||
context.Response.ContentType = "text/html";
|
context.Response.ContentType = "text/html";
|
||||||
|
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
await context.Response.WriteAsync("<html><body>");
|
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("You have been logged out. Goodbye " + context.User.Identity.Name + "<br>");
|
||||||
await context.Response.WriteAsync("<a href=\"/\">Home</a>");
|
await context.Response.WriteAsync("<a href=\"/\">Home</a>");
|
||||||
|
|
@ -219,7 +226,7 @@ namespace CookieSample
|
||||||
// Deny anonymous request beyond this point.
|
// Deny anonymous request beyond this point.
|
||||||
app.Use(async (context, next) =>
|
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
|
// The cookie middleware will intercept this 401 and redirect to /login
|
||||||
await context.Authentication.ChallengeAsync();
|
await context.Authentication.ChallengeAsync();
|
||||||
|
|
@ -233,7 +240,7 @@ namespace CookieSample
|
||||||
{
|
{
|
||||||
context.Response.ContentType = "text/html";
|
context.Response.ContentType = "text/html";
|
||||||
await context.Response.WriteAsync("<html><body>");
|
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)
|
foreach (var claim in context.User.Claims)
|
||||||
{
|
{
|
||||||
await context.Response.WriteAsync(claim.Type + ": " + claim.Value + "<br>");
|
await context.Response.WriteAsync(claim.Type + ": " + claim.Value + "<br>");
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,14 @@ using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.Facebook
|
namespace Microsoft.AspNet.Authentication.Facebook
|
||||||
{
|
{
|
||||||
internal class FacebookAuthenticationHandler : OAuthAuthenticationHandler<FacebookAuthenticationOptions, IFacebookAuthenticationNotifications>
|
internal class FacebookAuthenticationHandler : OAuthAuthenticationHandler<FacebookAuthenticationOptions>
|
||||||
{
|
{
|
||||||
public FacebookAuthenticationHandler(HttpClient httpClient)
|
public FacebookAuthenticationHandler(HttpClient httpClient)
|
||||||
: base(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()
|
var queryBuilder = new QueryBuilder()
|
||||||
{
|
{
|
||||||
|
|
@ -35,70 +35,78 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
||||||
{ "client_secret", Options.AppSecret },
|
{ "client_secret", Options.AppSecret },
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokenResponse = await Backchannel.GetAsync(Options.TokenEndpoint + queryBuilder.ToString(), Context.RequestAborted);
|
var response = await Backchannel.GetAsync(Options.TokenEndpoint + queryBuilder.ToString(), Context.RequestAborted);
|
||||||
tokenResponse.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
var oauthTokenResponse = await tokenResponse.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
var form = new FormCollection(FormReader.ReadForm(oauthTokenResponse));
|
var form = new FormCollection(FormReader.ReadForm(await response.Content.ReadAsStringAsync()));
|
||||||
var response = new JObject();
|
var payload = new JObject();
|
||||||
foreach (string key in form.Keys)
|
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.
|
// 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)
|
if (Options.SendAppSecretProof)
|
||||||
{
|
{
|
||||||
graphAddress += "&appsecret_proof=" + GenerateAppSecretProof(tokens.AccessToken);
|
endpoint += "&appsecret_proof=" + GenerateAppSecretProof(tokens.AccessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
var graphResponse = await Backchannel.GetAsync(graphAddress, Context.RequestAborted);
|
var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted);
|
||||||
graphResponse.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
var text = await graphResponse.Content.ReadAsStringAsync();
|
|
||||||
var user = JObject.Parse(text);
|
|
||||||
|
|
||||||
var context = new FacebookAuthenticatedContext(Context, Options, user, tokens);
|
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||||
var identity = new ClaimsIdentity(
|
|
||||||
Options.ClaimsIssuer,
|
var notification = new OAuthAuthenticatedContext(Context, Options, Backchannel, tokens, payload)
|
||||||
ClaimsIdentity.DefaultNameClaimType,
|
|
||||||
ClaimsIdentity.DefaultRoleClaimType);
|
|
||||||
if (!string.IsNullOrEmpty(context.Id))
|
|
||||||
{
|
{
|
||||||
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.
|
// 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)
|
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>
|
/// <summary>
|
||||||
/// An ASP.NET middleware for authenticating users using Facebook.
|
/// An ASP.NET middleware for authenticating users using Facebook.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FacebookAuthenticationMiddleware : OAuthAuthenticationMiddleware<FacebookAuthenticationOptions, IFacebookAuthenticationNotifications>
|
public class FacebookAuthenticationMiddleware : OAuthAuthenticationMiddleware<FacebookAuthenticationOptions>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="FacebookAuthenticationMiddleware"/>.
|
/// 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)));
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, nameof(Options.AppSecret)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Options.Notifications == null)
|
|
||||||
{
|
|
||||||
Options.Notifications = new FacebookAuthenticationNotifications();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Http;
|
||||||
using Microsoft.AspNet.Authentication.OAuth;
|
using Microsoft.AspNet.Authentication.OAuth;
|
||||||
|
|
||||||
|
|
@ -9,7 +10,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configuration options for <see cref="FacebookAuthenticationMiddleware"/>.
|
/// Configuration options for <see cref="FacebookAuthenticationMiddleware"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FacebookAuthenticationOptions : OAuthAuthenticationOptions<IFacebookAuthenticationNotifications>
|
public class FacebookAuthenticationOptions : OAuthAuthenticationOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="FacebookAuthenticationOptions"/>.
|
/// Initializes a new <see cref="FacebookAuthenticationOptions"/>.
|
||||||
|
|
@ -23,6 +24,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
||||||
AuthorizationEndpoint = FacebookAuthenticationDefaults.AuthorizationEndpoint;
|
AuthorizationEndpoint = FacebookAuthenticationDefaults.AuthorizationEndpoint;
|
||||||
TokenEndpoint = FacebookAuthenticationDefaults.TokenEndpoint;
|
TokenEndpoint = FacebookAuthenticationDefaults.TokenEndpoint;
|
||||||
UserInformationEndpoint = FacebookAuthenticationDefaults.UserInformationEndpoint;
|
UserInformationEndpoint = FacebookAuthenticationDefaults.UserInformationEndpoint;
|
||||||
|
SaveTokensAsClaims = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Facebook uses a non-standard term for this field.
|
// 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
|
namespace Microsoft.AspNet.Authentication.Google
|
||||||
{
|
{
|
||||||
internal class GoogleAuthenticationHandler : OAuthAuthenticationHandler<GoogleAuthenticationOptions, IGoogleAuthenticationNotifications>
|
internal class GoogleAuthenticationHandler : OAuthAuthenticationHandler<GoogleAuthenticationOptions>
|
||||||
{
|
{
|
||||||
public GoogleAuthenticationHandler(HttpClient httpClient)
|
public GoogleAuthenticationHandler(HttpClient httpClient)
|
||||||
: base(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
|
// Get the Google user
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
|
var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
|
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 response = await Backchannel.SendAsync(request, Context.RequestAborted);
|
||||||
var identity = new ClaimsIdentity(
|
response.EnsureSuccessStatusCode();
|
||||||
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,
|
Properties = properties,
|
||||||
ClaimValueTypes.String, Options.ClaimsIssuer));
|
Principal = new ClaimsPrincipal(identity)
|
||||||
}
|
};
|
||||||
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);
|
|
||||||
|
|
||||||
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?
|
// TODO: Abstract this properties override pattern into the base class?
|
||||||
|
|
|
||||||
|
|
@ -1,74 +1,46 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using Microsoft.Framework.Internal;
|
||||||
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 Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.Google
|
namespace Microsoft.AspNet.Authentication.Google
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets the Google user ID.
|
/// Gets the Google user ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; private set; }
|
public static string GetId([NotNull] JObject user) => user.Value<string>("id");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user's name.
|
/// Gets the user's name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; private set; }
|
public static string GetName([NotNull] JObject user) => user.Value<string>("displayName");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user's given name.
|
/// Gets the user's given name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GivenName { get; set; }
|
public static string GetGivenName([NotNull] JObject user) => TryGetValue(user, "name", "givenName");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user's family name.
|
/// Gets the user's family name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FamilyName { get; set; }
|
public static string GetFamilyName([NotNull] JObject user) => TryGetValue(user, "name", "familyName");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user's profile link.
|
/// Gets the user's profile link.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Profile { get; private set; }
|
public static string GetProfile([NotNull] JObject user) => user.Value<string>("url");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user's email.
|
/// Gets the user's email.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Email { get; private set; }
|
public static string GetEmail([NotNull] JObject user) => TryGetFirstValue(user, "emails", "value");
|
||||||
|
|
||||||
private static string TryGetValue(JObject user, string propertyName)
|
|
||||||
{
|
|
||||||
JToken value;
|
|
||||||
return user.TryGetValue(propertyName, out value) ? value.ToString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the given subProperty from a property.
|
// Get the given subProperty from a property.
|
||||||
private static string TryGetValue(JObject user, string propertyName, string subProperty)
|
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.
|
/// An ASP.NET middleware for authenticating users using Google OAuth 2.0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
||||||
public class GoogleAuthenticationMiddleware : OAuthAuthenticationMiddleware<GoogleAuthenticationOptions, IGoogleAuthenticationNotifications>
|
public class GoogleAuthenticationMiddleware : OAuthAuthenticationMiddleware<GoogleAuthenticationOptions>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="GoogleAuthenticationMiddleware"/>.
|
/// Initializes a new <see cref="GoogleAuthenticationMiddleware"/>.
|
||||||
|
|
@ -35,11 +35,6 @@ namespace Microsoft.AspNet.Authentication.Google
|
||||||
ConfigureOptions<GoogleAuthenticationOptions> configureOptions = null)
|
ConfigureOptions<GoogleAuthenticationOptions> configureOptions = null)
|
||||||
: base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions)
|
: base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions)
|
||||||
{
|
{
|
||||||
if (Options.Notifications == null)
|
|
||||||
{
|
|
||||||
Options.Notifications = new GoogleAuthenticationNotifications();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Options.Scope.Count == 0)
|
if (Options.Scope.Count == 0)
|
||||||
{
|
{
|
||||||
// Google OAuth 2.0 asks for non-empty scope. If user didn't set it, set default scope to
|
// 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>
|
/// <summary>
|
||||||
/// Configuration options for <see cref="GoogleAuthenticationMiddleware"/>.
|
/// Configuration options for <see cref="GoogleAuthenticationMiddleware"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GoogleAuthenticationOptions : OAuthAuthenticationOptions<IGoogleAuthenticationNotifications>
|
public class GoogleAuthenticationOptions : OAuthAuthenticationOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="GoogleAuthenticationOptions"/>.
|
/// Initializes a new <see cref="GoogleAuthenticationOptions"/>.
|
||||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
||||||
AuthorizationEndpoint = GoogleAuthenticationDefaults.AuthorizationEndpoint;
|
AuthorizationEndpoint = GoogleAuthenticationDefaults.AuthorizationEndpoint;
|
||||||
TokenEndpoint = GoogleAuthenticationDefaults.TokenEndpoint;
|
TokenEndpoint = GoogleAuthenticationDefaults.TokenEndpoint;
|
||||||
UserInformationEndpoint = GoogleAuthenticationDefaults.UserInformationEndpoint;
|
UserInformationEndpoint = GoogleAuthenticationDefaults.UserInformationEndpoint;
|
||||||
|
SaveTokensAsClaims = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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
|
namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||||
{
|
{
|
||||||
internal class MicrosoftAccountAuthenticationHandler : OAuthAuthenticationHandler<MicrosoftAccountAuthenticationOptions, IMicrosoftAccountAuthenticationNotifications>
|
internal class MicrosoftAccountAuthenticationHandler : OAuthAuthenticationHandler<MicrosoftAccountAuthenticationOptions>
|
||||||
{
|
{
|
||||||
public MicrosoftAccountAuthenticationHandler(HttpClient httpClient)
|
public MicrosoftAccountAuthenticationHandler(HttpClient httpClient)
|
||||||
: base(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);
|
var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
|
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);
|
var response = await Backchannel.SendAsync(request, Context.RequestAborted);
|
||||||
context.Properties = properties;
|
response.EnsureSuccessStatusCode();
|
||||||
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);
|
|
||||||
|
|
||||||
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>
|
/// <summary>
|
||||||
/// An ASP.NET middleware for authenticating users using the Microsoft Account service.
|
/// An ASP.NET middleware for authenticating users using the Microsoft Account service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MicrosoftAccountAuthenticationMiddleware : OAuthAuthenticationMiddleware<MicrosoftAccountAuthenticationOptions, IMicrosoftAccountAuthenticationNotifications>
|
public class MicrosoftAccountAuthenticationMiddleware : OAuthAuthenticationMiddleware<MicrosoftAccountAuthenticationOptions>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="MicrosoftAccountAuthenticationMiddleware"/>.
|
/// Initializes a new <see cref="MicrosoftAccountAuthenticationMiddleware"/>.
|
||||||
|
|
@ -33,10 +33,6 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||||
ConfigureOptions<MicrosoftAccountAuthenticationOptions> configureOptions = null)
|
ConfigureOptions<MicrosoftAccountAuthenticationOptions> configureOptions = null)
|
||||||
: base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions)
|
: base(next, dataProtectionProvider, loggerFactory, encoder, externalOptions, options, configureOptions)
|
||||||
{
|
{
|
||||||
if (Options.Notifications == null)
|
|
||||||
{
|
|
||||||
Options.Notifications = new MicrosoftAccountAuthenticationNotifications();
|
|
||||||
}
|
|
||||||
if (Options.Scope.Count == 0)
|
if (Options.Scope.Count == 0)
|
||||||
{
|
{
|
||||||
// LiveID requires a scope string, so if the user didn't set one we go for the least possible.
|
// 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>
|
/// <summary>
|
||||||
/// Configuration options for <see cref="MicrosoftAccountAuthenticationMiddleware"/>.
|
/// Configuration options for <see cref="MicrosoftAccountAuthenticationMiddleware"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MicrosoftAccountAuthenticationOptions : OAuthAuthenticationOptions<IMicrosoftAccountAuthenticationNotifications>
|
public class MicrosoftAccountAuthenticationOptions : OAuthAuthenticationOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="MicrosoftAccountAuthenticationOptions"/>.
|
/// Initializes a new <see cref="MicrosoftAccountAuthenticationOptions"/>.
|
||||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Authentication.MicrosoftAccount
|
||||||
AuthorizationEndpoint = MicrosoftAccountAuthenticationDefaults.AuthorizationEndpoint;
|
AuthorizationEndpoint = MicrosoftAccountAuthenticationDefaults.AuthorizationEndpoint;
|
||||||
TokenEndpoint = MicrosoftAccountAuthenticationDefaults.TokenEndpoint;
|
TokenEndpoint = MicrosoftAccountAuthenticationDefaults.TokenEndpoint;
|
||||||
UserInformationEndpoint = MicrosoftAccountAuthenticationDefaults.UserInformationEndpoint;
|
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.
|
// 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.
|
// 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 System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.OAuth
|
namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
|
|
@ -12,21 +14,21 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked after the provider successfully authenticates a user. This can be used to retrieve user information.
|
/// 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>
|
/// </summary>
|
||||||
/// <param name="context">Contains information about the login session.</param>
|
/// <param name="context">Contains information about the login session.</param>
|
||||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||||
Task GetUserInformationAsync(OAuthGetUserInformationContext context);
|
Task Authenticated(OAuthAuthenticatedContext context);
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||||
Task ReturnEndpoint(OAuthReturnEndpointContext context);
|
Task ReturnEndpoint(OAuthReturnEndpointContext context);
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
|
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
|
||||||
void ApplyRedirect(OAuthApplyRedirectContext context);
|
void ApplyRedirect(OAuthApplyRedirectContext context);
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNet.Authentication.Notifications;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
using Microsoft.AspNet.Authentication.Notifications;
|
using Microsoft.Framework.Internal;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.OAuth
|
namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
|
|
@ -20,54 +22,97 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
/// Initializes a new <see cref="OAuthAuthenticatedContext"/>.
|
/// Initializes a new <see cref="OAuthAuthenticatedContext"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The HTTP environment.</param>
|
/// <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>
|
/// <param name="tokens">The tokens returned from the token endpoint.</param>
|
||||||
public OAuthAuthenticatedContext(HttpContext context, OAuthAuthenticationOptions options, JObject user,
|
public OAuthAuthenticatedContext(
|
||||||
TokenResponse tokens)
|
[NotNull] HttpContext context,
|
||||||
: base(context, options)
|
[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>
|
/// <summary>
|
||||||
/// Gets the JSON-serialized user.
|
/// Initializes a new <see cref="OAuthAuthenticatedContext"/>.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets the access token provided by the authentication service.
|
/// Gets the access token provided by the authentication service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AccessToken { get; protected set; }
|
public string AccessToken => TokenResponse.AccessToken;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the access token type provided by the authentication service.
|
/// Gets the access token type provided by the authentication service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TokenType { get; protected set; }
|
public string TokenType => TokenResponse.TokenType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the refresh token provided by the authentication service.
|
/// Gets the refresh token provided by the authentication service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RefreshToken { get; protected set; }
|
public string RefreshToken => TokenResponse.RefreshToken;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the access token expiration time.
|
/// Gets the access token expiration time.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets the <see cref="ClaimsIdentity"/> representing the user.
|
/// Gets the <see cref="ClaimsPrincipal"/> representing the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ClaimsPrincipal Principal { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets a property bag for common authentication properties.
|
/// Gets or sets a property bag for common authentication properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.OAuth
|
namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
|
|
@ -11,58 +13,39 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OAuthAuthenticationNotifications : IOAuthAuthenticationNotifications
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<OAuthGetUserInformationContext, Task> OnGetUserInformationAsync { get; set; }
|
public Func<OAuthAuthenticatedContext, Task> OnAuthenticated { get; set; } = context => Task.FromResult(0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
|
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<OAuthReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
|
public Func<OAuthReturnEndpointContext, Task> OnReturnEndpoint { get; set; } = context => Task.FromResult(0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked.
|
/// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<OAuthApplyRedirectContext> OnApplyRedirect { get; set; }
|
public Action<OAuthApplyRedirectContext> OnApplyRedirect { get; set; } = context => context.Response.Redirect(context.RedirectUri);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked after the provider successfully authenticates a user.
|
/// Invoked after the provider successfully authenticates a user.
|
||||||
/// </summary>
|
/// </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>
|
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||||
public virtual Task GetUserInformationAsync(OAuthGetUserInformationContext context)
|
public virtual Task Authenticated(OAuthAuthenticatedContext context) => OnAuthenticated(context);
|
||||||
{
|
|
||||||
return OnGetUserInformationAsync(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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>
|
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||||
public virtual Task ReturnEndpoint(OAuthReturnEndpointContext context)
|
public virtual Task ReturnEndpoint(OAuthReturnEndpointContext context) => OnReturnEndpoint(context);
|
||||||
{
|
|
||||||
return OnReturnEndpoint(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a Challenge causes a redirect to authorize endpoint in the OAuth middleware.
|
/// Called when a Challenge causes a redirect to authorize endpoint in the OAuth middleware.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
|
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
|
||||||
public virtual void ApplyRedirect(OAuthApplyRedirectContext context)
|
public virtual void ApplyRedirect(OAuthApplyRedirectContext context) => OnApplyRedirect(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="app">The <see cref="IApplicationBuilder"/> passed to the configure method.</param>
|
||||||
/// <param name="options">The middleware configuration options.</param>
|
/// <param name="options">The middleware configuration options.</param>
|
||||||
/// <returns>The updated <see cref="IApplicationBuilder"/>.</returns>
|
/// <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>>(
|
return app.UseMiddleware<OAuthAuthenticationMiddleware<OAuthAuthenticationOptions>>(
|
||||||
new ConfigureOptions<OAuthAuthenticationOptions<IOAuthAuthenticationNotifications>>(options =>
|
new ConfigureOptions<OAuthAuthenticationOptions>(options =>
|
||||||
{
|
{
|
||||||
options.AuthenticationScheme = authenticationScheme;
|
options.AuthenticationScheme = authenticationScheme;
|
||||||
options.Caption = authenticationScheme;
|
options.Caption = authenticationScheme;
|
||||||
|
|
@ -30,10 +30,6 @@ namespace Microsoft.AspNet.Builder
|
||||||
{
|
{
|
||||||
configureOptions(options);
|
configureOptions(options);
|
||||||
}
|
}
|
||||||
if (options.Notifications == null)
|
|
||||||
{
|
|
||||||
options.Notifications = new OAuthAuthenticationNotifications();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
Name = authenticationScheme,
|
Name = authenticationScheme,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Authentication.DataHandler.Encoder;
|
using Microsoft.AspNet.Authentication.DataHandler.Encoder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -19,9 +20,7 @@ using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.OAuth
|
namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
public class OAuthAuthenticationHandler<TOptions, TNotifications> : AuthenticationHandler<TOptions>
|
public class OAuthAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions> where TOptions : OAuthAuthenticationOptions
|
||||||
where TOptions : OAuthAuthenticationOptions<TNotifications>
|
|
||||||
where TNotifications : IOAuthAuthenticationNotifications
|
|
||||||
{
|
{
|
||||||
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
|
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
|
||||||
|
|
||||||
|
|
@ -124,7 +123,33 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
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)
|
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>()
|
var tokenRequestParameters = new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
|
|
@ -151,20 +176,27 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
requestMessage.Content = requestContent;
|
requestMessage.Content = requestContent;
|
||||||
var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
|
var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
var oauthTokenResponse = await response.Content.ReadAsStringAsync();
|
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||||
|
|
||||||
var oauth2Token = JObject.Parse(oauthTokenResponse);
|
return new OAuthTokenResponse(payload);
|
||||||
return new TokenResponse(oauth2Token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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.
|
/// An ASP.NET middleware for authenticating users using OAuth services.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware are not disposable.")]
|
||||||
public class OAuthAuthenticationMiddleware<TOptions, TNotifications> : AuthenticationMiddleware<TOptions>
|
public class OAuthAuthenticationMiddleware<TOptions> : AuthenticationMiddleware<TOptions> where TOptions : OAuthAuthenticationOptions, new()
|
||||||
where TOptions : OAuthAuthenticationOptions<TNotifications>, new()
|
|
||||||
where TNotifications : IOAuthAuthenticationNotifications
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new <see cref="OAuthAuthenticationMiddleware"/>.
|
/// Initializes a new <see cref="OAuthAuthenticationMiddleware"/>.
|
||||||
|
|
@ -58,12 +56,12 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.AuthorizationEndpoint))
|
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))
|
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)
|
if (Options.StateDataFormat == null)
|
||||||
|
|
@ -82,10 +80,6 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
Options.SignInScheme = externalOptions.Options.SignInScheme;
|
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; }
|
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>
|
/// <returns>An <see cref="AuthenticationHandler"/> configured with the <see cref="OAuthAuthenticationOptions"/> supplied to the constructor.</returns>
|
||||||
protected override AuthenticationHandler<TOptions> CreateHandler()
|
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")]
|
[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 ??
|
HttpMessageHandler handler = options.BackchannelHttpHandler ??
|
||||||
#if DNX451
|
#if DNX451
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.OAuth
|
namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
|
|
@ -14,15 +16,6 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OAuthAuthenticationOptions : AuthenticationOptions
|
public class OAuthAuthenticationOptions : AuthenticationOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new <see cref="OAuthAuthenticationOptions"/>.
|
|
||||||
/// </summary>
|
|
||||||
public OAuthAuthenticationOptions()
|
|
||||||
{
|
|
||||||
Scope = new List<string>();
|
|
||||||
BackchannelTimeout = TimeSpan.FromSeconds(60);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the provider-assigned client id.
|
/// Gets or sets the provider-assigned client id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -46,7 +39,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the URI the middleware will access to obtain the user information.
|
/// 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
|
/// 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>
|
/// </summary>
|
||||||
public string UserInformationEndpoint { get; set; }
|
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>
|
/// validating the subject name and if the signing chain is a trusted party.</remarks>
|
||||||
public ICertificateValidator BackchannelCertificateValidator { get; set; }
|
public ICertificateValidator BackchannelCertificateValidator { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get or sets the text that the user can display on a sign in user interface.
|
/// Get or sets the text that the user can display on a sign in user interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -77,7 +71,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
/// <value>
|
/// <value>
|
||||||
/// The back channel timeout.
|
/// The back channel timeout.
|
||||||
/// </value>
|
/// </value>
|
||||||
public TimeSpan BackchannelTimeout { get; set; }
|
public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromSeconds(60);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HttpMessageHandler used to communicate with the auth provider.
|
/// The HttpMessageHandler used to communicate with the auth provider.
|
||||||
|
|
@ -86,10 +80,15 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HttpMessageHandler BackchannelHttpHandler { get; set; }
|
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>
|
/// <summary>
|
||||||
/// A list of permissions to request.
|
/// A list of permissions to request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<string> Scope { get; private set; }
|
public IList<string> Scope { get; } = new List<string>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The request path within the application's base path where the user-agent will be returned.
|
/// 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.
|
/// Gets or sets the type used to secure data handled by the middleware.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
|
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
|
namespace Microsoft.AspNet.Authentication.OAuth
|
||||||
{
|
{
|
||||||
public class TokenResponse
|
public class OAuthTokenResponse
|
||||||
{
|
{
|
||||||
public TokenResponse(JObject response)
|
public OAuthTokenResponse(JObject response)
|
||||||
{
|
{
|
||||||
Response = response;
|
Response = response;
|
||||||
AccessToken = response.Value<string>("access_token");
|
AccessToken = response.Value<string>("access_token");
|
||||||
|
|
@ -82,24 +82,6 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
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
|
var cookieOptions = new CookieOptions
|
||||||
{
|
{
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
|
|
@ -108,9 +90,23 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
|
|
||||||
Response.Cookies.Delete(StateCookie, cookieOptions);
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -118,6 +114,25 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
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)
|
protected override async Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||||
{
|
{
|
||||||
var properties = new AuthenticationProperties(context.Properties);
|
var properties = new AuthenticationProperties(context.Properties);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Authentication.Twitter.Messages;
|
using Microsoft.AspNet.Authentication.Twitter.Messages;
|
||||||
|
|
||||||
|
|
@ -102,8 +103,16 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
||||||
public ISecureDataFormat<RequestToken> StateDataFormat { get; set; }
|
public ISecureDataFormat<RequestToken> StateDataFormat { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public ITwitterAuthenticationNotifications Notifications { get; set; }
|
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;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Authentication.OAuth;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
|
|
@ -33,7 +34,7 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
||||||
{
|
{
|
||||||
options.AppId = "Test App Id";
|
options.AppId = "Test App Id";
|
||||||
options.AppSecret = "Test App Secret";
|
options.AppSecret = "Test App Secret";
|
||||||
options.Notifications = new FacebookAuthenticationNotifications
|
options.Notifications = new OAuthAuthenticationNotifications
|
||||||
{
|
{
|
||||||
OnApplyRedirect = context =>
|
OnApplyRedirect = context =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Authentication.DataHandler;
|
using Microsoft.AspNet.Authentication.DataHandler;
|
||||||
|
using Microsoft.AspNet.Authentication.OAuth;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.DataProtection;
|
using Microsoft.AspNet.DataProtection;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -198,7 +199,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
||||||
{
|
{
|
||||||
options.ClientId = "Test Id";
|
options.ClientId = "Test Id";
|
||||||
options.ClientSecret = "Test Secret";
|
options.ClientSecret = "Test Secret";
|
||||||
options.Notifications = new GoogleAuthenticationNotifications
|
options.Notifications = new OAuthAuthenticationNotifications
|
||||||
{
|
{
|
||||||
OnApplyRedirect = context =>
|
OnApplyRedirect = context =>
|
||||||
{
|
{
|
||||||
|
|
@ -414,14 +415,14 @@ namespace Microsoft.AspNet.Authentication.Google
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
options.Notifications = new GoogleAuthenticationNotifications()
|
options.Notifications = new OAuthAuthenticationNotifications
|
||||||
{
|
{
|
||||||
OnAuthenticated = context =>
|
OnAuthenticated = context =>
|
||||||
{
|
{
|
||||||
var refreshToken = context.RefreshToken;
|
var refreshToken = context.RefreshToken;
|
||||||
context.Principal.AddIdentity(new ClaimsIdentity(new Claim[] { new Claim("RefreshToken", refreshToken, ClaimValueTypes.String, "Google") }, "Google"));
|
context.Principal.AddIdentity(new ClaimsIdentity(new Claim[] { new Claim("RefreshToken", refreshToken, ClaimValueTypes.String, "Google") }, "Google"));
|
||||||
return Task.FromResult<object>(null);
|
return Task.FromResult<object>(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
var properties = new AuthenticationProperties();
|
var properties = new AuthenticationProperties();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Authentication.DataHandler;
|
using Microsoft.AspNet.Authentication.DataHandler;
|
||||||
using Microsoft.AspNet.Authentication.MicrosoftAccount;
|
using Microsoft.AspNet.Authentication.MicrosoftAccount;
|
||||||
|
using Microsoft.AspNet.Authentication.OAuth;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.DataProtection;
|
using Microsoft.AspNet.DataProtection;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
||||||
{
|
{
|
||||||
options.ClientId = "Test Client Id";
|
options.ClientId = "Test Client Id";
|
||||||
options.ClientSecret = "Test Client Secret";
|
options.ClientSecret = "Test Client Secret";
|
||||||
options.Notifications = new MicrosoftAccountAuthenticationNotifications
|
options.Notifications = new OAuthAuthenticationNotifications
|
||||||
{
|
{
|
||||||
OnApplyRedirect = context =>
|
OnApplyRedirect = context =>
|
||||||
{
|
{
|
||||||
|
|
@ -143,7 +144,7 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
options.Notifications = new MicrosoftAccountAuthenticationNotifications
|
options.Notifications = new OAuthAuthenticationNotifications
|
||||||
{
|
{
|
||||||
OnAuthenticated = context =>
|
OnAuthenticated = context =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue