Refactor Events + Add IAuthenticationBuilder

This commit is contained in:
Hao Kung 2017-06-29 16:27:03 -07:00
parent e1cd8c9bc4
commit ff9f145a8e
95 changed files with 1746 additions and 1493 deletions

View File

@ -18,9 +18,7 @@ namespace CookieSample
{ {
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}); }).AddCookie();
services.AddCookieAuthentication();
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -19,9 +19,7 @@ namespace CookieSessionSample
{ {
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}); }).AddCookie(o => o.SessionStore = new MemoryCacheTicketStore());
services.AddCookieAuthentication(o => o.SessionStore = new MemoryCacheTicketStore());
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -48,9 +48,7 @@ namespace JwtBearerSample
{ {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}); }).AddJwtBearer(o =>
services.AddJwtBearerAuthentication(o =>
{ {
// You also need to update /wwwroot/app/scripts/app.js // You also need to update /wwwroot/app/scripts/app.js
o.Authority = Configuration["jwt:authority"]; o.Authority = Configuration["jwt:authority"];
@ -59,7 +57,7 @@ namespace JwtBearerSample
{ {
OnAuthenticationFailed = c => OnAuthenticationFailed = c =>
{ {
c.HandleResponse(); c.NoResult();
c.Response.StatusCode = 500; c.Response.StatusCode = 500;
c.Response.ContentType = "text/plain"; c.Response.ContentType = "text/plain";

View File

@ -48,11 +48,9 @@ namespace OpenIdConnect.AzureAdSample
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}); })
.AddCookie()
services.AddCookieAuthentication(); .AddOpenIdConnect(o =>
services.AddOpenIdConnectAuthentication(o =>
{ {
o.ClientId = ClientId; o.ClientId = ClientId;
o.ClientSecret = ClientSecret; // for code flow o.ClientSecret = ClientSecret; // for code flow

View File

@ -45,11 +45,9 @@ namespace OpenIdConnectSample
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}); })
.AddCookie()
services.AddCookieAuthentication(); .AddOpenIdConnect(o =>
services.AddOpenIdConnectAuthentication(o =>
{ {
o.ClientId = Configuration["oidc:clientid"]; o.ClientId = Configuration["oidc:clientid"];
o.ClientSecret = Configuration["oidc:clientsecret"]; // for code flow o.ClientSecret = Configuration["oidc:clientsecret"]; // for code flow

View File

@ -55,13 +55,11 @@ namespace SocialSample
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}); })
.AddCookie(o => o.LoginPath = new PathString("/login"))
services.AddCookieAuthentication(o => o.LoginPath = new PathString("/login")); // You must first create an app with Facebook and add its ID and Secret to your user-secrets.
// https://developers.facebook.com/apps/
// You must first create an app with Facebook and add its ID and Secret to your user-secrets. .AddFacebook(o =>
// https://developers.facebook.com/apps/
services.AddFacebookAuthentication(o =>
{ {
o.AppId = Configuration["facebook:appid"]; o.AppId = Configuration["facebook:appid"];
o.AppSecret = Configuration["facebook:appsecret"]; o.AppSecret = Configuration["facebook:appsecret"];
@ -69,11 +67,10 @@ namespace SocialSample
o.Fields.Add("name"); o.Fields.Add("name");
o.Fields.Add("email"); o.Fields.Add("email");
o.SaveTokens = true; o.SaveTokens = true;
}); })
// You must first create an app with Google and add its ID and Secret to your user-secrets.
// You must first create an app with Google and add its ID and Secret to your user-secrets. // https://console.developers.google.com/project
// https://console.developers.google.com/project .AddOAuth("Google-AccessToken", o =>
services.AddOAuthAuthentication("Google-AccessToken", o =>
{ {
o.ClientId = Configuration["google:clientid"]; o.ClientId = Configuration["google:clientid"];
o.ClientSecret = Configuration["google:clientsecret"]; o.ClientSecret = Configuration["google:clientsecret"];
@ -84,11 +81,10 @@ namespace SocialSample
o.Scope.Add("profile"); o.Scope.Add("profile");
o.Scope.Add("email"); o.Scope.Add("email");
o.SaveTokens = true; o.SaveTokens = true;
}); })
// You must first create an app with Google and add its ID and Secret to your user-secrets.
// You must first create an app with Google and add its ID and Secret to your user-secrets. // https://console.developers.google.com/project
// https://console.developers.google.com/project .AddGoogle(o =>
services.AddGoogleAuthentication(o =>
{ {
o.ClientId = Configuration["google:clientid"]; o.ClientId = Configuration["google:clientid"];
o.ClientSecret = Configuration["google:clientsecret"]; o.ClientSecret = Configuration["google:clientsecret"];
@ -104,11 +100,10 @@ namespace SocialSample
}; };
o.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url"); o.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url");
o.ClaimActions.Remove(ClaimTypes.GivenName); o.ClaimActions.Remove(ClaimTypes.GivenName);
}); })
// You must first create an app with Twitter and add its key and Secret to your user-secrets.
// You must first create an app with Twitter and add its key and Secret to your user-secrets. // https://apps.twitter.com/
// https://apps.twitter.com/ .AddTwitter(o =>
services.AddTwitterAuthentication(o =>
{ {
o.ConsumerKey = Configuration["twitter:consumerkey"]; o.ConsumerKey = Configuration["twitter:consumerkey"];
o.ConsumerSecret = Configuration["twitter:consumersecret"]; o.ConsumerSecret = Configuration["twitter:consumersecret"];
@ -126,15 +121,14 @@ namespace SocialSample
return Task.FromResult(0); return Task.FromResult(0);
} }
}; };
}); })
/* Azure AD app model v2 has restrictions that prevent the use of plain HTTP for redirect URLs.
/* Azure AD app model v2 has restrictions that prevent the use of plain HTTP for redirect URLs. Therefore, to authenticate through microsoft accounts, tryout the sample using the following URL:
Therefore, to authenticate through microsoft accounts, tryout the sample using the following URL: https://localhost:44318/
https://localhost:44318/ */
*/ // You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets.
// You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets. // https://apps.dev.microsoft.com/
// https://apps.dev.microsoft.com/ .AddOAuth("Microsoft-AccessToken", o =>
services.AddOAuthAuthentication("Microsoft-AccessToken", o =>
{ {
o.ClientId = Configuration["microsoftaccount:clientid"]; o.ClientId = Configuration["microsoftaccount:clientid"];
o.ClientSecret = Configuration["microsoftaccount:clientsecret"]; o.ClientSecret = Configuration["microsoftaccount:clientsecret"];
@ -143,20 +137,18 @@ namespace SocialSample
o.TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint; o.TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint;
o.Scope.Add("https://graph.microsoft.com/user.read"); o.Scope.Add("https://graph.microsoft.com/user.read");
o.SaveTokens = true; o.SaveTokens = true;
}); })
// You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets.
// You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets. // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-app-registration/
// https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-app-registration/ .AddMicrosoftAccount(o =>
services.AddMicrosoftAccountAuthentication(o =>
{ {
o.ClientId = Configuration["microsoftaccount:clientid"]; o.ClientId = Configuration["microsoftaccount:clientid"];
o.ClientSecret = Configuration["microsoftaccount:clientsecret"]; o.ClientSecret = Configuration["microsoftaccount:clientsecret"];
o.SaveTokens = true; o.SaveTokens = true;
}); })
// You must first create an app with GitHub and add its ID and Secret to your user-secrets.
// You must first create an app with GitHub and add its ID and Secret to your user-secrets. // https://github.com/settings/applications/
// https://github.com/settings/applications/ .AddOAuth("GitHub-AccessToken", o =>
services.AddOAuthAuthentication("GitHub-AccessToken", o =>
{ {
o.ClientId = Configuration["github-token:clientid"]; o.ClientId = Configuration["github-token:clientid"];
o.ClientSecret = Configuration["github-token:clientsecret"]; o.ClientSecret = Configuration["github-token:clientsecret"];
@ -164,11 +156,10 @@ namespace SocialSample
o.AuthorizationEndpoint = "https://github.com/login/oauth/authorize"; o.AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
o.TokenEndpoint = "https://github.com/login/oauth/access_token"; o.TokenEndpoint = "https://github.com/login/oauth/access_token";
o.SaveTokens = true; o.SaveTokens = true;
}); })
// You must first create an app with GitHub and add its ID and Secret to your user-secrets.
// You must first create an app with GitHub and add its ID and Secret to your user-secrets. // https://github.com/settings/applications/
// https://github.com/settings/applications/ .AddOAuth("GitHub", o =>
services.AddOAuthAuthentication("GitHub", o =>
{ {
o.ClientId = Configuration["github:clientid"]; o.ClientId = Configuration["github:clientid"];
o.ClientSecret = Configuration["github:clientsecret"]; o.ClientSecret = Configuration["github:clientsecret"];

View File

@ -14,7 +14,10 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Authentication.Cookies namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
public class CookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions> public class CookieAuthenticationHandler :
AuthenticationHandler<CookieAuthenticationOptions>,
IAuthenticationSignInHandler,
IAuthenticationSignOutHandler
{ {
private const string HeaderValueNoCache = "no-cache"; private const string HeaderValueNoCache = "no-cache";
private const string HeaderValueMinusOne = "-1"; private const string HeaderValueMinusOne = "-1";
@ -104,7 +107,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName); var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
if (string.IsNullOrEmpty(cookie)) if (string.IsNullOrEmpty(cookie))
{ {
return AuthenticateResult.None(); return AuthenticateResult.NoResult();
} }
var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding()); var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding());
@ -155,7 +158,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
return result; return result;
} }
var context = new CookieValidatePrincipalContext(Context, Scheme, result.Ticket, Options); var context = new CookieValidatePrincipalContext(Context, Scheme, Options, result.Ticket);
await Events.ValidatePrincipal(context); await Events.ValidatePrincipal(context);
if (context.Principal == null) if (context.Principal == null)
@ -244,8 +247,15 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
} }
} }
protected override async Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) public async virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{ {
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
properties = properties ?? new AuthenticationProperties();
_signInCalled = true; _signInCalled = true;
// Process the request cookie to initialize members like _sessionKey. // Process the request cookie to initialize members like _sessionKey.
@ -284,7 +294,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime(); signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime();
} }
var ticket = new AuthenticationTicket(signInContext.Principal, signInContext.Properties, signInContext.AuthenticationScheme); var ticket = new AuthenticationTicket(signInContext.Principal, signInContext.Properties, signInContext.Scheme.Name);
if (Options.SessionStore != null) if (Options.SessionStore != null)
{ {
if (_sessionKey != null) if (_sessionKey != null)
@ -310,20 +321,23 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
var signedInContext = new CookieSignedInContext( var signedInContext = new CookieSignedInContext(
Context, Context,
Scheme, Scheme,
Options,
Scheme.Name,
signInContext.Principal, signInContext.Principal,
signInContext.Properties); signInContext.Properties,
Options);
await Events.SignedIn(signedInContext); await Events.SignedIn(signedInContext);
// Only redirect on the login path // Only redirect on the login path
var shouldRedirect = Options.LoginPath.HasValue && OriginalPath == Options.LoginPath; var shouldRedirect = Options.LoginPath.HasValue && OriginalPath == Options.LoginPath;
await ApplyHeaders(shouldRedirect, signedInContext.Properties); await ApplyHeaders(shouldRedirect, signedInContext.Properties);
Logger.SignedIn(Scheme.Name);
} }
protected override async Task HandleSignOutAsync(AuthenticationProperties properties) public async virtual Task SignOutAsync(AuthenticationProperties properties)
{ {
properties = properties ?? new AuthenticationProperties();
_signOutCalled = true; _signOutCalled = true;
// Process the request cookie to initialize members like _sessionKey. // Process the request cookie to initialize members like _sessionKey.
@ -351,6 +365,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
// Only redirect on the logout path // Only redirect on the logout path
var shouldRedirect = Options.LogoutPath.HasValue && OriginalPath == Options.LogoutPath; var shouldRedirect = Options.LogoutPath.HasValue && OriginalPath == Options.LogoutPath;
await ApplyHeaders(shouldRedirect, context.Properties); await ApplyHeaders(shouldRedirect, context.Properties);
Logger.SignedOut(Scheme.Name);
} }
private async Task ApplyHeaders(bool shouldRedirectToReturnUrl, AuthenticationProperties properties) private async Task ApplyHeaders(bool shouldRedirectToReturnUrl, AuthenticationProperties properties)
@ -380,7 +396,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
if (redirectUri != null) if (redirectUri != null)
{ {
await Events.RedirectToReturnUrl( await Events.RedirectToReturnUrl(
new CookieRedirectContext(Context, Scheme, Options, redirectUri, properties)); new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, redirectUri));
} }
} }
} }
@ -406,7 +422,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
returnUrl = OriginalPathBase + Request.Path + Request.QueryString; returnUrl = OriginalPathBase + Request.Path + Request.QueryString;
} }
var accessDeniedUri = Options.AccessDeniedPath + QueryString.Create(Options.ReturnUrlParameter, returnUrl); var accessDeniedUri = Options.AccessDeniedPath + QueryString.Create(Options.ReturnUrlParameter, returnUrl);
var redirectContext = new CookieRedirectContext(Context, Scheme, Options, BuildRedirectUri(accessDeniedUri), properties); var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(accessDeniedUri));
await Events.RedirectToAccessDenied(redirectContext); await Events.RedirectToAccessDenied(redirectContext);
} }
@ -419,7 +435,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
} }
var loginUri = Options.LoginPath + QueryString.Create(Options.ReturnUrlParameter, redirectUri); var loginUri = Options.LoginPath + QueryString.Create(Options.ReturnUrlParameter, redirectUri);
var redirectContext = new CookieRedirectContext(Context, Scheme, Options, BuildRedirectUri(loginUri), properties); var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(loginUri));
await Events.RedirectToLogin(redirectContext); await Events.RedirectToLogin(redirectContext);
} }

View File

@ -2,16 +2,32 @@
// 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 Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class CookieExtensions public static class CookieExtensions
{ {
public static AuthenticationBuilder AddCookie(this AuthenticationBuilder builder)
=> builder.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
public static AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, string authenticationScheme)
=> builder.AddCookie(authenticationScheme, configureOptions: null);
public static AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, Action<CookieAuthenticationOptions> configureOptions)
=> builder.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, string authenticationScheme, Action<CookieAuthenticationOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptions>());
return builder.AddScheme<CookieAuthenticationOptions, CookieAuthenticationHandler>(authenticationScheme, configureOptions);
}
// REMOVE below once callers have been updated
public static IServiceCollection AddCookieAuthentication(this IServiceCollection services) => services.AddCookieAuthentication(CookieAuthenticationDefaults.AuthenticationScheme); public static IServiceCollection AddCookieAuthentication(this IServiceCollection services) => services.AddCookieAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
public static IServiceCollection AddCookieAuthentication(this IServiceCollection services, string authenticationScheme) => services.AddCookieAuthentication(authenticationScheme, configureOptions: null); public static IServiceCollection AddCookieAuthentication(this IServiceCollection services, string authenticationScheme) => services.AddCookieAuthentication(authenticationScheme, configureOptions: null);

View File

@ -1,30 +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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Cookies
{
public class BaseCookieContext : BaseAuthenticationContext
{
public BaseCookieContext(
HttpContext context,
AuthenticationScheme scheme,
CookieAuthenticationOptions options,
AuthenticationProperties properties)
: base(context, scheme.Name, properties)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
Options = options;
}
public CookieAuthenticationOptions Options { get; }
public AuthenticationScheme Scheme { get; }
}
}

View File

@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <summary> /// <summary>
/// A delegate assigned to this property will be invoked when the related method is called. /// A delegate assigned to this property will be invoked when the related method is called.
/// </summary> /// </summary>
public Func<CookieRedirectContext, Task> OnRedirectToLogin { get; set; } = context => public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <summary> /// <summary>
/// A delegate assigned to this property will be invoked when the related method is called. /// A delegate assigned to this property will be invoked when the related method is called.
/// </summary> /// </summary>
public Func<CookieRedirectContext, Task> OnRedirectToAccessDenied { get; set; } = context => public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToAccessDenied { get; set; } = context =>
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <summary> /// <summary>
/// A delegate assigned to this property will be invoked when the related method is called. /// A delegate assigned to this property will be invoked when the related method is called.
/// </summary> /// </summary>
public Func<CookieRedirectContext, Task> OnRedirectToLogout { get; set; } = context => public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogout { get; set; } = context =>
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <summary> /// <summary>
/// A delegate assigned to this property will be invoked when the related method is called. /// A delegate assigned to this property will be invoked when the related method is called.
/// </summary> /// </summary>
public Func<CookieRedirectContext, Task> OnRedirectToReturnUrl { get; set; } = context => public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToReturnUrl { get; set; } = context =>
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
@ -135,24 +135,24 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// Implements the interface method by invoking the related delegate method. /// Implements the interface method by invoking the related delegate method.
/// </summary> /// </summary>
/// <param name="context">Contains information about the event</param> /// <param name="context">Contains information about the event</param>
public virtual Task RedirectToLogout(CookieRedirectContext context) => OnRedirectToLogout(context); public virtual Task RedirectToLogout(RedirectContext<CookieAuthenticationOptions> context) => OnRedirectToLogout(context);
/// <summary> /// <summary>
/// Implements the interface method by invoking the related delegate method. /// Implements the interface method by invoking the related delegate method.
/// </summary> /// </summary>
/// <param name="context">Contains information about the event</param> /// <param name="context">Contains information about the event</param>
public virtual Task RedirectToLogin(CookieRedirectContext context) => OnRedirectToLogin(context); public virtual Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context) => OnRedirectToLogin(context);
/// <summary> /// <summary>
/// Implements the interface method by invoking the related delegate method. /// Implements the interface method by invoking the related delegate method.
/// </summary> /// </summary>
/// <param name="context">Contains information about the event</param> /// <param name="context">Contains information about the event</param>
public virtual Task RedirectToReturnUrl(CookieRedirectContext context) => OnRedirectToReturnUrl(context); public virtual Task RedirectToReturnUrl(RedirectContext<CookieAuthenticationOptions> context) => OnRedirectToReturnUrl(context);
/// <summary> /// <summary>
/// Implements the interface method by invoking the related delegate method. /// Implements the interface method by invoking the related delegate method.
/// </summary> /// </summary>
/// <param name="context">Contains information about the event</param> /// <param name="context">Contains information about the event</param>
public virtual Task RedirectToAccessDenied(CookieRedirectContext context) => OnRedirectToAccessDenied(context); public virtual Task RedirectToAccessDenied(RedirectContext<CookieAuthenticationOptions> context) => OnRedirectToAccessDenied(context);
} }
} }

View File

@ -9,32 +9,25 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <summary> /// <summary>
/// Context object passed to the ICookieAuthenticationEvents method SignedIn. /// Context object passed to the ICookieAuthenticationEvents method SignedIn.
/// </summary> /// </summary>
public class CookieSignedInContext : BaseCookieContext public class CookieSignedInContext : PrincipalContext<CookieAuthenticationOptions>
{ {
/// <summary> /// <summary>
/// Creates a new instance of the context object. /// Creates a new instance of the context object.
/// </summary> /// </summary>
/// <param name="context">The HTTP request context</param> /// <param name="context">The HTTP request context</param>
/// <param name="scheme">The scheme data</param> /// <param name="scheme">The scheme data</param>
/// <param name="options">The handler options</param>
/// <param name="authenticationScheme">Initializes AuthenticationScheme property</param>
/// <param name="principal">Initializes Principal property</param> /// <param name="principal">Initializes Principal property</param>
/// <param name="properties">Initializes Properties property</param> /// <param name="properties">Initializes Properties property</param>
/// <param name="options">The handler options</param>
public CookieSignedInContext( public CookieSignedInContext(
HttpContext context, HttpContext context,
AuthenticationScheme scheme, AuthenticationScheme scheme,
CookieAuthenticationOptions options,
string authenticationScheme,
ClaimsPrincipal principal, ClaimsPrincipal principal,
AuthenticationProperties properties) AuthenticationProperties properties,
CookieAuthenticationOptions options)
: base(context, scheme, options, properties) : base(context, scheme, options, properties)
{ {
Principal = principal; Principal = principal;
} }
/// <summary>
/// Contains the claims that were converted into the outgoing cookie.
/// </summary>
public ClaimsPrincipal Principal { get; }
} }
} }

View File

@ -7,9 +7,9 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Cookies namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
/// <summary> /// <summary>
/// Context object passed to the ICookieAuthenticationEvents method SigningIn. /// Context object passed to the <see cref="CookieAuthenticationEvents.SigningIn(CookieSigningInContext)"/>.
/// </summary> /// </summary>
public class CookieSigningInContext : BaseCookieContext public class CookieSigningInContext : PrincipalContext<CookieAuthenticationOptions>
{ {
/// <summary> /// <summary>
/// Creates a new instance of the context object. /// Creates a new instance of the context object.
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <param name="scheme">The scheme data</param> /// <param name="scheme">The scheme data</param>
/// <param name="options">The handler options</param> /// <param name="options">The handler options</param>
/// <param name="principal">Initializes Principal property</param> /// <param name="principal">Initializes Principal property</param>
/// <param name="properties">Initializes Extra property</param> /// <param name="properties">The authentication properties.</param>
/// <param name="cookieOptions">Initializes options for the authentication cookie.</param> /// <param name="cookieOptions">Initializes options for the authentication cookie.</param>
public CookieSigningInContext( public CookieSigningInContext(
HttpContext context, HttpContext context,
@ -29,16 +29,10 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
CookieOptions cookieOptions) CookieOptions cookieOptions)
: base(context, scheme, options, properties) : base(context, scheme, options, properties)
{ {
Principal = principal;
CookieOptions = cookieOptions; CookieOptions = cookieOptions;
Principal = principal;
} }
/// <summary>
/// Contains the claims about to be converted into the outgoing cookie.
/// May be replaced or altered during the SigningIn call.
/// </summary>
public ClaimsPrincipal Principal { get; set; }
/// <summary> /// <summary>
/// The options for creating the outgoing cookie. /// The options for creating the outgoing cookie.
/// May be replace or altered during the SigningIn call. /// May be replace or altered during the SigningIn call.

View File

@ -6,9 +6,9 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Cookies namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
/// <summary> /// <summary>
/// Context object passed to the ICookieAuthenticationEvents method SigningOut /// Context object passed to the <see cref="CookieAuthenticationEvents.SigningOut(CookieSigningOutContext)"/>
/// </summary> /// </summary>
public class CookieSigningOutContext : BaseCookieContext public class CookieSigningOutContext : PropertiesContext<CookieAuthenticationOptions>
{ {
/// <summary> /// <summary>
/// ///
@ -25,9 +25,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
AuthenticationProperties properties, AuthenticationProperties properties,
CookieOptions cookieOptions) CookieOptions cookieOptions)
: base(context, scheme, options, properties) : base(context, scheme, options, properties)
{ => CookieOptions = cookieOptions;
CookieOptions = cookieOptions;
}
/// <summary> /// <summary>
/// The options for creating the outgoing cookie. /// The options for creating the outgoing cookie.

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <summary> /// <summary>
/// Context object passed to the CookieAuthenticationEvents ValidatePrincipal method. /// Context object passed to the CookieAuthenticationEvents ValidatePrincipal method.
/// </summary> /// </summary>
public class CookieValidatePrincipalContext : BaseCookieContext public class CookieValidatePrincipalContext : PrincipalContext<CookieAuthenticationOptions>
{ {
/// <summary> /// <summary>
/// Creates a new instance of the context object. /// Creates a new instance of the context object.
@ -19,33 +19,17 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <param name="scheme"></param> /// <param name="scheme"></param>
/// <param name="ticket">Contains the initial values for identity and extra data</param> /// <param name="ticket">Contains the initial values for identity and extra data</param>
/// <param name="options"></param> /// <param name="options"></param>
public CookieValidatePrincipalContext(HttpContext context, AuthenticationScheme scheme, AuthenticationTicket ticket, CookieAuthenticationOptions options) public CookieValidatePrincipalContext(HttpContext context, AuthenticationScheme scheme, CookieAuthenticationOptions options, AuthenticationTicket ticket)
: base(context, scheme, options, ticket?.Properties) : base(context, scheme, options, ticket?.Properties)
{ {
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (ticket == null) if (ticket == null)
{ {
throw new ArgumentNullException(nameof(ticket)); throw new ArgumentNullException(nameof(ticket));
} }
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
Principal = ticket.Principal; Principal = ticket.Principal;
} }
/// <summary>
/// Contains the claims principal arriving with the request. May be altered to change the
/// details of the authenticated user.
/// </summary>
public ClaimsPrincipal Principal { get; private set; }
/// <summary> /// <summary>
/// If true, the cookie will be renewed /// If true, the cookie will be renewed
/// </summary> /// </summary>
@ -56,18 +40,12 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// Principal property, which determines the identity of the authenticated request. /// Principal property, which determines the identity of the authenticated request.
/// </summary> /// </summary>
/// <param name="principal">The <see cref="ClaimsPrincipal"/> used as the replacement</param> /// <param name="principal">The <see cref="ClaimsPrincipal"/> used as the replacement</param>
public void ReplacePrincipal(ClaimsPrincipal principal) public void ReplacePrincipal(ClaimsPrincipal principal) => Principal = principal;
{
Principal = principal;
}
/// <summary> /// <summary>
/// Called to reject the incoming principal. This may be done if the application has determined the /// Called to reject the incoming principal. This may be done if the application has determined the
/// account is no longer active, and the request should be treated as if it was anonymous. /// account is no longer active, and the request should be treated as if it was anonymous.
/// </summary> /// </summary>
public void RejectPrincipal() public void RejectPrincipal() => Principal = null;
{
Principal = null;
}
} }
} }

View File

@ -0,0 +1,35 @@
// 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;
namespace Microsoft.Extensions.Logging
{
internal static class LoggingExtensions
{
private static Action<ILogger, string, Exception> _authSchemeSignedIn;
private static Action<ILogger, string, Exception> _authSchemeSignedOut;
static LoggingExtensions()
{
_authSchemeSignedIn = LoggerMessage.Define<string>(
eventId: 10,
logLevel: LogLevel.Information,
formatString: "AuthenticationScheme: {AuthenticationScheme} signed in.");
_authSchemeSignedOut = LoggerMessage.Define<string>(
eventId: 11,
logLevel: LogLevel.Information,
formatString: "AuthenticationScheme: {AuthenticationScheme} signed out.");
}
public static void SignedIn(this ILogger logger, string authenticationScheme)
{
_authSchemeSignedIn(logger, authenticationScheme, null);
}
public static void SignedOut(this ILogger logger, string authenticationScheme)
{
_authSchemeSignedOut(logger, authenticationScheme, null);
}
}
}

View File

@ -55,6 +55,5 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
options.AccessDeniedPath = CookieAuthenticationDefaults.AccessDeniedPath; options.AccessDeniedPath = CookieAuthenticationDefaults.AccessDeniedPath;
} }
} }
} }
} }

View File

@ -19,69 +19,29 @@
"TypeId": "public interface Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "TypeId": "public interface Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents",
"Kind": "Removal" "Kind": "Removal"
}, },
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Authentication.AuthenticationTicket ticket, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties, Microsoft.AspNetCore.Http.CookieOptions cookieOptions)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSignedInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options, System.String authenticationScheme, System.Security.Claims.ClaimsPrincipal principal, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSignedInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options, System.String authenticationScheme, System.Security.Claims.ClaimsPrincipal principal, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties, Microsoft.AspNetCore.Http.CookieOptions cookieOptions)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options, System.String redirectUri, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{ {
"TypeId": "public static class Microsoft.AspNetCore.Builder.CookieAppBuilderExtensions", "TypeId": "public static class Microsoft.AspNetCore.Builder.CookieAppBuilderExtensions",
"MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseCookieAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options)", "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseCookieAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options)",
"Kind": "Removal" "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSignedInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext",
"Kind": "Removal"
} }
] ]

View File

@ -2,12 +2,24 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Facebook; using Microsoft.AspNetCore.Authentication.Facebook;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class FacebookAuthenticationOptionsExtensions public static class FacebookAuthenticationOptionsExtensions
{ {
public static AuthenticationBuilder AddFacebook(this AuthenticationBuilder builder)
=> builder.AddFacebook(FacebookDefaults.AuthenticationScheme, _ => { });
public static AuthenticationBuilder AddFacebook(this AuthenticationBuilder builder, Action<FacebookOptions> configureOptions)
=> builder.AddFacebook(FacebookDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddFacebook(this AuthenticationBuilder builder, string authenticationScheme, Action<FacebookOptions> configureOptions)
=> builder.AddOAuth<FacebookOptions, FacebookHandler>(authenticationScheme, configureOptions);
// REMOVE below once callers have been updated
public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services) public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services)
=> services.AddFacebookAuthentication(FacebookDefaults.AuthenticationScheme, _ => { }); => services.AddFacebookAuthentication(FacebookDefaults.AuthenticationScheme, _ => { });

View File

@ -42,13 +42,13 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), properties, Scheme.Name); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);
var context = new OAuthCreatingTicketContext(ticket, Context, Scheme, Options, Backchannel, tokens, payload);
context.RunClaimActions(); context.RunClaimActions();
await Events.CreatingTicket(context); await Events.CreatingTicket(context);
return context.Ticket; return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
} }
private string GenerateAppSecretProof(string accessToken) private string GenerateAppSecretProof(string accessToken)

View File

@ -2,12 +2,25 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authentication.Google;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class GoogleExtensions public static class GoogleExtensions
{ {
public static AuthenticationBuilder AddGoogle(this AuthenticationBuilder builder)
=> builder.AddGoogle(GoogleDefaults.AuthenticationScheme, _ => { });
public static AuthenticationBuilder AddGoogle(this AuthenticationBuilder builder, Action<GoogleOptions> configureOptions)
=> builder.AddGoogle(GoogleDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddGoogle(this AuthenticationBuilder builder, string authenticationScheme, Action<GoogleOptions> configureOptions)
=> builder.AddOAuth<GoogleOptions, GoogleHandler>(authenticationScheme, configureOptions);
// REMOVE below once callers have been updated
public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services) public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services)
=> services.AddGoogleAuthentication(GoogleDefaults.AuthenticationScheme, _ => { }); => services.AddGoogleAuthentication(GoogleDefaults.AuthenticationScheme, _ => { });

View File

@ -39,14 +39,11 @@ namespace Microsoft.AspNetCore.Authentication.Google
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var principal = new ClaimsPrincipal(identity); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);
var ticket = new AuthenticationTicket(principal, properties, Scheme.Name);
var context = new OAuthCreatingTicketContext(ticket, Context, Scheme, Options, Backchannel, tokens, payload);
context.RunClaimActions(); context.RunClaimActions();
await Events.CreatingTicket(context); await Events.CreatingTicket(context);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
return context.Ticket;
} }
// TODO: Abstract this properties override pattern into the base class? // TODO: Abstract this properties override pattern into the base class?

View File

@ -6,12 +6,13 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.JwtBearer namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
public class AuthenticationFailedContext : BaseJwtBearerContext public class AuthenticationFailedContext : ResultContext<JwtBearerOptions>
{ {
public AuthenticationFailedContext(HttpContext context, AuthenticationScheme scheme, JwtBearerOptions options) public AuthenticationFailedContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
} JwtBearerOptions options)
: base(context, scheme, options) { }
public Exception Exception { get; set; } public Exception Exception { get; set; }
} }

View File

@ -1,32 +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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.JwtBearer
{
public class BaseJwtBearerContext : BaseControlContext
{
public BaseJwtBearerContext(HttpContext context, AuthenticationScheme scheme, JwtBearerOptions options)
: base(context)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme));
}
Options = options;
Scheme = scheme;
}
public JwtBearerOptions Options { get; }
public AuthenticationScheme Scheme { get; }
}
}

View File

@ -6,15 +6,14 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.JwtBearer namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
public class JwtBearerChallengeContext : BaseJwtBearerContext public class JwtBearerChallengeContext : PropertiesContext<JwtBearerOptions>
{ {
public JwtBearerChallengeContext(HttpContext context, AuthenticationScheme scheme, JwtBearerOptions options, AuthenticationProperties properties) public JwtBearerChallengeContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
Properties = properties; JwtBearerOptions options,
} AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
public AuthenticationProperties Properties { get; }
/// <summary> /// <summary>
/// Any failures encountered during the authentication process. /// Any failures encountered during the authentication process.
@ -40,5 +39,15 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
/// WWW-Authenticate header. This property is always null unless explicitly set. /// WWW-Authenticate header. This property is always null unless explicitly set.
/// </summary> /// </summary>
public string ErrorUri { get; set; } public string ErrorUri { get; set; }
/// <summary>
/// If true, will skip any default logic for this challenge.
/// </summary>
public bool Handled { get; private set; }
/// <summary>
/// Skips any default logic for this challenge.
/// </summary>
public void HandleResponse() => Handled = true;
} }
} }

View File

@ -5,12 +5,13 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.JwtBearer namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
public class MessageReceivedContext : BaseJwtBearerContext public class MessageReceivedContext : ResultContext<JwtBearerOptions>
{ {
public MessageReceivedContext(HttpContext context, AuthenticationScheme scheme, JwtBearerOptions options) public MessageReceivedContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
} JwtBearerOptions options)
: base(context, scheme, options) { }
/// <summary> /// <summary>
/// Bearer Token. This will give application an opportunity to retrieve token from an alternation location. /// Bearer Token. This will give application an opportunity to retrieve token from an alternation location.

View File

@ -6,12 +6,13 @@ using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Authentication.JwtBearer namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
public class TokenValidatedContext : BaseJwtBearerContext public class TokenValidatedContext : ResultContext<JwtBearerOptions>
{ {
public TokenValidatedContext(HttpContext context, AuthenticationScheme scheme, JwtBearerOptions options) public TokenValidatedContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
} JwtBearerOptions options)
: base(context, scheme, options) { }
public SecurityToken SecurityToken { get; set; } public SecurityToken SecurityToken { get; set; }
} }

View File

@ -2,6 +2,7 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -10,6 +11,20 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static class JwtBearerExtensions public static class JwtBearerExtensions
{ {
public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder)
=> builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ => { });
public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, Action<JwtBearerOptions> configureOptions)
=> builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, Action<JwtBearerOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>());
return builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, configureOptions);
}
// REMOVE once callers updated
public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services) public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services)
=> services.AddJwtBearerAuthentication(JwtBearerDefaults.AuthenticationScheme, _ => { }); => services.AddJwtBearerAuthentication(JwtBearerDefaults.AuthenticationScheme, _ => { });

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
@ -13,7 +12,6 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
@ -47,7 +45,6 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{ {
string token = null; string token = null;
AuthenticateResult result = null;
try try
{ {
// Give application opportunity to find from a different location, adjust, or reject token // Give application opportunity to find from a different location, adjust, or reject token
@ -55,9 +52,9 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
// event can set the token // event can set the token
await Events.MessageReceived(messageReceivedContext); await Events.MessageReceived(messageReceivedContext);
if (messageReceivedContext.IsProcessingComplete(out result)) if (messageReceivedContext.Result != null)
{ {
return result; return messageReceivedContext.Result;
} }
// If application retrieved token from somewhere else, use that. // If application retrieved token from somewhere else, use that.
@ -70,7 +67,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
// If no authorization header found, nothing to process further // If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization)) if (string.IsNullOrEmpty(authorization))
{ {
return AuthenticateResult.None(); return AuthenticateResult.NoResult();
} }
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
@ -81,7 +78,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
// If no token found, no further work possible // If no token found, no further work possible
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
return AuthenticateResult.None(); return AuthenticateResult.NoResult();
} }
} }
@ -138,29 +135,28 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Logger.TokenValidationSucceeded(); Logger.TokenValidationSucceeded();
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name);
var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options) var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options)
{ {
Ticket = ticket, Principal = principal,
SecurityToken = validatedToken, SecurityToken = validatedToken
}; };
await Events.TokenValidated(tokenValidatedContext); await Events.TokenValidated(tokenValidatedContext);
if (tokenValidatedContext.IsProcessingComplete(out result)) if (tokenValidatedContext.Result != null)
{ {
return result; return tokenValidatedContext.Result;
} }
ticket = tokenValidatedContext.Ticket;
if (Options.SaveToken) if (Options.SaveToken)
{ {
ticket.Properties.StoreTokens(new[] tokenValidatedContext.Properties.StoreTokens(new[]
{ {
new AuthenticationToken { Name = "access_token", Value = token } new AuthenticationToken { Name = "access_token", Value = token }
}); });
} }
return AuthenticateResult.Success(ticket); tokenValidatedContext.Success();
return tokenValidatedContext.Result;
} }
} }
@ -172,9 +168,9 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}; };
await Events.AuthenticationFailed(authenticationFailedContext); await Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.IsProcessingComplete(out result)) if (authenticationFailedContext.Result != null)
{ {
return result; return authenticationFailedContext.Result;
} }
return AuthenticateResult.Fail(authenticationFailedContext.Exception); return AuthenticateResult.Fail(authenticationFailedContext.Exception);
@ -192,9 +188,9 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}; };
await Events.AuthenticationFailed(authenticationFailedContext); await Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.IsProcessingComplete(out result)) if (authenticationFailedContext.Result != null)
{ {
return result; return authenticationFailedContext.Result;
} }
throw; throw;
@ -217,7 +213,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
await Events.Challenge(eventContext); await Events.Challenge(eventContext);
if (eventContext.IsProcessingComplete(out var ignored)) if (eventContext.Handled)
{ {
return; return;
} }
@ -329,15 +325,5 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
return string.Join("; ", messages); return string.Join("; ", messages);
} }
protected override Task HandleSignOutAsync(AuthenticationProperties properties)
{
throw new NotSupportedException();
}
protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
throw new NotSupportedException();
}
} }
} }

View File

@ -15,44 +15,29 @@
"TypeId": "public interface Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", "TypeId": "public interface Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents",
"Kind": "Removal" "Kind": "Removal"
}, },
{
"TypeId": "public static class Microsoft.AspNetCore.Builder.JwtBearerAppBuilderExtensions",
"MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseJwtBearerAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.JwtBearerOptions options)",
"Kind": "Removal"
},
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.JwtBearerOptions options)",
"Kind": "Removal" "Kind": "Removal"
}, },
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext : Microsoft.AspNetCore.Authentication.BaseControlContext", "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.JwtBearerOptions options)",
"Kind": "Removal" "Kind": "Removal"
}, },
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext : Microsoft.AspNetCore.Authentication.BaseControlContext", "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext",
"MemberId": "public Microsoft.AspNetCore.Builder.JwtBearerOptions get_Options()",
"Kind": "Removal" "Kind": "Removal"
}, },
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.JwtBearerOptions options)",
"Kind": "Removal" "Kind": "Removal"
}, },
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.JwtBearerOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.JwtBearerOptions options, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public static class Microsoft.AspNetCore.Builder.JwtBearerAppBuilderExtensions",
"MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseJwtBearerAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.JwtBearerOptions options)",
"Kind": "Removal" "Kind": "Removal"
} }
] ]

View File

@ -2,12 +2,25 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount; using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
public static class MicrosoftAccountExtensions public static class MicrosoftAccountExtensions
{ {
public static AuthenticationBuilder AddMicrosoftAccount(this AuthenticationBuilder builder)
=> builder.AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, _ => { });
public static AuthenticationBuilder AddMicrosoftAccount(this AuthenticationBuilder builder, Action<MicrosoftAccountOptions> configureOptions)
=> builder.AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddMicrosoftAccount(this AuthenticationBuilder builder, string authenticationScheme, Action<MicrosoftAccountOptions> configureOptions)
=> builder.AddOAuth<MicrosoftAccountOptions, MicrosoftAccountHandler>(authenticationScheme, configureOptions);
// REMOVE below once callers have been updated
public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services) public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services)
=> services.AddMicrosoftAccountAuthentication(MicrosoftAccountDefaults.AuthenticationScheme, _ => { }); => services.AddMicrosoftAccountAuthentication(MicrosoftAccountDefaults.AuthenticationScheme, _ => { });

View File

@ -32,12 +32,11 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount
var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), properties, Scheme.Name); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);
var context = new OAuthCreatingTicketContext(ticket, Context, Scheme, Options, Backchannel, tokens, payload);
context.RunClaimActions(); context.RunClaimActions();
await Events.CreatingTicket(context); await Events.CreatingTicket(context);
return context.Ticket; return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
} }
} }
} }

View File

@ -13,32 +13,34 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <summary> /// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>. /// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary> /// </summary>
public class OAuthCreatingTicketContext : BaseAuthenticationContext public class OAuthCreatingTicketContext : ResultContext<OAuthOptions>
{ {
/// <summary> /// <summary>
/// Initializes a new <see cref="OAuthCreatingTicketContext"/>. /// Initializes a new <see cref="OAuthCreatingTicketContext"/>.
/// </summary> /// </summary>
/// <param name="ticket">The <see cref="AuthenticationTicket"/>.</param> /// <param name="principal">The <see cref="ClaimsPrincipal"/>.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="context">The HTTP environment.</param> /// <param name="context">The HTTP environment.</param>
/// <param name="scheme">The authentication scheme.</param> /// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The options used by the authentication middleware.</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="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 OAuthCreatingTicketContext( public OAuthCreatingTicketContext(
AuthenticationTicket ticket, ClaimsPrincipal principal,
AuthenticationProperties properties,
HttpContext context, HttpContext context,
AuthenticationScheme scheme, AuthenticationScheme scheme,
OAuthOptions options, OAuthOptions options,
HttpClient backchannel, HttpClient backchannel,
OAuthTokenResponse tokens) OAuthTokenResponse tokens)
: this(ticket, context, scheme, options, backchannel, tokens, user: new JObject()) : this(principal, properties, context, scheme, options, backchannel, tokens, user: new JObject())
{ { }
}
/// <summary> /// <summary>
/// Initializes a new <see cref="OAuthCreatingTicketContext"/>. /// Initializes a new <see cref="OAuthCreatingTicketContext"/>.
/// </summary> /// </summary>
/// <param name="ticket">The <see cref="AuthenticationTicket"/>.</param> /// <param name="principal">The <see cref="ClaimsPrincipal"/>.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="context">The HTTP environment.</param> /// <param name="context">The HTTP environment.</param>
/// <param name="scheme">The authentication scheme.</param> /// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The options used by the authentication middleware.</param> /// <param name="options">The options used by the authentication middleware.</param>
@ -46,25 +48,16 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <param name="tokens">The tokens returned from the token endpoint.</param> /// <param name="tokens">The tokens returned from the token endpoint.</param>
/// <param name="user">The JSON-serialized user.</param> /// <param name="user">The JSON-serialized user.</param>
public OAuthCreatingTicketContext( public OAuthCreatingTicketContext(
AuthenticationTicket ticket, ClaimsPrincipal principal,
AuthenticationProperties properties,
HttpContext context, HttpContext context,
AuthenticationScheme scheme, AuthenticationScheme scheme,
OAuthOptions options, OAuthOptions options,
HttpClient backchannel, HttpClient backchannel,
OAuthTokenResponse tokens, OAuthTokenResponse tokens,
JObject user) JObject user)
: base(context, scheme.Name, ticket.Properties) : base(context, scheme, options)
{ {
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (backchannel == null) if (backchannel == null)
{ {
throw new ArgumentNullException(nameof(backchannel)); throw new ArgumentNullException(nameof(backchannel));
@ -80,23 +73,13 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme));
}
TokenResponse = tokens; TokenResponse = tokens;
Backchannel = backchannel; Backchannel = backchannel;
User = user; User = user;
Options = options; Principal = principal;
Scheme = scheme; Properties = properties;
Ticket = ticket;
} }
public OAuthOptions Options { get; }
public AuthenticationScheme Scheme { get; }
/// <summary> /// <summary>
/// Gets the JSON-serialized user or an empty /// Gets the JSON-serialized user or an empty
/// <see cref="JObject"/> if it is not available. /// <see cref="JObject"/> if it is not available.
@ -146,20 +129,12 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
public HttpClient Backchannel { get; } public HttpClient Backchannel { get; }
/// <summary> /// <summary>
/// The <see cref="AuthenticationTicket"/> that will be created. /// Gets the main identity exposed by the authentication ticket.
/// This property returns <c>null</c> when the ticket is <c>null</c>.
/// </summary> /// </summary>
public AuthenticationTicket Ticket { get; set; } public ClaimsIdentity Identity => Principal?.Identity as ClaimsIdentity;
/// <summary> public void RunClaimActions() => RunClaimActions(User);
/// Gets the main identity exposed by <see cref="Ticket"/>.
/// This property returns <c>null</c> when <see cref="Ticket"/> is <c>null</c>.
/// </summary>
public ClaimsIdentity Identity => Ticket?.Principal.Identity as ClaimsIdentity;
public void RunClaimActions()
{
RunClaimActions(User);
}
public void RunClaimActions(JObject userData) public void RunClaimActions(JObject userData)
{ {

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <summary> /// <summary>
/// Gets or sets the delegate that is invoked when the RedirectToAuthorizationEndpoint method is invoked. /// Gets or sets the delegate that is invoked when the RedirectToAuthorizationEndpoint method is invoked.
/// </summary> /// </summary>
public Func<OAuthRedirectToAuthorizationContext, Task> OnRedirectToAuthorizationEndpoint { get; set; } = context => public Func<RedirectContext<OAuthOptions>, Task> OnRedirectToAuthorizationEndpoint { get; set; } = context =>
{ {
context.Response.Redirect(context.RedirectUri); context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask; return Task.CompletedTask;
@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <summary> /// <summary>
/// Called when a Challenge causes a redirect to authorize endpoint in the OAuth handler. /// Called when a Challenge causes a redirect to authorize endpoint in the OAuth handler.
/// </summary> /// </summary>
/// <param name="context">Contains redirect URI and <see cref="Http.Authentication.AuthenticationProperties"/> of the challenge.</param> /// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge.</param>
public virtual Task RedirectToAuthorizationEndpoint(OAuthRedirectToAuthorizationContext context) => OnRedirectToAuthorizationEndpoint(context); public virtual Task RedirectToAuthorizationEndpoint(RedirectContext<OAuthOptions> context) => OnRedirectToAuthorizationEndpoint(context);
} }
} }

View File

@ -1,41 +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.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.OAuth
{
/// <summary>
/// Context passed when a Challenge causes a redirect to authorize endpoint in the handler.
/// </summary>
public class OAuthRedirectToAuthorizationContext : BaseContext
{
/// <summary>
/// Creates a new context object.
/// </summary>
/// <param name="context">The HTTP request context.</param>
/// <param name="options">The <see cref="OAuthOptions"/>.</param>
/// <param name="properties">The authentication properties of the challenge.</param>
/// <param name="redirectUri">The initial redirect URI.</param>
public OAuthRedirectToAuthorizationContext(HttpContext context, OAuthOptions options, AuthenticationProperties properties, string redirectUri)
: base(context)
{
RedirectUri = redirectUri;
Properties = properties;
Options = options;
}
public OAuthOptions Options { get; }
/// <summary>
/// Gets the URI used for the redirect operation.
/// </summary>
public string RedirectUri { get; private set; }
/// <summary>
/// Gets the authentication properties of the challenge.
/// </summary>
public AuthenticationProperties Properties { get; private set; }
}
}

View File

@ -2,6 +2,7 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -10,6 +11,18 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static class OAuthExtensions public static class OAuthExtensions
{ {
public static AuthenticationBuilder AddOAuth(this AuthenticationBuilder builder, string authenticationScheme, Action<OAuthOptions> configureOptions)
=> builder.AddOAuth<OAuthOptions, OAuthHandler<OAuthOptions>>(authenticationScheme, configureOptions);
public static AuthenticationBuilder AddOAuth<TOptions, THandler>(this AuthenticationBuilder builder, string authenticationScheme, Action<TOptions> configureOptions)
where TOptions : OAuthOptions, new()
where THandler : OAuthHandler<TOptions>
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<TOptions>, OAuthPostConfigureOptions<TOptions, THandler>>());
return builder.AddRemoteScheme<TOptions, THandler>(authenticationScheme, authenticationScheme, configureOptions);
}
// REMOVE below once callers have been updated
public static IServiceCollection AddOAuthAuthentication(this IServiceCollection services, string authenticationScheme, Action<OAuthOptions> configureOptions) public static IServiceCollection AddOAuthAuthentication(this IServiceCollection services, string authenticationScheme, Action<OAuthOptions> configureOptions)
{ {
return services.AddOAuthAuthentication<OAuthOptions, OAuthHandler<OAuthOptions>>(authenticationScheme, configureOptions); return services.AddOAuthAuthentication<OAuthOptions, OAuthHandler<OAuthOptions>>(authenticationScheme, configureOptions);

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
/// <returns>A new instance of the events instance.</returns> /// <returns>A new instance of the events instance.</returns>
protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new OAuthEvents()); protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new OAuthEvents());
protected override async Task<AuthenticateResult> HandleRemoteAuthenticateAsync() protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{ {
AuthenticationProperties properties = null; AuthenticationProperties properties = null;
var query = Request.Query; var query = Request.Query;
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
failureMessage.Append(";Uri=").Append(errorUri); failureMessage.Append(";Uri=").Append(errorUri);
} }
return AuthenticateResult.Fail(failureMessage.ToString()); return HandleRequestResult.Fail(failureMessage.ToString());
} }
var code = query["code"]; var code = query["code"];
@ -72,30 +72,30 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
properties = Options.StateDataFormat.Unprotect(state); properties = Options.StateDataFormat.Unprotect(state);
if (properties == null) if (properties == null)
{ {
return AuthenticateResult.Fail("The oauth state was missing or invalid."); return HandleRequestResult.Fail("The oauth state was missing or invalid.");
} }
// OAuth2 10.12 CSRF // OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties)) if (!ValidateCorrelationId(properties))
{ {
return AuthenticateResult.Fail("Correlation failed."); return HandleRequestResult.Fail("Correlation failed.");
} }
if (StringValues.IsNullOrEmpty(code)) if (StringValues.IsNullOrEmpty(code))
{ {
return AuthenticateResult.Fail("Code was not found."); return HandleRequestResult.Fail("Code was not found.");
} }
var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath)); var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath));
if (tokens.Error != null) if (tokens.Error != null)
{ {
return AuthenticateResult.Fail(tokens.Error); return HandleRequestResult.Fail(tokens.Error);
} }
if (string.IsNullOrEmpty(tokens.AccessToken)) if (string.IsNullOrEmpty(tokens.AccessToken))
{ {
return AuthenticateResult.Fail("Failed to retrieve access token."); return HandleRequestResult.Fail("Failed to retrieve access token.");
} }
var identity = new ClaimsIdentity(ClaimsIssuer); var identity = new ClaimsIdentity(ClaimsIssuer);
@ -137,11 +137,11 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
var ticket = await CreateTicketAsync(identity, properties, tokens); var ticket = await CreateTicketAsync(identity, properties, tokens);
if (ticket != null) if (ticket != null)
{ {
return AuthenticateResult.Success(ticket); return HandleRequestResult.Success(ticket);
} }
else else
{ {
return AuthenticateResult.Fail("Failed to retrieve user information from remote server."); return HandleRequestResult.Fail("Failed to retrieve user information from remote server.");
} }
} }
@ -185,10 +185,9 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{ {
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), properties, Scheme.Name); var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens);
var context = new OAuthCreatingTicketContext(ticket, Context, Scheme, Options, Backchannel, tokens);
await Events.CreatingTicket(context); await Events.CreatingTicket(context);
return context.Ticket; return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
} }
protected override async Task HandleChallengeAsync(AuthenticationProperties properties) protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
@ -202,8 +201,8 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
GenerateCorrelationId(properties); GenerateCorrelationId(properties);
var authorizationEndpoint = BuildChallengeUrl(properties, BuildRedirectUri(Options.CallbackPath)); var authorizationEndpoint = BuildChallengeUrl(properties, BuildRedirectUri(Options.CallbackPath));
var redirectContext = new OAuthRedirectToAuthorizationContext( var redirectContext = new RedirectContext<OAuthOptions>(
Context, Options, Context, Scheme, Options,
properties, authorizationEndpoint); properties, authorizationEndpoint);
await Events.RedirectToAuthorizationEndpoint(redirectContext); await Events.RedirectToAuthorizationEndpoint(redirectContext);
} }

View File

@ -23,24 +23,13 @@
"TypeId": "public interface Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", "TypeId": "public interface Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents",
"Kind": "Removal" "Kind": "Removal"
}, },
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext : Microsoft.AspNetCore.Authentication.BaseContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OAuthOptions options, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties, System.String redirectUri)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext : Microsoft.AspNetCore.Authentication.BaseContext",
"MemberId": "public Microsoft.AspNetCore.Builder.OAuthOptions get_Options()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext : Microsoft.AspNetCore.Authentication.BaseContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{ {
"TypeId": "public static class Microsoft.AspNetCore.Builder.OAuthAppBuilderExtensions", "TypeId": "public static class Microsoft.AspNetCore.Builder.OAuthAppBuilderExtensions",
"MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseOAuthAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.OAuthOptions options)", "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseOAuthAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.OAuthOptions options)",
"Kind": "Removal" "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext : Microsoft.AspNetCore.Authentication.BaseContext",
"Kind": "Removal"
} }
] ]

View File

@ -2,17 +2,18 @@
// 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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
public class AuthenticationFailedContext : BaseOpenIdConnectContext public class AuthenticationFailedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
public AuthenticationFailedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options) public AuthenticationFailedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options)
: base(context, scheme, options) : base(context, scheme, options, new AuthenticationProperties())
{ { }
}
public OpenIdConnectMessage ProtocolMessage { get; set; }
public Exception Exception { get; set; } public Exception Exception { get; set; }
} }

View File

@ -3,9 +3,7 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
@ -13,17 +11,19 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary> /// <summary>
/// This Context can be used to be informed when an 'AuthorizationCode' is received over the OpenIdConnect protocol. /// This Context can be used to be informed when an 'AuthorizationCode' is received over the OpenIdConnect protocol.
/// </summary> /// </summary>
public class AuthorizationCodeReceivedContext : BaseOpenIdConnectContext public class AuthorizationCodeReceivedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
/// <summary> /// <summary>
/// Creates a <see cref="AuthorizationCodeReceivedContext"/> /// Creates a <see cref="AuthorizationCodeReceivedContext"/>
/// </summary> /// </summary>
public AuthorizationCodeReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options) public AuthorizationCodeReceivedContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
} OpenIdConnectOptions options,
AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
public AuthenticationProperties Properties { get; set; } public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="JwtSecurityToken"/> that was received in the authentication response, if any. /// Gets or sets the <see cref="JwtSecurityToken"/> that was received in the authentication response, if any.

View File

@ -1,25 +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 Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
public class BaseOpenIdConnectContext : BaseControlContext
{
public BaseOpenIdConnectContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options)
: base(context)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
Scheme = scheme ?? throw new ArgumentNullException(nameof(scheme));
}
public OpenIdConnectOptions Options { get; }
public AuthenticationScheme Scheme { get; }
public OpenIdConnectMessage ProtocolMessage { get; set; }
}
}

View File

@ -2,21 +2,24 @@
// 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 Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
public class MessageReceivedContext : BaseOpenIdConnectContext public class MessageReceivedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
public MessageReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options) public MessageReceivedContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
} OpenIdConnectOptions options,
AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary> /// <summary>
/// Bearer Token. This will give application an opportunity to retrieve token from an alternation location. /// Bearer Token. This will give application an opportunity to retrieve token from an alternation location.
/// </summary> /// </summary>
public string Token { get; set; } public string Token { get; set; }
public AuthenticationProperties Properties { get; set; }
} }
} }

View File

@ -2,6 +2,7 @@
// 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 Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
@ -9,14 +10,25 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// When a user configures the <see cref="OpenIdConnectHandler"/> to be notified prior to redirecting to an IdentityProvider /// When a user configures the <see cref="OpenIdConnectHandler"/> to be notified prior to redirecting to an IdentityProvider
/// an instance of <see cref="RedirectContext"/> is passed to the 'RedirectToAuthenticationEndpoint' or 'RedirectToEndSessionEndpoint' events. /// an instance of <see cref="RedirectContext"/> is passed to the 'RedirectToAuthenticationEndpoint' or 'RedirectToEndSessionEndpoint' events.
/// </summary> /// </summary>
public class RedirectContext : BaseOpenIdConnectContext public class RedirectContext : PropertiesContext<OpenIdConnectOptions>
{ {
public RedirectContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, AuthenticationProperties properties) public RedirectContext(
: base(context, scheme, options) HttpContext context,
{ AuthenticationScheme scheme,
Properties = properties; OpenIdConnectOptions options,
} AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
public AuthenticationProperties Properties { get; } public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
/// If true, will skip any default logic for this redirect.
/// </summary>
public bool Handled { get; private set; }
/// <summary>
/// Skips any default logic for this redirect.
/// </summary>
public void HandleResponse() => Handled = true;
} }
} }

View File

@ -6,16 +6,12 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
public class RemoteSignOutContext : BaseOpenIdConnectContext public class RemoteSignOutContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
public RemoteSignOutContext( public RemoteSignOutContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, OpenIdConnectMessage message)
HttpContext context, : base(context, scheme, options, new AuthenticationProperties())
AuthenticationScheme scheme, => ProtocolMessage = message;
OpenIdConnectOptions options,
OpenIdConnectMessage message) public OpenIdConnectMessage ProtocolMessage { get; set; }
: base(context, scheme, options)
{
ProtocolMessage = message;
}
} }
} }

View File

@ -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.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
@ -9,18 +10,16 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary> /// <summary>
/// This Context can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint. /// This Context can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint.
/// </summary> /// </summary>
public class TokenResponseReceivedContext : BaseOpenIdConnectContext public class TokenResponseReceivedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
/// <summary> /// <summary>
/// Creates a <see cref="TokenResponseReceivedContext"/> /// Creates a <see cref="TokenResponseReceivedContext"/>
/// </summary> /// </summary>
public TokenResponseReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, AuthenticationProperties properties) public TokenResponseReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, ClaimsPrincipal user, AuthenticationProperties properties)
: base(context, scheme, options) : base(context, scheme, options, properties)
{ => Principal = user;
Properties = properties;
}
public AuthenticationProperties Properties { get; } public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/> that contains the tokens received after redeeming the code at the token endpoint. /// Gets or sets the <see cref="OpenIdConnectMessage"/> that contains the tokens received after redeeming the code at the token endpoint.

View File

@ -2,26 +2,23 @@
// 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.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net.Http; using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
public class TokenValidatedContext : BaseOpenIdConnectContext public class TokenValidatedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
/// <summary> /// <summary>
/// Creates a <see cref="TokenValidatedContext"/> /// Creates a <see cref="TokenValidatedContext"/>
/// </summary> /// </summary>
public TokenValidatedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options) public TokenValidatedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, ClaimsPrincipal principal, AuthenticationProperties properties)
: base(context, scheme, options) : base(context, scheme, options, properties)
{ => Principal = principal;
}
public OpenIdConnectMessage ProtocolMessage { get; set; }
public AuthenticationProperties Properties { get; set; }
public JwtSecurityToken SecurityToken { get; set; } public JwtSecurityToken SecurityToken { get; set; }
public OpenIdConnectMessage TokenEndpointResponse { get; set; } public OpenIdConnectMessage TokenEndpointResponse { get; set; }

View File

@ -1,17 +1,20 @@
// 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.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
public class UserInformationReceivedContext : BaseOpenIdConnectContext public class UserInformationReceivedContext : RemoteAuthenticationContext<OpenIdConnectOptions>
{ {
public UserInformationReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options) public UserInformationReceivedContext(HttpContext context, AuthenticationScheme scheme, OpenIdConnectOptions options, ClaimsPrincipal principal, AuthenticationProperties properties)
: base(context, scheme, options) : base(context, scheme, options, properties)
{ => Principal = principal;
}
public OpenIdConnectMessage ProtocolMessage { get; set; }
public JObject User { get; set; } public JObject User { get; set; }
} }

View File

@ -8,9 +8,7 @@ namespace Microsoft.Extensions.Logging
internal static class LoggingExtensions internal static class LoggingExtensions
{ {
private static Action<ILogger, Exception> _redirectToIdentityProviderForSignOutHandledResponse; private static Action<ILogger, Exception> _redirectToIdentityProviderForSignOutHandledResponse;
private static Action<ILogger, Exception> _redirectToIdentityProviderForSignOutSkipped;
private static Action<ILogger, Exception> _redirectToIdentityProviderHandledResponse; private static Action<ILogger, Exception> _redirectToIdentityProviderHandledResponse;
private static Action<ILogger, Exception> _redirectToIdentityProviderSkipped;
private static Action<ILogger, Exception> _updatingConfiguration; private static Action<ILogger, Exception> _updatingConfiguration;
private static Action<ILogger, Exception> _receivedIdToken; private static Action<ILogger, Exception> _receivedIdToken;
private static Action<ILogger, Exception> _redeemingCodeForTokens; private static Action<ILogger, Exception> _redeemingCodeForTokens;
@ -55,6 +53,7 @@ namespace Microsoft.Extensions.Logging
private static Action<ILogger, Exception> _remoteSignOut; private static Action<ILogger, Exception> _remoteSignOut;
private static Action<ILogger, Exception> _remoteSignOutSessionIdMissing; private static Action<ILogger, Exception> _remoteSignOutSessionIdMissing;
private static Action<ILogger, Exception> _remoteSignOutSessionIdInvalid; private static Action<ILogger, Exception> _remoteSignOutSessionIdInvalid;
private static Action<ILogger, string, Exception> _signOut;
static LoggingExtensions() static LoggingExtensions()
{ {
@ -63,10 +62,6 @@ namespace Microsoft.Extensions.Logging
eventId: 1, eventId: 1,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "RedirectToIdentityProviderForSignOut.HandledResponse"); formatString: "RedirectToIdentityProviderForSignOut.HandledResponse");
_redirectToIdentityProviderForSignOutSkipped = LoggerMessage.Define(
eventId: 2,
logLevel: LogLevel.Debug,
formatString: "RedirectToIdentityProviderForSignOut.Skipped");
_invalidLogoutQueryStringRedirectUrl = LoggerMessage.Define<string>( _invalidLogoutQueryStringRedirectUrl = LoggerMessage.Define<string>(
eventId: 3, eventId: 3,
logLevel: LogLevel.Warning, logLevel: LogLevel.Warning,
@ -87,10 +82,6 @@ namespace Microsoft.Extensions.Logging
eventId: 6, eventId: 6,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "RedirectToIdentityProvider.HandledResponse"); formatString: "RedirectToIdentityProvider.HandledResponse");
_redirectToIdentityProviderSkipped = LoggerMessage.Define(
eventId: 7,
logLevel: LogLevel.Debug,
formatString: "RedirectToIdentityProvider.Skipped");
_invalidAuthenticationRequestUrl = LoggerMessage.Define<string>( _invalidAuthenticationRequestUrl = LoggerMessage.Define<string>(
eventId: 8, eventId: 8,
logLevel: LogLevel.Warning, logLevel: LogLevel.Warning,
@ -253,6 +244,10 @@ namespace Microsoft.Extensions.Logging
logLevel: LogLevel.Error, logLevel: LogLevel.Error,
formatString: "The remote signout request was ignored because the 'sid' parameter didn't match " + formatString: "The remote signout request was ignored because the 'sid' parameter didn't match " +
"the expected value, which may indicate an unsolicited logout."); "the expected value, which may indicate an unsolicited logout.");
_signOut = LoggerMessage.Define<string>(
eventId: 49,
logLevel: LogLevel.Information,
formatString: "AuthenticationScheme: {AuthenticationScheme} signed out.");
} }
public static void UpdatingConfiguration(this ILogger logger) public static void UpdatingConfiguration(this ILogger logger)
@ -345,21 +340,11 @@ namespace Microsoft.Extensions.Logging
_redirectToIdentityProviderForSignOutHandledResponse(logger, null); _redirectToIdentityProviderForSignOutHandledResponse(logger, null);
} }
public static void RedirectToIdentityProviderForSignOutSkipped(this ILogger logger)
{
_redirectToIdentityProviderForSignOutSkipped(logger, null);
}
public static void RedirectToIdentityProviderHandledResponse(this ILogger logger) public static void RedirectToIdentityProviderHandledResponse(this ILogger logger)
{ {
_redirectToIdentityProviderHandledResponse(logger, null); _redirectToIdentityProviderHandledResponse(logger, null);
} }
public static void RedirectToIdentityProviderSkipped(this ILogger logger)
{
_redirectToIdentityProviderSkipped(logger, null);
}
public static void UserInformationReceivedHandledResponse(this ILogger logger) public static void UserInformationReceivedHandledResponse(this ILogger logger)
{ {
_userInformationReceivedHandledResponse(logger, null); _userInformationReceivedHandledResponse(logger, null);
@ -494,5 +479,10 @@ namespace Microsoft.Extensions.Logging
{ {
_remoteSignOutSessionIdInvalid(logger, null); _remoteSignOutSessionIdInvalid(logger, null);
} }
public static void SignedOut(this ILogger logger, string authenticationScheme)
{
_signOut(logger, authenticationScheme, null);
}
} }
} }

View File

@ -2,6 +2,7 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -10,6 +11,19 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static class OpenIdConnectExtensions public static class OpenIdConnectExtensions
{ {
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder)
=> builder.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, _ => { });
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, Action<OpenIdConnectOptions> configureOptions)
=> builder.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, Action<OpenIdConnectOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>());
return builder.AddRemoteScheme<OpenIdConnectOptions, OpenIdConnectHandler>(authenticationScheme, authenticationScheme, configureOptions);
}
// REMOVE once callers have been updated
public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services) public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services)
=> services.AddOpenIdConnectAuthentication(OpenIdConnectDefaults.AuthenticationScheme, _ => { }); => services.AddOpenIdConnectAuthentication(OpenIdConnectDefaults.AuthenticationScheme, _ => { });

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary> /// <summary>
/// A per-request authentication handler for the OpenIdConnectAuthenticationMiddleware. /// A per-request authentication handler for the OpenIdConnectAuthenticationMiddleware.
/// </summary> /// </summary>
public class OpenIdConnectHandler : RemoteAuthenticationHandler<OpenIdConnectOptions> public class OpenIdConnectHandler : RemoteAuthenticationHandler<OpenIdConnectOptions>, IAuthenticationSignOutHandler
{ {
private const string NonceProperty = "N"; private const string NonceProperty = "N";
private const string UriSchemeDelimiter = "://"; private const string UriSchemeDelimiter = "://";
@ -110,15 +110,18 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
var remoteSignOutContext = new RemoteSignOutContext(Context, Scheme, Options, message); var remoteSignOutContext = new RemoteSignOutContext(Context, Scheme, Options, message);
await Events.RemoteSignOut(remoteSignOutContext); await Events.RemoteSignOut(remoteSignOutContext);
if (remoteSignOutContext.HandledResponse) if (remoteSignOutContext.Result != null)
{ {
Logger.RemoteSignOutHandledResponse(); if (remoteSignOutContext.Result.Handled)
return true; {
} Logger.RemoteSignOutHandledResponse();
if (remoteSignOutContext.Skipped) return true;
{ }
Logger.RemoteSignOutSkipped(); if (remoteSignOutContext.Result.Skipped)
return false; {
Logger.RemoteSignOutSkipped();
return false;
}
} }
if (message == null) if (message == null)
@ -161,8 +164,10 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// Redirect user to the identity provider for sign out /// Redirect user to the identity provider for sign out
/// </summary> /// </summary>
/// <returns>A task executing the sign out procedure</returns> /// <returns>A task executing the sign out procedure</returns>
protected override async Task HandleSignOutAsync(AuthenticationProperties properties) public async virtual Task SignOutAsync(AuthenticationProperties properties)
{ {
properties = properties ?? new AuthenticationProperties();
Logger.EnteringOpenIdAuthenticationHandlerHandleSignOutAsync(GetType().FullName); Logger.EnteringOpenIdAuthenticationHandlerHandleSignOutAsync(GetType().FullName);
if (_configuration == null && Options.ConfigurationManager != null) if (_configuration == null && Options.ConfigurationManager != null)
@ -199,16 +204,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
}; };
await Events.RedirectToIdentityProviderForSignOut(redirectContext); await Events.RedirectToIdentityProviderForSignOut(redirectContext);
if (redirectContext.HandledResponse) if (redirectContext.Handled)
{ {
Logger.RedirectToIdentityProviderForSignOutHandledResponse(); Logger.RedirectToIdentityProviderForSignOutHandledResponse();
return; return;
} }
else if (redirectContext.Skipped)
{
Logger.RedirectToIdentityProviderForSignOutSkipped();
return;
}
message = redirectContext.ProtocolMessage; message = redirectContext.ProtocolMessage;
@ -221,8 +221,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (string.IsNullOrEmpty(message.IssuerAddress)) if (string.IsNullOrEmpty(message.IssuerAddress))
{ {
throw new InvalidOperationException( throw new InvalidOperationException("Cannot redirect to the end session endpoint, the configuration may be missing or invalid.");
"Cannot redirect to the end session endpoint, the configuration may be missing or invalid.");
} }
if (Options.AuthenticationMethod == OpenIdConnectRedirectBehavior.RedirectGet) if (Options.AuthenticationMethod == OpenIdConnectRedirectBehavior.RedirectGet)
@ -266,6 +265,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
throw new NotImplementedException($"An unsupported authentication method has been configured: {Options.AuthenticationMethod}"); throw new NotImplementedException($"An unsupported authentication method has been configured: {Options.AuthenticationMethod}");
} }
Logger.SignedOut(Scheme.Name);
} }
/// <summary> /// <summary>
@ -343,16 +344,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
}; };
await Events.RedirectToIdentityProvider(redirectContext); await Events.RedirectToIdentityProvider(redirectContext);
if (redirectContext.HandledResponse) if (redirectContext.Handled)
{ {
Logger.RedirectToIdentityProviderHandledResponse(); Logger.RedirectToIdentityProviderHandledResponse();
return; return;
} }
else if (redirectContext.Skipped)
{
Logger.RedirectToIdentityProviderSkipped();
return;
}
message = redirectContext.ProtocolMessage; message = redirectContext.ProtocolMessage;
@ -418,8 +414,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary> /// <summary>
/// Invoked to process incoming OpenIdConnect messages. /// Invoked to process incoming OpenIdConnect messages.
/// </summary> /// </summary>
/// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns> /// <returns>An <see cref="HandleRequestResult"/>.</returns>
protected override async Task<AuthenticateResult> HandleRemoteAuthenticateAsync() protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{ {
Logger.EnteringOpenIdAuthenticationHandlerHandleRemoteAuthenticateAsync(GetType().FullName); Logger.EnteringOpenIdAuthenticationHandlerHandleRemoteAuthenticateAsync(GetType().FullName);
@ -437,9 +433,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (Options.SkipUnrecognizedRequests) if (Options.SkipUnrecognizedRequests)
{ {
// Not for us? // Not for us?
return AuthenticateResult.None(); return HandleRequestResult.SkipHandler();
} }
return AuthenticateResult.Fail("An OpenID Connect response cannot contain an " + return HandleRequestResult.Fail("An OpenID Connect response cannot contain an " +
"identity token or an access token when using response_mode=query"); "identity token or an access token when using response_mode=query");
} }
} }
@ -459,13 +455,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (Options.SkipUnrecognizedRequests) if (Options.SkipUnrecognizedRequests)
{ {
// Not for us? // Not for us?
return AuthenticateResult.None(); return HandleRequestResult.SkipHandler();
} }
return AuthenticateResult.Fail("No message."); return HandleRequestResult.Fail("No message.");
} }
AuthenticateResult result;
try try
{ {
AuthenticationProperties properties = null; AuthenticationProperties properties = null;
@ -475,9 +469,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse, properties); var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse, properties);
if (messageReceivedContext.IsProcessingComplete(out result)) if (messageReceivedContext.Result != null)
{ {
return result; return messageReceivedContext.Result;
} }
authorizationResponse = messageReceivedContext.ProtocolMessage; authorizationResponse = messageReceivedContext.ProtocolMessage;
properties = messageReceivedContext.Properties; properties = messageReceivedContext.Properties;
@ -491,9 +485,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
Logger.NullOrEmptyAuthorizationResponseState(); Logger.NullOrEmptyAuthorizationResponseState();
if (Options.SkipUnrecognizedRequests) if (Options.SkipUnrecognizedRequests)
{ {
return AuthenticateResult.None(); return HandleRequestResult.SkipHandler();
} }
return AuthenticateResult.Fail(Resources.MessageStateIsNullOrEmpty); return HandleRequestResult.Fail(Resources.MessageStateIsNullOrEmpty);
} }
// if state exists and we failed to 'unprotect' this is not a message we should process. // if state exists and we failed to 'unprotect' this is not a message we should process.
@ -506,9 +500,9 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (Options.SkipUnrecognizedRequests) if (Options.SkipUnrecognizedRequests)
{ {
// Not for us? // Not for us?
return AuthenticateResult.None(); return HandleRequestResult.SkipHandler();
} }
return AuthenticateResult.Fail(Resources.MessageStateIsInvalid); return HandleRequestResult.Fail(Resources.MessageStateIsInvalid);
} }
string userstate = null; string userstate = null;
@ -517,13 +511,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (!ValidateCorrelationId(properties)) if (!ValidateCorrelationId(properties))
{ {
return AuthenticateResult.Fail("Correlation failed."); return HandleRequestResult.Fail("Correlation failed.");
} }
// if any of the error fields are set, throw error null // if any of the error fields are set, throw error null
if (!string.IsNullOrEmpty(authorizationResponse.Error)) if (!string.IsNullOrEmpty(authorizationResponse.Error))
{ {
return AuthenticateResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null)); return HandleRequestResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null));
} }
if (_configuration == null && Options.ConfigurationManager != null) if (_configuration == null && Options.ConfigurationManager != null)
@ -534,7 +528,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
PopulateSessionProperties(authorizationResponse, properties); PopulateSessionProperties(authorizationResponse, properties);
AuthenticationTicket ticket = null; ClaimsPrincipal user = null;
JwtSecurityToken jwt = null; JwtSecurityToken jwt = null;
string nonce = null; string nonce = null;
var validationParameters = Options.TokenValidationParameters.Clone(); var validationParameters = Options.TokenValidationParameters.Clone();
@ -543,7 +537,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (!string.IsNullOrEmpty(authorizationResponse.IdToken)) if (!string.IsNullOrEmpty(authorizationResponse.IdToken))
{ {
Logger.ReceivedIdToken(); Logger.ReceivedIdToken();
ticket = ValidateToken(authorizationResponse.IdToken, properties, validationParameters, out jwt); user = ValidateToken(authorizationResponse.IdToken, properties, validationParameters, out jwt);
nonce = jwt.Payload.Nonce; nonce = jwt.Payload.Nonce;
if (!string.IsNullOrEmpty(nonce)) if (!string.IsNullOrEmpty(nonce))
@ -551,14 +545,14 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
nonce = ReadNonceCookie(nonce); nonce = ReadNonceCookie(nonce);
} }
var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, null, properties, ticket, jwt, nonce); var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, null, user, properties, jwt, nonce);
if (tokenValidatedContext.IsProcessingComplete(out result)) if (tokenValidatedContext.Result != null)
{ {
return result; return tokenValidatedContext.Result;
} }
authorizationResponse = tokenValidatedContext.ProtocolMessage; authorizationResponse = tokenValidatedContext.ProtocolMessage;
user = tokenValidatedContext.Principal;
properties = tokenValidatedContext.Properties; properties = tokenValidatedContext.Properties;
ticket = tokenValidatedContext.Ticket;
jwt = tokenValidatedContext.SecurityToken; jwt = tokenValidatedContext.SecurityToken;
nonce = tokenValidatedContext.Nonce; nonce = tokenValidatedContext.Nonce;
} }
@ -576,17 +570,17 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
// Authorization Code or Hybrid flow // Authorization Code or Hybrid flow
if (!string.IsNullOrEmpty(authorizationResponse.Code)) if (!string.IsNullOrEmpty(authorizationResponse.Code))
{ {
var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(authorizationResponse, properties, ticket, jwt); var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(authorizationResponse, user, properties, jwt);
if (authorizationCodeReceivedContext.IsProcessingComplete(out result)) if (authorizationCodeReceivedContext.Result != null)
{ {
return result; return authorizationCodeReceivedContext.Result;
} }
authorizationResponse = authorizationCodeReceivedContext.ProtocolMessage; authorizationResponse = authorizationCodeReceivedContext.ProtocolMessage;
user = authorizationCodeReceivedContext.Principal;
properties = authorizationCodeReceivedContext.Properties; properties = authorizationCodeReceivedContext.Properties;
var tokenEndpointRequest = authorizationCodeReceivedContext.TokenEndpointRequest; var tokenEndpointRequest = authorizationCodeReceivedContext.TokenEndpointRequest;
// If the developer redeemed the code themselves... // If the developer redeemed the code themselves...
tokenEndpointResponse = authorizationCodeReceivedContext.TokenEndpointResponse; tokenEndpointResponse = authorizationCodeReceivedContext.TokenEndpointResponse;
ticket = authorizationCodeReceivedContext.Ticket;
jwt = authorizationCodeReceivedContext.JwtSecurityToken; jwt = authorizationCodeReceivedContext.JwtSecurityToken;
if (!authorizationCodeReceivedContext.HandledCodeRedemption) if (!authorizationCodeReceivedContext.HandledCodeRedemption)
@ -594,14 +588,16 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest); tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest);
} }
var tokenResponseReceivedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, properties, ticket); var tokenResponseReceivedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, user, properties);
if (tokenResponseReceivedContext.IsProcessingComplete(out result)) if (tokenResponseReceivedContext.Result != null)
{ {
return result; return tokenResponseReceivedContext.Result;
} }
authorizationResponse = tokenResponseReceivedContext.ProtocolMessage; authorizationResponse = tokenResponseReceivedContext.ProtocolMessage;
tokenEndpointResponse = tokenResponseReceivedContext.TokenEndpointResponse; tokenEndpointResponse = tokenResponseReceivedContext.TokenEndpointResponse;
user = tokenResponseReceivedContext.Principal;
properties = tokenResponseReceivedContext.Properties;
// no need to validate signature when token is received using "code flow" as per spec // no need to validate signature when token is received using "code flow" as per spec
// [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation]. // [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation].
@ -610,10 +606,10 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
// At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response. // At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response.
// And we'll want to validate the new JWT in ValidateTokenResponse. // And we'll want to validate the new JWT in ValidateTokenResponse.
JwtSecurityToken tokenEndpointJwt; JwtSecurityToken tokenEndpointJwt;
var tokenEndpointTicket = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out tokenEndpointJwt); var tokenEndpointUser = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out tokenEndpointJwt);
// Avoid reading & deleting the nonce cookie, running the event, etc, if it was already done as part of the authorization response validation. // Avoid reading & deleting the nonce cookie, running the event, etc, if it was already done as part of the authorization response validation.
if (ticket == null) if (user == null)
{ {
nonce = tokenEndpointJwt.Payload.Nonce; nonce = tokenEndpointJwt.Payload.Nonce;
if (!string.IsNullOrEmpty(nonce)) if (!string.IsNullOrEmpty(nonce))
@ -621,15 +617,15 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
nonce = ReadNonceCookie(nonce); nonce = ReadNonceCookie(nonce);
} }
var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, tokenEndpointResponse, properties, tokenEndpointTicket, tokenEndpointJwt, nonce); var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, tokenEndpointResponse, tokenEndpointUser, properties, tokenEndpointJwt, nonce);
if (tokenValidatedContext.IsProcessingComplete(out result)) if (tokenValidatedContext.Result != null)
{ {
return result; return tokenValidatedContext.Result;
} }
authorizationResponse = tokenValidatedContext.ProtocolMessage; authorizationResponse = tokenValidatedContext.ProtocolMessage;
tokenEndpointResponse = tokenValidatedContext.TokenEndpointResponse; tokenEndpointResponse = tokenValidatedContext.TokenEndpointResponse;
user = tokenValidatedContext.Principal;
properties = tokenValidatedContext.Properties; properties = tokenValidatedContext.Properties;
ticket = tokenValidatedContext.Ticket;
jwt = tokenValidatedContext.SecurityToken; jwt = tokenValidatedContext.SecurityToken;
nonce = tokenValidatedContext.Nonce; nonce = tokenValidatedContext.Nonce;
} }
@ -658,23 +654,23 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
if (Options.SaveTokens) if (Options.SaveTokens)
{ {
SaveTokens(ticket.Properties, tokenEndpointResponse ?? authorizationResponse); SaveTokens(properties, tokenEndpointResponse ?? authorizationResponse);
} }
if (Options.GetClaimsFromUserInfoEndpoint) if (Options.GetClaimsFromUserInfoEndpoint)
{ {
return await GetUserInformationAsync(tokenEndpointResponse ?? authorizationResponse, jwt, ticket); return await GetUserInformationAsync(tokenEndpointResponse ?? authorizationResponse, jwt, user, properties);
} }
else else
{ {
var identity = (ClaimsIdentity)ticket.Principal.Identity; var identity = (ClaimsIdentity)user.Identity;
foreach (var action in Options.ClaimActions) foreach (var action in Options.ClaimActions)
{ {
action.Run(null, identity, ClaimsIssuer); action.Run(null, identity, ClaimsIssuer);
} }
} }
return AuthenticateResult.Success(ticket); return HandleRequestResult.Success(new AuthenticationTicket(user, properties, Scheme.Name));
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -691,12 +687,12 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
var authenticationFailedContext = await RunAuthenticationFailedEventAsync(authorizationResponse, exception); var authenticationFailedContext = await RunAuthenticationFailedEventAsync(authorizationResponse, exception);
if (authenticationFailedContext.IsProcessingComplete(out result)) if (authenticationFailedContext.Result != null)
{ {
return result; return authenticationFailedContext.Result;
} }
return AuthenticateResult.Fail(exception); return HandleRequestResult.Fail(exception);
} }
} }
@ -765,21 +761,24 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary> /// </summary>
/// <param name="message">message that is being processed</param> /// <param name="message">message that is being processed</param>
/// <param name="jwt">The <see cref="JwtSecurityToken"/>.</param> /// <param name="jwt">The <see cref="JwtSecurityToken"/>.</param>
/// <param name="ticket">authentication ticket with claims principal and identities</param> /// <param name="principal">The claims principal and identities.</param>
/// <returns>Authentication ticket with identity with additional claims, if any.</returns> /// <param name="properties">The authentication properties.</param>
protected virtual async Task<AuthenticateResult> GetUserInformationAsync(OpenIdConnectMessage message, JwtSecurityToken jwt, AuthenticationTicket ticket) /// <returns><see cref="HandleRequestResult"/> which is used to determine if the remote authentication was successful.</returns>
protected virtual async Task<HandleRequestResult> GetUserInformationAsync(
OpenIdConnectMessage message, JwtSecurityToken jwt,
ClaimsPrincipal principal, AuthenticationProperties properties)
{ {
var userInfoEndpoint = _configuration?.UserInfoEndpoint; var userInfoEndpoint = _configuration?.UserInfoEndpoint;
if (string.IsNullOrEmpty(userInfoEndpoint)) if (string.IsNullOrEmpty(userInfoEndpoint))
{ {
Logger.UserInfoEndpointNotSet(); Logger.UserInfoEndpointNotSet();
return AuthenticateResult.Success(ticket); return HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name));
} }
if (string.IsNullOrEmpty(message.AccessToken)) if (string.IsNullOrEmpty(message.AccessToken))
{ {
Logger.AccessTokenNotAvailable(); Logger.AccessTokenNotAvailable();
return AuthenticateResult.Success(ticket); return HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name));
} }
Logger.RetrievingClaims(); Logger.RetrievingClaims();
var requestMessage = new HttpRequestMessage(HttpMethod.Get, userInfoEndpoint); var requestMessage = new HttpRequestMessage(HttpMethod.Get, userInfoEndpoint);
@ -801,16 +800,16 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
else else
{ {
return AuthenticateResult.Fail("Unknown response type: " + contentType.MediaType); return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType);
} }
var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(ticket, message, user); var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(principal, properties, message, user);
AuthenticateResult result; if (userInformationReceivedContext.Result != null)
if (userInformationReceivedContext.IsProcessingComplete(out result))
{ {
return result; return userInformationReceivedContext.Result;
} }
ticket = userInformationReceivedContext.Ticket; principal = userInformationReceivedContext.Principal;
properties = userInformationReceivedContext.Properties;
user = userInformationReceivedContext.User; user = userInformationReceivedContext.User;
Options.ProtocolValidator.ValidateUserInfoResponse(new OpenIdConnectProtocolValidationContext() Options.ProtocolValidator.ValidateUserInfoResponse(new OpenIdConnectProtocolValidationContext()
@ -819,14 +818,14 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
ValidatedIdToken = jwt, ValidatedIdToken = jwt,
}); });
var identity = (ClaimsIdentity)ticket.Principal.Identity; var identity = (ClaimsIdentity)principal.Identity;
foreach (var action in Options.ClaimActions) foreach (var action in Options.ClaimActions)
{ {
action.Run(user, identity, ClaimsIssuer); action.Run(user, identity, ClaimsIssuer);
} }
return AuthenticateResult.Success(ticket); return HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name));
} }
/// <summary> /// <summary>
@ -983,51 +982,54 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
private async Task<MessageReceivedContext> RunMessageReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties) private async Task<MessageReceivedContext> RunMessageReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties)
{ {
Logger.MessageReceived(message.BuildRedirectUrl()); Logger.MessageReceived(message.BuildRedirectUrl());
var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options) var context = new MessageReceivedContext(Context, Scheme, Options, properties)
{ {
ProtocolMessage = message, ProtocolMessage = message,
Properties = properties,
}; };
await Events.MessageReceived(messageReceivedContext); await Events.MessageReceived(context);
if (messageReceivedContext.HandledResponse) if (context.Result != null)
{ {
Logger.MessageReceivedContextHandledResponse(); if (context.Result.Handled)
} {
else if (messageReceivedContext.Skipped) Logger.MessageReceivedContextHandledResponse();
{ }
Logger.MessageReceivedContextSkipped(); else if (context.Result.Skipped)
{
Logger.MessageReceivedContextSkipped();
}
} }
return messageReceivedContext; return context;
} }
private async Task<TokenValidatedContext> RunTokenValidatedEventAsync(OpenIdConnectMessage authorizationResponse, OpenIdConnectMessage tokenEndpointResponse, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt, string nonce) private async Task<TokenValidatedContext> RunTokenValidatedEventAsync(OpenIdConnectMessage authorizationResponse, OpenIdConnectMessage tokenEndpointResponse, ClaimsPrincipal user, AuthenticationProperties properties, JwtSecurityToken jwt, string nonce)
{ {
var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options) var context = new TokenValidatedContext(Context, Scheme, Options, user, properties)
{ {
ProtocolMessage = authorizationResponse, ProtocolMessage = authorizationResponse,
TokenEndpointResponse = tokenEndpointResponse, TokenEndpointResponse = tokenEndpointResponse,
Properties = properties,
Ticket = ticket,
SecurityToken = jwt, SecurityToken = jwt,
Nonce = nonce, Nonce = nonce,
}; };
await Events.TokenValidated(tokenValidatedContext); await Events.TokenValidated(context);
if (tokenValidatedContext.HandledResponse) if (context.Result != null)
{ {
Logger.TokenValidatedHandledResponse(); if (context.Result.Handled)
} {
else if (tokenValidatedContext.Skipped) Logger.TokenValidatedHandledResponse();
{ }
Logger.TokenValidatedSkipped(); else if (context.Result.Skipped)
{
Logger.TokenValidatedSkipped();
}
} }
return tokenValidatedContext; return context;
} }
private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage authorizationResponse, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt) private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage authorizationResponse, ClaimsPrincipal user, AuthenticationProperties properties, JwtSecurityToken jwt)
{ {
Logger.AuthorizationCodeReceived(); Logger.AuthorizationCodeReceived();
@ -1041,102 +1043,112 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
RedirectUri = properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey] RedirectUri = properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]
}; };
var authorizationCodeReceivedContext = new AuthorizationCodeReceivedContext(Context, Scheme, Options) var context = new AuthorizationCodeReceivedContext(Context, Scheme, Options, properties)
{ {
ProtocolMessage = authorizationResponse, ProtocolMessage = authorizationResponse,
Properties = properties,
TokenEndpointRequest = tokenEndpointRequest, TokenEndpointRequest = tokenEndpointRequest,
Ticket = ticket, Principal = user,
JwtSecurityToken = jwt, JwtSecurityToken = jwt,
Backchannel = Backchannel, Backchannel = Backchannel
}; };
await Events.AuthorizationCodeReceived(authorizationCodeReceivedContext); await Events.AuthorizationCodeReceived(context);
if (authorizationCodeReceivedContext.HandledResponse) if (context.Result != null)
{ {
Logger.AuthorizationCodeReceivedContextHandledResponse(); if (context.Result.Handled)
} {
else if (authorizationCodeReceivedContext.Skipped) Logger.AuthorizationCodeReceivedContextHandledResponse();
{ }
Logger.AuthorizationCodeReceivedContextSkipped(); else if (context.Result.Skipped)
{
Logger.AuthorizationCodeReceivedContextSkipped();
}
} }
return authorizationCodeReceivedContext; return context;
} }
private async Task<TokenResponseReceivedContext> RunTokenResponseReceivedEventAsync( private async Task<TokenResponseReceivedContext> RunTokenResponseReceivedEventAsync(
OpenIdConnectMessage message, OpenIdConnectMessage message,
OpenIdConnectMessage tokenEndpointResponse, OpenIdConnectMessage tokenEndpointResponse,
AuthenticationProperties properties, ClaimsPrincipal user,
AuthenticationTicket ticket) AuthenticationProperties properties)
{ {
Logger.TokenResponseReceived(); Logger.TokenResponseReceived();
var eventContext = new TokenResponseReceivedContext(Context, Scheme, Options, properties) var context = new TokenResponseReceivedContext(Context, Scheme, Options, user, properties)
{ {
ProtocolMessage = message, ProtocolMessage = message,
TokenEndpointResponse = tokenEndpointResponse, TokenEndpointResponse = tokenEndpointResponse,
Ticket = ticket
}; };
await Events.TokenResponseReceived(eventContext); await Events.TokenResponseReceived(context);
if (eventContext.HandledResponse) if (context.Result != null)
{ {
Logger.TokenResponseReceivedHandledResponse(); if (context.Result.Handled)
} {
else if (eventContext.Skipped) Logger.TokenResponseReceivedHandledResponse();
{ }
Logger.TokenResponseReceivedSkipped(); else if (context.Result.Skipped)
{
Logger.TokenResponseReceivedSkipped();
}
} }
return eventContext; return context;
} }
private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(AuthenticationTicket ticket, OpenIdConnectMessage message, JObject user) private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(ClaimsPrincipal principal, AuthenticationProperties properties, OpenIdConnectMessage message, JObject user)
{ {
Logger.UserInformationReceived(user.ToString()); Logger.UserInformationReceived(user.ToString());
var userInformationReceivedContext = new UserInformationReceivedContext(Context, Scheme, Options) var context = new UserInformationReceivedContext(Context, Scheme, Options, principal, properties)
{ {
Ticket = ticket,
ProtocolMessage = message, ProtocolMessage = message,
User = user, User = user,
}; };
await Events.UserInformationReceived(userInformationReceivedContext); await Events.UserInformationReceived(context);
if (userInformationReceivedContext.HandledResponse) if (context.Result != null)
{ {
Logger.UserInformationReceivedHandledResponse(); if (context.Result.Handled)
} {
else if (userInformationReceivedContext.Skipped) Logger.UserInformationReceivedHandledResponse();
{ }
Logger.UserInformationReceivedSkipped(); else if (context.Result.Skipped)
{
Logger.UserInformationReceivedSkipped();
}
} }
return userInformationReceivedContext; return context;
} }
private async Task<AuthenticationFailedContext> RunAuthenticationFailedEventAsync(OpenIdConnectMessage message, Exception exception) private async Task<AuthenticationFailedContext> RunAuthenticationFailedEventAsync(OpenIdConnectMessage message, Exception exception)
{ {
var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options) var context = new AuthenticationFailedContext(Context, Scheme, Options)
{ {
ProtocolMessage = message, ProtocolMessage = message,
Exception = exception Exception = exception
}; };
await Events.AuthenticationFailed(authenticationFailedContext); await Events.AuthenticationFailed(context);
if (authenticationFailedContext.HandledResponse) if (context.Result != null)
{ {
Logger.AuthenticationFailedContextHandledResponse(); if (context.Result.Handled)
} {
else if (authenticationFailedContext.Skipped) Logger.AuthenticationFailedContextHandledResponse();
{ }
Logger.AuthenticationFailedContextSkipped(); else if (context.Result.Skipped)
{
Logger.AuthenticationFailedContextSkipped();
}
} }
return authenticationFailedContext; return context;
} }
private AuthenticationTicket ValidateToken(string idToken, AuthenticationProperties properties, TokenValidationParameters validationParameters, out JwtSecurityToken jwt) // Note this modifies properties if Options.UseTokenLifetime
private ClaimsPrincipal ValidateToken(string idToken, AuthenticationProperties properties, TokenValidationParameters validationParameters, out JwtSecurityToken jwt)
{ {
if (!Options.SecurityTokenValidator.CanReadToken(idToken)) if (!Options.SecurityTokenValidator.CanReadToken(idToken))
{ {
@ -1173,24 +1185,22 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.UnableToValidateToken, idToken)); throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.UnableToValidateToken, idToken));
} }
var ticket = new AuthenticationTicket(principal, properties, Scheme.Name);
if (Options.UseTokenLifetime) if (Options.UseTokenLifetime)
{ {
var issued = validatedToken.ValidFrom; var issued = validatedToken.ValidFrom;
if (issued != DateTime.MinValue) if (issued != DateTime.MinValue)
{ {
ticket.Properties.IssuedUtc = issued; properties.IssuedUtc = issued;
} }
var expires = validatedToken.ValidTo; var expires = validatedToken.ValidTo;
if (expires != DateTime.MinValue) if (expires != DateTime.MinValue)
{ {
ticket.Properties.ExpiresUtc = expires; properties.ExpiresUtc = expires;
} }
} }
return ticket; return principal;
} }
/// <summary> /// <summary>

View File

@ -4,13 +4,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;

View File

@ -19,99 +19,45 @@
"TypeId": "public interface Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", "TypeId": "public interface Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents",
"Kind": "Removal" "Kind": "Removal"
}, },
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthenticationFailedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public Microsoft.AspNetCore.Builder.OpenIdConnectOptions get_Options()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.UserInformationReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options, Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage message)",
"Kind": "Removal"
},
{ {
"TypeId": "public static class Microsoft.AspNetCore.Builder.OpenIdConnectAppBuilderExtensions", "TypeId": "public static class Microsoft.AspNetCore.Builder.OpenIdConnectAppBuilderExtensions",
"MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseOpenIdConnectAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)", "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseOpenIdConnectAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)",
"Kind": "Removal" "Kind": "Removal"
} },
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthenticationFailedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.UserInformationReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext",
"Kind": "Removal"
}
] ]

View File

@ -1,30 +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.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Twitter
{
/// <summary>
/// Base class for other Twitter contexts.
/// </summary>
public class BaseTwitterContext : BaseAuthenticationContext
{
/// <summary>
/// Initializes a <see cref="BaseTwitterContext"/>
/// </summary>
/// <param name="context">The HTTP environment</param>
/// <param name="scheme">The scheme data</param>
/// <param name="options">The options for Twitter</param>
/// <param name="properties">The AuthenticationProperties</param>
public BaseTwitterContext(HttpContext context, AuthenticationScheme scheme, TwitterOptions options, AuthenticationProperties properties)
: base(context, scheme.Name, properties)
{
Options = options;
}
public TwitterOptions Options { get; }
public AuthenticationScheme Scheme { get; }
}
}

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// <summary> /// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>. /// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary> /// </summary>
public class TwitterCreatingTicketContext : BaseTwitterContext public class TwitterCreatingTicketContext : ResultContext<TwitterOptions>
{ {
/// <summary> /// <summary>
/// Initializes a <see cref="TwitterCreatingTicketContext"/> /// Initializes a <see cref="TwitterCreatingTicketContext"/>
@ -19,29 +19,33 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// <param name="context">The HTTP environment</param> /// <param name="context">The HTTP environment</param>
/// <param name="scheme">The scheme data</param> /// <param name="scheme">The scheme data</param>
/// <param name="options">The options for Twitter</param> /// <param name="options">The options for Twitter</param>
/// <param name="principal">The <see cref="ClaimsPrincipal"/>.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="userId">Twitter user ID</param> /// <param name="userId">Twitter user ID</param>
/// <param name="screenName">Twitter screen name</param> /// <param name="screenName">Twitter screen name</param>
/// <param name="accessToken">Twitter access token</param> /// <param name="accessToken">Twitter access token</param>
/// <param name="accessTokenSecret">Twitter access token secret</param> /// <param name="accessTokenSecret">Twitter access token secret</param>
/// <param name="user">User details</param> /// <param name="user">User details</param>
/// <param name="properties">AuthenticationProperties.</param>
public TwitterCreatingTicketContext( public TwitterCreatingTicketContext(
HttpContext context, HttpContext context,
AuthenticationScheme scheme, AuthenticationScheme scheme,
TwitterOptions options, TwitterOptions options,
ClaimsPrincipal principal,
AuthenticationProperties properties, AuthenticationProperties properties,
string userId, string userId,
string screenName, string screenName,
string accessToken, string accessToken,
string accessTokenSecret, string accessTokenSecret,
JObject user) JObject user)
: base(context, scheme, options, properties) : base(context, scheme, options)
{ {
UserId = userId; UserId = userId;
ScreenName = screenName; ScreenName = screenName;
AccessToken = accessToken; AccessToken = accessToken;
AccessTokenSecret = accessTokenSecret; AccessTokenSecret = accessTokenSecret;
User = user ?? new JObject(); User = user ?? new JObject();
Principal = principal;
Properties = properties;
} }
/// <summary> /// <summary>
@ -69,10 +73,5 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// <see cref="JObject"/> if it is not available. /// <see cref="JObject"/> if it is not available.
/// </summary> /// </summary>
public JObject User { get; } public JObject User { get; }
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> representing the user
/// </summary>
public ClaimsPrincipal Principal { get; set; }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// <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 Func<TwitterRedirectToAuthorizationEndpointContext, Task> OnRedirectToAuthorizationEndpoint { get; set; } = context => public Func<RedirectContext<TwitterOptions>, Task> OnRedirectToAuthorizationEndpoint { get; set; } = context =>
{ {
context.Response.Redirect(context.RedirectUri); context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask; return Task.CompletedTask;
@ -36,6 +36,6 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
/// Called when a Challenge causes a redirect to authorize endpoint in the Twitter handler /// Called when a Challenge causes a redirect to authorize endpoint in the Twitter handler
/// </summary> /// </summary>
/// <param name="context">Contains redirect URI and <see cref="Http.Authentication.AuthenticationProperties"/> of the challenge </param> /// <param name="context">Contains redirect URI and <see cref="Http.Authentication.AuthenticationProperties"/> of the challenge </param>
public virtual Task RedirectToAuthorizationEndpoint(TwitterRedirectToAuthorizationEndpointContext context) => OnRedirectToAuthorizationEndpoint(context); public virtual Task RedirectToAuthorizationEndpoint(RedirectContext<TwitterOptions> context) => OnRedirectToAuthorizationEndpoint(context);
} }
} }

View File

@ -1,35 +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.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.Twitter
{
/// <summary>
/// The Context passed when a Challenge causes a redirect to authorize endpoint in the Twitter handler.
/// </summary>
public class TwitterRedirectToAuthorizationEndpointContext : BaseTwitterContext
{
/// <summary>
/// Creates a new context object.
/// </summary>
/// <param name="context">The HTTP request context.</param>
/// <param name="scheme">The scheme data</param>
/// <param name="options">The Twitter handler options.</param>
/// <param name="properties">The authentication properties of the challenge.</param>
/// <param name="redirectUri">The initial redirect URI.</param>
public TwitterRedirectToAuthorizationEndpointContext(HttpContext context, AuthenticationScheme scheme,
TwitterOptions options, AuthenticationProperties properties, string redirectUri)
: base(context, scheme, options, properties)
{
RedirectUri = redirectUri;
Properties = properties;
}
/// <summary>
/// Gets the URI used for the redirect operation.
/// </summary>
public string RedirectUri { get; private set; }
}
}

View File

@ -2,6 +2,7 @@
// 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 Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Twitter; using Microsoft.AspNetCore.Authentication.Twitter;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -10,6 +11,19 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static class TwitterExtensions public static class TwitterExtensions
{ {
public static AuthenticationBuilder AddTwitter(this AuthenticationBuilder builder)
=> builder.AddTwitter(TwitterDefaults.AuthenticationScheme, _ => { });
public static AuthenticationBuilder AddTwitter(this AuthenticationBuilder builder, Action<TwitterOptions> configureOptions)
=> builder.AddTwitter(TwitterDefaults.AuthenticationScheme, configureOptions);
public static AuthenticationBuilder AddTwitter(this AuthenticationBuilder builder, string authenticationScheme, Action<TwitterOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<TwitterOptions>, TwitterPostConfigureOptions>());
return builder.AddRemoteScheme<TwitterOptions, TwitterHandler>(authenticationScheme, authenticationScheme, configureOptions);
}
// REMOVE below once callers have been updated.
public static IServiceCollection AddTwitterAuthentication(this IServiceCollection services) public static IServiceCollection AddTwitterAuthentication(this IServiceCollection services)
=> services.AddTwitterAuthentication(TwitterDefaults.AuthenticationScheme, _ => { }); => services.AddTwitterAuthentication(TwitterDefaults.AuthenticationScheme, _ => { });

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new TwitterEvents()); protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new TwitterEvents());
protected override async Task<AuthenticateResult> HandleRemoteAuthenticateAsync() protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{ {
AuthenticationProperties properties = null; AuthenticationProperties properties = null;
var query = Request.Query; var query = Request.Query;
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
if (requestToken == null) if (requestToken == null)
{ {
return AuthenticateResult.Fail("Invalid state cookie."); return HandleRequestResult.Fail("Invalid state cookie.");
} }
properties = requestToken.Properties; properties = requestToken.Properties;
@ -66,18 +66,18 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
var returnedToken = query["oauth_token"]; var returnedToken = query["oauth_token"];
if (StringValues.IsNullOrEmpty(returnedToken)) if (StringValues.IsNullOrEmpty(returnedToken))
{ {
return AuthenticateResult.Fail("Missing oauth_token"); return HandleRequestResult.Fail("Missing oauth_token");
} }
if (!string.Equals(returnedToken, requestToken.Token, StringComparison.Ordinal)) if (!string.Equals(returnedToken, requestToken.Token, StringComparison.Ordinal))
{ {
return AuthenticateResult.Fail("Unmatched token"); return HandleRequestResult.Fail("Unmatched token");
} }
var oauthVerifier = query["oauth_verifier"]; var oauthVerifier = query["oauth_verifier"];
if (StringValues.IsNullOrEmpty(oauthVerifier)) if (StringValues.IsNullOrEmpty(oauthVerifier))
{ {
return AuthenticateResult.Fail("Missing or blank oauth_verifier"); return HandleRequestResult.Fail("Missing or blank oauth_verifier");
} }
var cookieOptions = new CookieOptions var cookieOptions = new CookieOptions
@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
}); });
} }
return AuthenticateResult.Success(await CreateTicketAsync(identity, properties, accessToken, user)); return HandleRequestResult.Success(await CreateTicketAsync(identity, properties, accessToken, user));
} }
protected virtual async Task<AuthenticationTicket> CreateTicketAsync( protected virtual async Task<AuthenticationTicket> CreateTicketAsync(
@ -127,18 +127,9 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
action.Run(user, identity, ClaimsIssuer); action.Run(user, identity, ClaimsIssuer);
} }
var context = new TwitterCreatingTicketContext(Context, Scheme, Options, properties, token.UserId, token.ScreenName, token.Token, token.TokenSecret, user) var context = new TwitterCreatingTicketContext(Context, Scheme, Options, new ClaimsPrincipal(identity), properties, token.UserId, token.ScreenName, token.Token, token.TokenSecret, user);
{
Principal = new ClaimsPrincipal(identity)
};
await Events.CreatingTicket(context); await Events.CreatingTicket(context);
if (context.Principal?.Identity == null)
{
return null;
}
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
} }
@ -165,7 +156,7 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(requestToken), cookieOptions); Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(requestToken), cookieOptions);
var redirectContext = new TwitterRedirectToAuthorizationEndpointContext(Context, Scheme, Options, properties, twitterAuthenticationEndpoint); var redirectContext = new RedirectContext<TwitterOptions>(Context, Scheme, Options, properties, twitterAuthenticationEndpoint);
await Events.RedirectToAuthorizationEndpoint(redirectContext); await Events.RedirectToAuthorizationEndpoint(redirectContext);
} }

View File

@ -19,31 +19,6 @@
"TypeId": "public interface Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", "TypeId": "public interface Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents",
"Kind": "Removal" "Kind": "Removal"
}, },
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.TwitterOptions options, Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties properties, System.String redirectUri)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.TwitterOptions options, System.String userId, System.String screenName, System.String accessToken, System.String accessTokenSecret, Newtonsoft.Json.Linq.JObject user)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.RequestToken", "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.RequestToken",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()", "MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
@ -58,5 +33,13 @@
"TypeId": "public static class Microsoft.AspNetCore.Builder.TwitterAppBuilderExtensions", "TypeId": "public static class Microsoft.AspNetCore.Builder.TwitterAppBuilderExtensions",
"MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseTwitterAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.TwitterOptions options)", "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseTwitterAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.TwitterOptions options)",
"Kind": "Removal" "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext",
"Kind": "Removal"
} }
] ]

View File

@ -0,0 +1,103 @@
// 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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Used to configure authentication
/// </summary>
public class AuthenticationBuilder
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="services">The services being configured.</param>
public AuthenticationBuilder(IServiceCollection services)
=> Services = services;
/// <summary>
/// The services being configured.
/// </summary>
public virtual IServiceCollection Services { get; }
/// <summary>
/// Adds a <see cref="AuthenticationScheme"/> which can be used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <typeparam name="TOptions">The <see cref="AuthenticationSchemeOptions"/> type to configure the handler."/>.</typeparam>
/// <typeparam name="THandler">The <see cref="AuthenticationHandler{TOptions}"/> used to handle this scheme.</typeparam>
/// <param name="authenticationScheme">The name of this scheme.</param>
/// <param name="displayName">The display name of this scheme.</param>
/// <param name="configureOptions">Used to configure the scheme options.</param>
/// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : AuthenticationSchemeOptions, new()
where THandler : AuthenticationHandler<TOptions>
{
Services.Configure<AuthenticationOptions>(o =>
{
o.AddScheme(authenticationScheme, scheme => {
scheme.HandlerType = typeof(THandler);
scheme.DisplayName = displayName;
});
});
if (configureOptions != null)
{
Services.Configure(authenticationScheme, configureOptions);
}
Services.AddTransient<THandler>();
return this;
}
/// <summary>
/// Adds a <see cref="AuthenticationScheme"/> which can be used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <typeparam name="TOptions">The <see cref="AuthenticationSchemeOptions"/> type to configure the handler."/>.</typeparam>
/// <typeparam name="THandler">The <see cref="AuthenticationHandler{TOptions}"/> used to handle this scheme.</typeparam>
/// <param name="authenticationScheme">The name of this scheme.</param>
/// <param name="configureOptions">Used to configure the scheme options.</param>
/// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, Action<TOptions> configureOptions)
where TOptions : AuthenticationSchemeOptions, new()
where THandler : AuthenticationHandler<TOptions>
=> AddScheme<TOptions, THandler>(authenticationScheme, displayName: null, configureOptions: configureOptions);
/// <summary>
/// Adds a <see cref="RemoteAuthenticationHandler{TOptions}"/> based <see cref="AuthenticationScheme"/> that supports remote authentication
/// which can be used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <typeparam name="TOptions">The <see cref="RemoteAuthenticationOptions"/> type to configure the handler."/>.</typeparam>
/// <typeparam name="THandler">The <see cref="RemoteAuthenticationHandler{TOptions}"/> used to handle this scheme.</typeparam>
/// <param name="authenticationScheme">The name of this scheme.</param>
/// <param name="displayName">The display name of this scheme.</param>
/// <param name="configureOptions">Used to configure the scheme options.</param>
/// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddRemoteScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : RemoteAuthenticationOptions, new()
where THandler : RemoteAuthenticationHandler<TOptions>
{
Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<TOptions>, EnsureSignInScheme<TOptions>>());
return AddScheme<TOptions, THandler>(authenticationScheme, displayName, configureOptions: configureOptions);
}
// Used to ensure that there's always a default data protection provider
private class EnsureSignInScheme<TOptions> : IPostConfigureOptions<TOptions> where TOptions : RemoteAuthenticationOptions
{
private readonly AuthenticationOptions _authOptions;
public EnsureSignInScheme(IOptions<AuthenticationOptions> authOptions)
{
_authOptions = authOptions.Value;
}
public void PostConfigure(string name, TOptions options)
{
options.SignInScheme = options.SignInScheme ?? _authOptions.DefaultSignInScheme;
}
}
}
}

View File

@ -178,35 +178,6 @@ namespace Microsoft.AspNetCore.Authentication
protected abstract Task<AuthenticateResult> HandleAuthenticateAsync(); protected abstract Task<AuthenticateResult> HandleAuthenticateAsync();
public async Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
properties = properties ?? new AuthenticationProperties();
await HandleSignInAsync(user, properties);
Logger.AuthenticationSchemeSignedIn(Scheme.Name);
}
protected virtual Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
return Task.CompletedTask;
}
public async Task SignOutAsync(AuthenticationProperties properties)
{
properties = properties ?? new AuthenticationProperties();
await HandleSignOutAsync(properties);
Logger.AuthenticationSchemeSignedOut(Scheme.Name);
}
protected virtual Task HandleSignOutAsync(AuthenticationProperties properties)
{
return Task.CompletedTask;
}
/// <summary> /// <summary>
/// Override this method to handle Forbid. /// Override this method to handle Forbid.
/// </summary> /// </summary>

View File

@ -13,7 +13,7 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary> /// </summary>
public static class AuthenticationServiceCollectionExtensions public static class AuthenticationServiceCollectionExtensions
{ {
public static IServiceCollection AddAuthentication(this IServiceCollection services) public static AuthenticationBuilder AddAuthentication(this IServiceCollection services)
{ {
if (services == null) if (services == null)
{ {
@ -24,10 +24,10 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddDataProtection(); services.AddDataProtection();
services.AddWebEncoders(); services.AddWebEncoders();
services.TryAddSingleton<ISystemClock, SystemClock>(); services.TryAddSingleton<ISystemClock, SystemClock>();
return services; return new AuthenticationBuilder(services);
} }
public static IServiceCollection AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) { public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {
if (services == null) if (services == null)
{ {
throw new ArgumentNullException(nameof(services)); throw new ArgumentNullException(nameof(services));
@ -38,11 +38,12 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(configureOptions)); throw new ArgumentNullException(nameof(configureOptions));
} }
services.AddAuthentication(); var builder = services.AddAuthentication();
services.Configure(configureOptions); services.Configure(configureOptions);
return services; return builder;
} }
// REMOVE below once callers have been updated
public static IServiceCollection AddScheme<TOptions, THandler>(this IServiceCollection services, string authenticationScheme, string displayName, Action<AuthenticationSchemeBuilder> configureScheme, Action<TOptions> configureOptions) public static IServiceCollection AddScheme<TOptions, THandler>(this IServiceCollection services, string authenticationScheme, string displayName, Action<AuthenticationSchemeBuilder> configureScheme, Action<TOptions> configureOptions)
where TOptions : AuthenticationSchemeOptions, new() where TOptions : AuthenticationSchemeOptions, new()
where THandler : AuthenticationHandler<TOptions> where THandler : AuthenticationHandler<TOptions>

View File

@ -1,41 +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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Base context for authentication.
/// </summary>
public abstract class BaseAuthenticationContext : BaseContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
/// <param name="properties">The properties.</param>
protected BaseAuthenticationContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties) : base(context)
{
if (string.IsNullOrEmpty(authenticationScheme))
{
throw new ArgumentException(nameof(authenticationScheme));
}
AuthenticationScheme = authenticationScheme;
Properties = properties ?? new AuthenticationProperties();
}
/// <summary>
/// The name of the scheme.
/// </summary>
public string AuthenticationScheme { get; }
/// <summary>
/// Contains the extra meta-data arriving with the authentication. May be altered.
/// </summary>
public AuthenticationProperties Properties { get; protected set; }
}
}

View File

@ -9,22 +9,44 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary> /// <summary>
/// Base class used by other context classes. /// Base class used by other context classes.
/// </summary> /// </summary>
public abstract class BaseContext public abstract class BaseContext<TOptions> where TOptions : AuthenticationSchemeOptions
{ {
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
/// <param name="context">The request context.</param> /// <param name="context">The context.</param>
protected BaseContext(HttpContext context) /// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The authentication options associated with the scheme.</param>
protected BaseContext(HttpContext context, AuthenticationScheme scheme, TOptions options)
{ {
if (context == null) if (context == null)
{ {
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
HttpContext = context; HttpContext = context;
Scheme = scheme;
Options = options;
} }
/// <summary>
/// The authentication scheme.
/// </summary>
public AuthenticationScheme Scheme { get; }
/// <summary>
/// Gets the authentication options associated with the scheme.
/// </summary>
public TOptions Options { get; }
/// <summary> /// <summary>
/// The context. /// The context.
/// </summary> /// </summary>
@ -33,17 +55,11 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary> /// <summary>
/// The request. /// The request.
/// </summary> /// </summary>
public HttpRequest Request public HttpRequest Request => HttpContext.Request;
{
get { return HttpContext.Request; }
}
/// <summary> /// <summary>
/// The response. /// The response.
/// </summary> /// </summary>
public HttpResponse Response public HttpResponse Response => HttpContext.Response;
{
get { return HttpContext.Response; }
}
} }
} }

View File

@ -1,78 +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.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
public class BaseControlContext : BaseContext
{
protected BaseControlContext(HttpContext context) : base(context)
{
}
public EventResultState State { get; set; }
public bool HandledResponse
{
get { return State == EventResultState.HandledResponse; }
}
public bool Skipped
{
get { return State == EventResultState.Skipped; }
}
/// <summary>
/// Discontinue all processing for this request and return to the client.
/// The caller is responsible for generating the full response.
/// Set the <see cref="Ticket"/> to trigger SignIn.
/// </summary>
public void HandleResponse()
{
State = EventResultState.HandledResponse;
}
/// <summary>
/// Discontinue processing the request in the current handler.
/// SignIn will not be called.
/// </summary>
public void Skip()
{
State = EventResultState.Skipped;
}
/// <summary>
/// Gets or set the <see cref="Ticket"/> to return if this event signals it handled the event.
/// </summary>
public AuthenticationTicket Ticket { get; set; }
/// <summary>
/// Returns true if the handler should be done processing.
/// </summary>
/// <param name="result">The result.</param>
/// <returns>Whether the handler should be done processing.</returns>
public bool IsProcessingComplete(out AuthenticateResult result)
{
if (HandledResponse)
{
if (Ticket == null)
{
result = AuthenticateResult.Handle();
}
else
{
result = AuthenticateResult.Success(Ticket);
}
return true;
}
else if (Skipped)
{
result = AuthenticateResult.None();
return true;
}
result = null;
return false;
}
}
}

View File

@ -1,23 +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.AspNetCore.Authentication
{
public enum EventResultState
{
/// <summary>
/// Continue with normal processing.
/// </summary>
Continue,
/// <summary>
/// Discontinue processing the request.
/// </summary>
Skipped,
/// <summary>
/// Discontinue all processing for this request.
/// </summary>
HandledResponse
}
}

View File

@ -0,0 +1,32 @@
// 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.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
public class HandleRequestContext<TOptions> : BaseContext<TOptions> where TOptions : AuthenticationSchemeOptions
{
protected HandleRequestContext(
HttpContext context,
AuthenticationScheme scheme,
TOptions options)
: base(context, scheme, options) { }
/// <summary>
/// The <see cref="HandleRequestResult"/> which is used by the handler.
/// </summary>
public HandleRequestResult Result { get; protected set; }
/// <summary>
/// Discontinue all processing for this request and return to the client.
/// The caller is responsible for generating the full response.
/// </summary>
public void HandleResponse() => Result = HandleRequestResult.Handle();
/// <summary>
/// Discontinue processing the request in the current handler.
/// </summary>
public void SkipHandler() => Result = HandleRequestResult.SkipHandler();
}
}

View File

@ -0,0 +1,30 @@
// 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.Security.Claims;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Base context for authentication events which deal with a ClaimsPrincipal.
/// </summary>
public abstract class PrincipalContext<TOptions> : PropertiesContext<TOptions> where TOptions : AuthenticationSchemeOptions
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The authentication options associated with the scheme.</param>
/// <param name="properties">The authentication properties.</param>
protected PrincipalContext(HttpContext context, AuthenticationScheme scheme, TOptions options, AuthenticationProperties properties)
: base(context, scheme, options, properties) { }
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> containing the user claims.
/// </summary>
public virtual ClaimsPrincipal Principal { get; set; }
}
}

View File

@ -0,0 +1,31 @@
// 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.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Base context for authentication events which contain <see cref="AuthenticationProperties"/>.
/// </summary>
public abstract class PropertiesContext<TOptions> : BaseContext<TOptions> where TOptions : AuthenticationSchemeOptions
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The authentication options associated with the scheme.</param>
/// <param name="properties">The authentication properties.</param>
protected PropertiesContext(HttpContext context, AuthenticationScheme scheme, TOptions options, AuthenticationProperties properties)
: base(context, scheme, options)
{
Properties = properties ?? new AuthenticationProperties();
}
/// <summary>
/// Gets or sets the <see cref="AuthenticationProperties"/>.
/// </summary>
public virtual AuthenticationProperties Properties { get; protected set; }
}
}

View File

@ -1,28 +1,32 @@
// 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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
namespace Microsoft.AspNetCore.Authentication.Cookies namespace Microsoft.AspNetCore.Authentication
{ {
/// <summary> /// <summary>
/// Context passed when a Challenge, SignIn, or SignOut causes a redirect in the cookie handler /// Context passed for redirect events.
/// </summary> /// </summary>
public class CookieRedirectContext : BaseCookieContext public class RedirectContext<TOptions> : PropertiesContext<TOptions> where TOptions : AuthenticationSchemeOptions
{ {
/// <summary> /// <summary>
/// Creates a new context object. /// Creates a new context object.
/// </summary> /// </summary>
/// <param name="context">The HTTP request context</param> /// <param name="context">The HTTP request context</param>
/// <param name="scheme">The scheme data</param> /// <param name="scheme">The scheme data</param>
/// <param name="options">The cookie handler options</param> /// <param name="options">The handler options</param>
/// <param name="redirectUri">The initial redirect URI</param> /// <param name="redirectUri">The initial redirect URI</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
public CookieRedirectContext(HttpContext context, AuthenticationScheme scheme, CookieAuthenticationOptions options, string redirectUri, AuthenticationProperties properties) public RedirectContext(
HttpContext context,
AuthenticationScheme scheme,
TOptions options,
AuthenticationProperties properties,
string redirectUri)
: base(context, scheme, options, properties) : base(context, scheme, options, properties)
{ {
Properties = properties;
RedirectUri = redirectUri; RedirectUri = redirectUri;
} }

View File

@ -0,0 +1,49 @@
// 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.Security.Claims;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Base context for remote authentication.
/// </summary>
public abstract class RemoteAuthenticationContext<TOptions> : HandleRequestContext<TOptions> where TOptions : AuthenticationSchemeOptions
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The authentication options associated with the scheme.</param>
/// <param name="properties">The authentication properties.</param>
protected RemoteAuthenticationContext(
HttpContext context,
AuthenticationScheme scheme,
TOptions options,
AuthenticationProperties properties)
: base(context, scheme, options)
=> Properties = properties ?? new AuthenticationProperties();
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> containing the user claims.
/// </summary>
public ClaimsPrincipal Principal { get; set; }
/// <summary>
/// Gets or sets the <see cref="AuthenticationProperties"/>.
/// </summary>
public virtual AuthenticationProperties Properties { get; set; }
/// <summary>
/// Calls success creating a ticket with the <see cref="Principal"/> and <see cref="Properties"/>.
/// </summary>
public void Success() => Result = HandleRequestResult.Success(new AuthenticationTicket(Principal, Properties, Scheme.Name));
public void Fail(Exception failure) => Result = HandleRequestResult.Fail(failure);
public void Fail(string failureMessage) => Result = HandleRequestResult.Fail(failureMessage);
}
}

View File

@ -8,14 +8,14 @@ namespace Microsoft.AspNetCore.Authentication
{ {
public class RemoteAuthenticationEvents public class RemoteAuthenticationEvents
{ {
public Func<FailureContext, Task> OnRemoteFailure { get; set; } = context => Task.CompletedTask; public Func<RemoteFailureContext, Task> OnRemoteFailure { get; set; } = context => Task.CompletedTask;
public Func<TicketReceivedContext, Task> OnTicketReceived { get; set; } = context => Task.CompletedTask; public Func<TicketReceivedContext, Task> OnTicketReceived { get; set; } = context => Task.CompletedTask;
/// <summary> /// <summary>
/// Invoked when there is a remote failure /// Invoked when there is a remote failure
/// </summary> /// </summary>
public virtual Task RemoteFailure(FailureContext context) => OnRemoteFailure(context); public virtual Task RemoteFailure(RemoteFailureContext context) => OnRemoteFailure(context);
/// <summary> /// <summary>
/// Invoked after the remote ticket has been received. /// Invoked after the remote ticket has been received.

View File

@ -9,10 +9,14 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary> /// <summary>
/// Provides failure context information to handler providers. /// Provides failure context information to handler providers.
/// </summary> /// </summary>
public class FailureContext : BaseControlContext public class RemoteFailureContext : HandleRequestContext<RemoteAuthenticationOptions>
{ {
public FailureContext(HttpContext context, Exception failure) public RemoteFailureContext(
: base(context) HttpContext context,
AuthenticationScheme scheme,
RemoteAuthenticationOptions options,
Exception failure)
: base(context, scheme, options)
{ {
Failure = failure; Failure = failure;
} }

View File

@ -0,0 +1,65 @@
// 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.Security.Claims;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Base context for events that produce AuthenticateResults.
/// </summary>
public abstract class ResultContext<TOptions> : BaseContext<TOptions> where TOptions : AuthenticationSchemeOptions
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="scheme">The authentication scheme.</param>
/// <param name="options">The authentication options associated with the scheme.</param>
protected ResultContext(HttpContext context, AuthenticationScheme scheme, TOptions options)
: base(context, scheme, options) { }
/// <summary>
/// Gets or sets the <see cref="ClaimsPrincipal"/> containing the user claims.
/// </summary>
public ClaimsPrincipal Principal { get; set; }
private AuthenticationProperties _properties;
/// <summary>
/// Gets or sets the <see cref="AuthenticationProperties"/>.
/// </summary>
public AuthenticationProperties Properties {
get => _properties ?? (_properties = new AuthenticationProperties());
set => _properties = value;
}
/// <summary>
/// Gets the <see cref="AuthenticateResult"/> result.
/// </summary>
public AuthenticateResult Result { get; private set; }
/// <summary>
/// Calls success creating a ticket with the <see cref="Principal"/> and <see cref="Properties"/>.
/// </summary>
public void Success() => Result = HandleRequestResult.Success(new AuthenticationTicket(Principal, Properties, Scheme.Name));
/// <summary>
/// Indicates that there was no information returned for this authentication scheme.
/// </summary>
public void NoResult() => Result = AuthenticateResult.NoResult();
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure"></param>
public void Fail(Exception failure) => Result = AuthenticateResult.Fail(failure);
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage"></param>
public void Fail(string failureMessage) => Result = AuthenticateResult.Fail(failureMessage);
}
}

View File

@ -3,30 +3,21 @@
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
namespace Microsoft.AspNetCore.Authentication namespace Microsoft.AspNetCore.Authentication
{ {
/// <summary> /// <summary>
/// Provides context information to handler providers. /// Provides context information to handler providers.
/// </summary> /// </summary>
public class TicketReceivedContext : BaseControlContext public class TicketReceivedContext : RemoteAuthenticationContext<RemoteAuthenticationOptions>
{ {
public TicketReceivedContext(HttpContext context, RemoteAuthenticationOptions options, AuthenticationTicket ticket) public TicketReceivedContext(
: base(context) HttpContext context,
{ AuthenticationScheme scheme,
Options = options; RemoteAuthenticationOptions options,
Ticket = ticket; AuthenticationTicket ticket)
if (ticket != null) : base(context, scheme, options, ticket?.Properties)
{ => Principal = ticket?.Principal;
Principal = ticket.Principal;
Properties = ticket.Properties;
}
}
public ClaimsPrincipal Principal { get; set; }
public AuthenticationProperties Properties { get; set; }
public RemoteAuthenticationOptions Options { get; set; }
public string ReturnUri { get; set; } public string ReturnUri { get; set; }
} }

View File

@ -9,14 +9,8 @@ namespace Microsoft.Extensions.Logging
{ {
private static Action<ILogger, string, Exception> _authSchemeAuthenticated; private static Action<ILogger, string, Exception> _authSchemeAuthenticated;
private static Action<ILogger, string, Exception> _authSchemeNotAuthenticated; private static Action<ILogger, string, Exception> _authSchemeNotAuthenticated;
private static Action<ILogger, string, string, Exception> _authSchemeNotAuthenticatedWithFailure;
private static Action<ILogger, string, Exception> _authSchemeSignedIn;
private static Action<ILogger, string, Exception> _authSchemeSignedOut;
private static Action<ILogger, string, Exception> _authSchemeChallenged; private static Action<ILogger, string, Exception> _authSchemeChallenged;
private static Action<ILogger, string, Exception> _authSchemeForbidden; private static Action<ILogger, string, Exception> _authSchemeForbidden;
private static Action<ILogger, string, Exception> _userAuthorizationFailed;
private static Action<ILogger, string, Exception> _userAuthorizationSucceeded;
private static Action<ILogger, string, Exception> _userPrincipalMerged;
private static Action<ILogger, string, Exception> _remoteAuthenticationError; private static Action<ILogger, string, Exception> _remoteAuthenticationError;
private static Action<ILogger, Exception> _signInHandled; private static Action<ILogger, Exception> _signInHandled;
private static Action<ILogger, Exception> _signInSkipped; private static Action<ILogger, Exception> _signInSkipped;
@ -26,18 +20,6 @@ namespace Microsoft.Extensions.Logging
static LoggingExtensions() static LoggingExtensions()
{ {
_userAuthorizationSucceeded = LoggerMessage.Define<string>(
eventId: 1,
logLevel: LogLevel.Information,
formatString: "Authorization was successful for user: {UserName}.");
_userAuthorizationFailed = LoggerMessage.Define<string>(
eventId: 2,
logLevel: LogLevel.Information,
formatString: "Authorization failed for user: {UserName}.");
_userPrincipalMerged = LoggerMessage.Define<string>(
eventId: 3,
logLevel: LogLevel.Information,
formatString: "HttpContext.User merged via AutomaticAuthentication from authenticationScheme: {AuthenticationScheme}.");
_remoteAuthenticationError = LoggerMessage.Define<string>( _remoteAuthenticationError = LoggerMessage.Define<string>(
eventId: 4, eventId: 4,
logLevel: LogLevel.Information, logLevel: LogLevel.Information,
@ -50,10 +32,6 @@ namespace Microsoft.Extensions.Logging
eventId: 6, eventId: 6,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "The SigningIn event returned Skipped."); formatString: "The SigningIn event returned Skipped.");
_authSchemeNotAuthenticatedWithFailure = LoggerMessage.Define<string, string>(
eventId: 7,
logLevel: LogLevel.Information,
formatString: "{AuthenticationScheme} was not authenticated. Failure message: {FailureMessage}");
_authSchemeAuthenticated = LoggerMessage.Define<string>( _authSchemeAuthenticated = LoggerMessage.Define<string>(
eventId: 8, eventId: 8,
logLevel: LogLevel.Information, logLevel: LogLevel.Information,
@ -62,14 +40,6 @@ namespace Microsoft.Extensions.Logging
eventId: 9, eventId: 9,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "AuthenticationScheme: {AuthenticationScheme} was not authenticated."); formatString: "AuthenticationScheme: {AuthenticationScheme} was not authenticated.");
_authSchemeSignedIn = LoggerMessage.Define<string>(
eventId: 10,
logLevel: LogLevel.Information,
formatString: "AuthenticationScheme: {AuthenticationScheme} signed in.");
_authSchemeSignedOut = LoggerMessage.Define<string>(
eventId: 11,
logLevel: LogLevel.Information,
formatString: "AuthenticationScheme: {AuthenticationScheme} signed out.");
_authSchemeChallenged = LoggerMessage.Define<string>( _authSchemeChallenged = LoggerMessage.Define<string>(
eventId: 12, eventId: 12,
logLevel: LogLevel.Information, logLevel: LogLevel.Information,
@ -102,21 +72,6 @@ namespace Microsoft.Extensions.Logging
_authSchemeNotAuthenticated(logger, authenticationScheme, null); _authSchemeNotAuthenticated(logger, authenticationScheme, null);
} }
public static void AuthenticationSchemeNotAuthenticatedWithFailure(this ILogger logger, string authenticationScheme, string failureMessage)
{
_authSchemeNotAuthenticatedWithFailure(logger, authenticationScheme, failureMessage, null);
}
public static void AuthenticationSchemeSignedIn(this ILogger logger, string authenticationScheme)
{
_authSchemeSignedIn(logger, authenticationScheme, null);
}
public static void AuthenticationSchemeSignedOut(this ILogger logger, string authenticationScheme)
{
_authSchemeSignedOut(logger, authenticationScheme, null);
}
public static void AuthenticationSchemeChallenged(this ILogger logger, string authenticationScheme) public static void AuthenticationSchemeChallenged(this ILogger logger, string authenticationScheme)
{ {
_authSchemeChallenged(logger, authenticationScheme, null); _authSchemeChallenged(logger, authenticationScheme, null);
@ -127,21 +82,6 @@ namespace Microsoft.Extensions.Logging
_authSchemeForbidden(logger, authenticationScheme, null); _authSchemeForbidden(logger, authenticationScheme, null);
} }
public static void UserAuthorizationSucceeded(this ILogger logger, string userName)
{
_userAuthorizationSucceeded(logger, userName, null);
}
public static void UserAuthorizationFailed(this ILogger logger, string userName)
{
_userAuthorizationFailed(logger, userName, null);
}
public static void UserPrinicpalMerged(this ILogger logger, string authenticationScheme)
{
_userPrincipalMerged(logger, authenticationScheme, null);
}
public static void RemoteAuthenticationError(this ILogger logger, string errorMessage) public static void RemoteAuthenticationError(this ILogger logger, string errorMessage)
{ {
_remoteAuthenticationError(logger, errorMessage, null); _remoteAuthenticationError(logger, errorMessage, null);

View File

@ -2,7 +2,6 @@
// 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.Security.Cryptography; using System.Security.Cryptography;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -35,19 +34,13 @@ namespace Microsoft.AspNetCore.Authentication
} }
protected RemoteAuthenticationHandler(IOptionsSnapshot<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) protected RemoteAuthenticationHandler(IOptionsSnapshot<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock) : base(options, logger, encoder, clock) { }
{
}
protected override Task<object> CreateEventsAsync() protected override Task<object> CreateEventsAsync()
{ => Task.FromResult<object>(new RemoteAuthenticationEvents());
return Task.FromResult<object>(new RemoteAuthenticationEvents());
}
public virtual Task<bool> ShouldHandleRequestAsync() public virtual Task<bool> ShouldHandleRequestAsync()
{ => Task.FromResult(Options.CallbackPath == Request.Path);
return Task.FromResult(Options.CallbackPath == Request.Path);
}
public virtual async Task<bool> HandleRequestAsync() public virtual async Task<bool> HandleRequestAsync()
{ {
@ -69,7 +62,7 @@ namespace Microsoft.AspNetCore.Authentication
{ {
return true; return true;
} }
else if (authResult.Nothing) else if (authResult.Skipped || authResult.None)
{ {
return false; return false;
} }
@ -89,25 +82,28 @@ namespace Microsoft.AspNetCore.Authentication
if (exception != null) if (exception != null)
{ {
Logger.RemoteAuthenticationError(exception.Message); Logger.RemoteAuthenticationError(exception.Message);
var errorContext = new FailureContext(Context, exception); var errorContext = new RemoteFailureContext(Context, Scheme, Options, exception);
await Events.RemoteFailure(errorContext); await Events.RemoteFailure(errorContext);
if (errorContext.HandledResponse) if (errorContext.Result != null)
{ {
return true; if (errorContext.Result.Handled)
} {
else if (errorContext.Skipped) return true;
{ }
return false; else if (errorContext.Result.Skipped)
{
return false;
}
} }
throw new AggregateException("Unhandled remote failure.", exception); throw exception;
} }
// We have a ticket if we get here // We have a ticket if we get here
var ticketContext = new TicketReceivedContext(Context, Options, ticket) var ticketContext = new TicketReceivedContext(Context, Scheme, Options, ticket)
{ {
ReturnUri = ticket.Properties.RedirectUri, ReturnUri = ticket.Properties.RedirectUri
}; };
// REVIEW: is this safe or good? // REVIEW: is this safe or good?
ticket.Properties.RedirectUri = null; ticket.Properties.RedirectUri = null;
@ -117,15 +113,18 @@ namespace Microsoft.AspNetCore.Authentication
await Events.TicketReceived(ticketContext); await Events.TicketReceived(ticketContext);
if (ticketContext.HandledResponse) if (ticketContext.Result != null)
{ {
Logger.SigninHandled(); if (ticketContext.Result.Handled)
return true; {
} Logger.SigninHandled();
else if (ticketContext.Skipped) return true;
{ }
Logger.SigninSkipped(); else if (ticketContext.Result.Skipped)
return false; {
Logger.SigninSkipped();
return false;
}
} }
await Context.SignInAsync(SignInScheme, ticketContext.Principal, ticketContext.Properties); await Context.SignInAsync(SignInScheme, ticketContext.Principal, ticketContext.Properties);
@ -145,7 +144,7 @@ namespace Microsoft.AspNetCore.Authentication
/// ///
/// The method process the request on the endpoint defined by CallbackPath. /// The method process the request on the endpoint defined by CallbackPath.
/// </summary> /// </summary>
protected abstract Task<AuthenticateResult> HandleRemoteAuthenticateAsync(); protected abstract Task<HandleRequestResult> HandleRemoteAuthenticateAsync();
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{ {
@ -174,20 +173,8 @@ namespace Microsoft.AspNetCore.Authentication
return AuthenticateResult.Fail("Remote authentication does not directly support AuthenticateAsync"); return AuthenticateResult.Fail("Remote authentication does not directly support AuthenticateAsync");
} }
protected override Task HandleSignOutAsync(AuthenticationProperties properties)
{
throw new NotSupportedException();
}
protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
throw new NotSupportedException();
}
protected override Task HandleForbiddenAsync(AuthenticationProperties properties) protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
{ => Context.ForbidAsync(SignInScheme);
return Context.ForbidAsync(SignInScheme);
}
protected virtual void GenerateCorrelationId(AuthenticationProperties properties) protected virtual void GenerateCorrelationId(AuthenticationProperties properties)
{ {

View File

@ -23,11 +23,6 @@ namespace Microsoft.AspNetCore.Authentication
{ {
throw new ArgumentException(Resources.FormatException_OptionMustBeProvided(nameof(CallbackPath)), nameof(CallbackPath)); throw new ArgumentException(Resources.FormatException_OptionMustBeProvided(nameof(CallbackPath)), nameof(CallbackPath));
} }
if (string.IsNullOrEmpty(SignInScheme))
{
throw new ArgumentException(Resources.FormatException_OptionMustBeProvided(nameof(SignInScheme)), nameof(SignInScheme));
}
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,78 @@
// 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;
namespace Microsoft.AspNetCore.Authentication
{
/// <summary>
/// Contains the result of an Authenticate call
/// </summary>
public class HandleRequestResult : AuthenticateResult
{
/// <summary>
/// Indicates that stage of authentication was directly handled by
/// user intervention and no further processing should be attempted.
/// </summary>
public bool Handled { get; private set; }
/// <summary>
/// Indicates that the default authentication logic should be
/// skipped and that the rest of the pipeline should be invoked.
/// </summary>
public bool Skipped { get; private set; }
/// <summary>
/// Indicates that authentication was successful.
/// </summary>
/// <param name="ticket">The ticket representing the authentication result.</param>
/// <returns>The result.</returns>
public static new HandleRequestResult Success(AuthenticationTicket ticket)
{
if (ticket == null)
{
throw new ArgumentNullException(nameof(ticket));
}
return new HandleRequestResult() { Ticket = ticket };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failure">The failure exception.</param>
/// <returns>The result.</returns>
public static new HandleRequestResult Fail(Exception failure)
{
return new HandleRequestResult() { Failure = failure };
}
/// <summary>
/// Indicates that there was a failure during authentication.
/// </summary>
/// <param name="failureMessage">The failure message.</param>
/// <returns>The result.</returns>
public static new HandleRequestResult Fail(string failureMessage)
{
return new HandleRequestResult() { Failure = new Exception(failureMessage) };
}
/// <summary>
/// Discontinue all processing for this request and return to the client.
/// The caller is responsible for generating the full response.
/// </summary>
/// <returns>The result.</returns>
public static HandleRequestResult Handle()
{
return new HandleRequestResult() { Handled = true };
}
/// <summary>
/// Discontinue processing the request in the current handler.
/// </summary>
/// <returns>The result.</returns>
public static HandleRequestResult SkipHandler()
{
return new HandleRequestResult() { Skipped = true };
}
}
}

View File

@ -83,44 +83,34 @@
"TypeId": "public static class Microsoft.AspNetCore.Builder.ClaimsTransformationAppBuilderExtensions", "TypeId": "public static class Microsoft.AspNetCore.Builder.ClaimsTransformationAppBuilderExtensions",
"Kind": "Removal" "Kind": "Removal"
}, },
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions options, Microsoft.AspNetCore.Authentication.AuthenticationTicket ticket)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions get_Options()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public System.Void set_Options(Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)",
"Kind": "Removal"
},
{ {
"TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions", "TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions",
"MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Authentication.SharedAuthenticationOptions> configureOptions)", "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Authentication.SharedAuthenticationOptions> configureOptions)",
"Kind": "Removal" "Kind": "Removal"
}, },
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.BaseControlContext : Microsoft.AspNetCore.Authentication.BaseContext", "TypeId": "public abstract class Microsoft.AspNetCore.Authentication.BaseContext",
"MemberId": "public System.Boolean CheckEventResult(out Microsoft.AspNetCore.Authentication.AuthenticateResult result)",
"Kind": "Removal" "Kind": "Removal"
}, },
{ {
"TypeId": "public class Microsoft.AspNetCore.Authentication.BaseControlContext : Microsoft.AspNetCore.Authentication.BaseContext", "TypeId": "public class Microsoft.AspNetCore.Authentication.BaseControlContext : Microsoft.AspNetCore.Authentication.BaseContext",
"MemberId": "public System.Void SkipToNextMiddleware()", "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.FailureContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext",
"Kind": "Removal"
},
{
"TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions",
"MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection services)",
"Kind": "Removal"
},
{
"TypeId": "public enum Microsoft.AspNetCore.Authentication.EventResultState",
"Kind": "Removal" "Kind": "Removal"
} }
] ]

View File

@ -52,13 +52,13 @@ namespace Microsoft.AspNetCore.Authorization.Policy
else else
{ {
context.User = new ClaimsPrincipal(new ClaimsIdentity()); context.User = new ClaimsPrincipal(new ClaimsIdentity());
return AuthenticateResult.None(); return AuthenticateResult.NoResult();
} }
} }
return (context.User?.Identity?.IsAuthenticated ?? false) return (context.User?.Identity?.IsAuthenticated ?? false)
? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User")) ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User"))
: AuthenticateResult.None(); : AuthenticateResult.NoResult();
} }
/// <summary> /// <summary>

View File

@ -2,6 +2,7 @@
// 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.Diagnostics;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
@ -29,7 +30,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
[Fact] [Fact]
public async Task VerifySchemeDefaults() public async Task VerifySchemeDefaults()
{ {
var services = new ServiceCollection().AddCookieAuthentication(); var services = new ServiceCollection();
services.AddAuthentication().AddCookie();
var sp = services.BuildServiceProvider(); var sp = services.BuildServiceProvider();
var schemeProvider = sp.GetRequiredService<IAuthenticationSchemeProvider>(); var schemeProvider = sp.GetRequiredService<IAuthenticationSchemeProvider>();
var scheme = await schemeProvider.GetSchemeAsync(CookieAuthenticationDefaults.AuthenticationScheme); var scheme = await schemeProvider.GetSchemeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
@ -124,7 +126,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
[Fact] [Fact]
public async Task SignInCausesDefaultCookieToBeCreated() public async Task SignInCausesDefaultCookieToBeCreated()
{ {
var server = CreateServerWithServices(s => s.AddCookieAuthentication(o => var server = CreateServerWithServices(s => s.AddAuthentication().AddCookie(o =>
{ {
o.LoginPath = new PathString("/login"); o.LoginPath = new PathString("/login");
o.CookieName = "TestCookie"; o.CookieName = "TestCookie";
@ -772,7 +774,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
} }
app.Map("/login", signoutApp => signoutApp.Run(context => context.ChallengeAsync("Cookies", new AuthenticationProperties() { RedirectUri = "/" }))); app.Map("/login", signoutApp => signoutApp.Run(context => context.ChallengeAsync("Cookies", new AuthenticationProperties() { RedirectUri = "/" })));
}) })
.ConfigureServices(s => s.AddCookieAuthentication(o => o.LoginPath = new PathString("/page"))); .ConfigureServices(s => s.AddAuthentication().AddCookie(o => o.LoginPath = new PathString("/page")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/login"); var transaction = await server.SendAsync("http://example.com/login");
@ -803,7 +805,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
await Assert.ThrowsAsync<InvalidOperationException>(() => context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme)); await Assert.ThrowsAsync<InvalidOperationException>(() => context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme));
}); });
}) })
.ConfigureServices(services => services.AddCookieAuthentication()); .ConfigureServices(services => services.AddAuthentication().AddCookie());
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com"); var transaction = await server.SendAsync("http://example.com");
@ -821,7 +823,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
}) })
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication(); services.AddAuthentication().AddCookie();
services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme,
o => o.CookieName = "One"); o => o.CookieName = "One");
}); });
@ -844,7 +846,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
}) })
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication("Cookie1"); services.AddAuthentication().AddCookie("Cookie1");
services.Configure<CookieAuthenticationOptions>("Cookie1", services.Configure<CookieAuthenticationOptions>("Cookie1",
o => o.CookieName = "One"); o => o.CookieName = "One");
}); });
@ -866,7 +868,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
app.Map("/notlogin", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies", app.Map("/notlogin", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies",
new ClaimsPrincipal()))); new ClaimsPrincipal())));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.LoginPath = new PathString("/login"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LoginPath = new PathString("/login")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/notlogin?ReturnUrl=%2Fpage"); var transaction = await server.SendAsync("http://example.com/notlogin?ReturnUrl=%2Fpage");
@ -883,7 +885,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
app.UseAuthentication(); app.UseAuthentication();
app.Map("/login", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies", new ClaimsPrincipal()))); app.Map("/login", signoutApp => signoutApp.Run(context => context.SignInAsync("Cookies", new ClaimsPrincipal())));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.LoginPath = new PathString("/login"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LoginPath = new PathString("/login")));
var server = new TestServer(builder); var server = new TestServer(builder);
@ -905,7 +907,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
app.UseAuthentication(); app.UseAuthentication();
app.Map("/notlogout", signoutApp => signoutApp.Run(context => context.SignOutAsync("Cookies"))); app.Map("/notlogout", signoutApp => signoutApp.Run(context => context.SignOutAsync("Cookies")));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.LogoutPath = new PathString("/logout"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LogoutPath = new PathString("/logout")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/notlogout?ReturnUrl=%2Fpage"); var transaction = await server.SendAsync("http://example.com/notlogout?ReturnUrl=%2Fpage");
@ -922,7 +924,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
app.UseAuthentication(); app.UseAuthentication();
app.Map("/logout", signoutApp => signoutApp.Run(context => context.SignOutAsync("Cookies"))); app.Map("/logout", signoutApp => signoutApp.Run(context => context.SignOutAsync("Cookies")));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.LogoutPath = new PathString("/logout"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LogoutPath = new PathString("/logout")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/logout?ReturnUrl=%2Fpage"); var transaction = await server.SendAsync("http://example.com/logout?ReturnUrl=%2Fpage");
@ -943,7 +945,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
app.UseAuthentication(); app.UseAuthentication();
app.Map("/forbid", signoutApp => signoutApp.Run(context => context.ForbidAsync("Cookies"))); app.Map("/forbid", signoutApp => signoutApp.Run(context => context.ForbidAsync("Cookies")));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.AccessDeniedPath = new PathString("/denied"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.AccessDeniedPath = new PathString("/denied")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/forbid"); var transaction = await server.SendAsync("http://example.com/forbid");
@ -963,7 +965,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
map.UseAuthentication(); map.UseAuthentication();
map.Map("/login", signoutApp => signoutApp.Run(context => context.ChallengeAsync("Cookies", new AuthenticationProperties() { RedirectUri = "/" }))); map.Map("/login", signoutApp => signoutApp.Run(context => context.ChallengeAsync("Cookies", new AuthenticationProperties() { RedirectUri = "/" })));
})) }))
.ConfigureServices(services => services.AddCookieAuthentication(o => o.LoginPath = new PathString("/page"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.LoginPath = new PathString("/page")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/base/login"); var transaction = await server.SendAsync("http://example.com/base/login");
@ -1073,7 +1075,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
map.UseAuthentication(); map.UseAuthentication();
map.Map("/forbid", signoutApp => signoutApp.Run(context => context.ForbidAsync("Cookies"))); map.Map("/forbid", signoutApp => signoutApp.Run(context => context.ForbidAsync("Cookies")));
})) }))
.ConfigureServices(services => services.AddCookieAuthentication(o => o.AccessDeniedPath = new PathString("/denied"))); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.AccessDeniedPath = new PathString("/denied")));
var server = new TestServer(builder); var server = new TestServer(builder);
var transaction = await server.SendAsync("http://example.com/base/forbid"); var transaction = await server.SendAsync("http://example.com/base/forbid");
@ -1097,7 +1099,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))), new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))),
new AuthenticationProperties())); new AuthenticationProperties()));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => .ConfigureServices(services => services.AddAuthentication().AddCookie(o =>
{ {
o.TicketDataFormat = new TicketDataFormat(dp); o.TicketDataFormat = new TicketDataFormat(dp);
o.CookieName = "Cookie"; o.CookieName = "Cookie";
@ -1117,7 +1119,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
Describe(context.Response, result); Describe(context.Response, result);
}); });
}) })
.ConfigureServices(services => services.AddCookieAuthentication("Cookies", o => .ConfigureServices(services => services.AddAuthentication().AddCookie("Cookies", o =>
{ {
o.CookieName = "Cookie"; o.CookieName = "Cookie";
o.TicketDataFormat = new TicketDataFormat(dp); o.TicketDataFormat = new TicketDataFormat(dp);
@ -1132,7 +1134,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
public async Task NullExpiresUtcPropertyIsGuarded() public async Task NullExpiresUtcPropertyIsGuarded()
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.ConfigureServices(services => services.AddCookieAuthentication(o => .ConfigureServices(services => services.AddAuthentication().AddCookie(o =>
{ {
o.Events = new CookieAuthenticationEvents o.Events = new CookieAuthenticationEvents
{ {
@ -1229,7 +1231,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
=> CreateServerWithServices(s => => CreateServerWithServices(s =>
{ {
s.AddSingleton<ISystemClock>(_clock); s.AddSingleton<ISystemClock>(_clock);
s.AddCookieAuthentication(configureOptions); s.AddAuthentication().AddCookie(configureOptions);
s.AddSingleton<IClaimsTransformation, ClaimsTransformer>(); s.AddSingleton<IClaimsTransformation, ClaimsTransformer>();
}, testpath, baseAddress); }, testpath, baseAddress);
@ -1281,6 +1283,15 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
await testpath(context); await testpath(context);
} }
else if (req.Path == new PathString("/checkforerrors"))
{
var result = await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); // this used to be "Automatic"
if (result.Failure != null)
{
throw new Exception("Failed to authenticate", result.Failure);
}
return;
}
else else
{ {
await next(); await next();

View File

@ -85,9 +85,9 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
{ {
options.DefaultSignInScheme = "External"; options.DefaultSignInScheme = "External";
options.DefaultAuthenticateScheme = "External"; options.DefaultAuthenticateScheme = "External";
}); })
services.AddCookieAuthentication("External", o => { }); .AddCookie("External", o => { })
services.AddFacebookAuthentication(o => .AddFacebook(o =>
{ {
o.AppId = "Test App Id"; o.AppId = "Test App Id";
o.AppSecret = "Test App Secret"; o.AppSecret = "Test App Secret";
@ -123,12 +123,12 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
}), }),
services => services =>
{ {
services.AddCookieAuthentication("External", o => { }); services.AddAuthentication()
services.AddFacebookAuthentication(o => .AddCookie("External", o => { })
.AddFacebook(o =>
{ {
o.AppId = "Test App Id"; o.AppId = "Test App Id";
o.AppSecret = "Test App Secret"; o.AppSecret = "Test App Secret";
o.SignInScheme = "External";
}); });
}, },
handler: null); handler: null);
@ -155,8 +155,9 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
}, },
services => services =>
{ {
services.AddCookieAuthentication("External", o => { }); services.AddAuthentication()
services.AddFacebookAuthentication(o => .AddCookie("External", o => { })
.AddFacebook(o =>
{ {
o.AppId = "Test App Id"; o.AppId = "Test App Id";
o.AppSecret = "Test App Secret"; o.AppSecret = "Test App Secret";
@ -185,9 +186,9 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
services.AddAuthentication(options => services.AddAuthentication(options =>
{ {
options.DefaultSignInScheme = "External"; options.DefaultSignInScheme = "External";
}); })
services.AddCookieAuthentication(); .AddCookie()
services.AddFacebookAuthentication(o => .AddFacebook(o =>
{ {
o.AppId = "Test App Id"; o.AppId = "Test App Id";
o.AppSecret = "Test App Secret"; o.AppSecret = "Test App Secret";
@ -217,19 +218,15 @@ namespace Microsoft.AspNetCore.Authentication.Facebook
var finalUserInfoEndpoint = string.Empty; var finalUserInfoEndpoint = string.Empty;
var stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("FacebookTest")); var stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("FacebookTest"));
var server = CreateServer( var server = CreateServer(
app => app => app.UseAuthentication(),
{
app.UseAuthentication();
},
services => services =>
{ {
services.AddAuthentication(options => services.AddAuthentication(options =>
{ {
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}); })
services.AddCookieAuthentication(); .AddCookie()
services.AddFacebookAuthentication(o => .AddFacebook(o =>
{ {
o.AppId = "Test App Id"; o.AppId = "Test App Id";
o.AppSecret = "Test App Secret"; o.AppSecret = "Test App Secret";

View File

@ -516,7 +516,7 @@ namespace Microsoft.AspNetCore.Authentication.Google
OnCreatingTicket = context => OnCreatingTicket = context =>
{ {
var refreshToken = context.RefreshToken; var refreshToken = context.RefreshToken;
context.Ticket.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(0); return Task.FromResult(0);
} }
}; };
@ -595,7 +595,7 @@ namespace Microsoft.AspNetCore.Authentication.Google
{ {
OnTicketReceived = context => OnTicketReceived = context =>
{ {
context.Ticket.Properties.RedirectUri = null; context.Properties.RedirectUri = null;
return Task.FromResult(0); return Task.FromResult(0);
} }
}; };
@ -985,7 +985,7 @@ namespace Microsoft.AspNetCore.Authentication.Google
else if (req.Path == new PathString("/tokens")) else if (req.Path == new PathString("/tokens"))
{ {
var result = await context.AuthenticateAsync(TestExtensions.CookieAuthenticationScheme); var result = await context.AuthenticateAsync(TestExtensions.CookieAuthenticationScheme);
var tokens = result.Ticket.Properties.GetTokens(); var tokens = result.Properties.GetTokens();
res.Describe(tokens); res.Describe(tokens);
} }
else if (req.Path == new PathString("/me")) else if (req.Path == new PathString("/me"))
@ -995,17 +995,17 @@ namespace Microsoft.AspNetCore.Authentication.Google
else if (req.Path == new PathString("/authenticate")) else if (req.Path == new PathString("/authenticate"))
{ {
var result = await context.AuthenticateAsync(TestExtensions.CookieAuthenticationScheme); var result = await context.AuthenticateAsync(TestExtensions.CookieAuthenticationScheme);
res.Describe(result.Ticket.Principal); res.Describe(result.Principal);
} }
else if (req.Path == new PathString("/authenticateGoogle")) else if (req.Path == new PathString("/authenticateGoogle"))
{ {
var result = await context.AuthenticateAsync("Google"); var result = await context.AuthenticateAsync("Google");
res.Describe(result?.Ticket?.Principal); res.Describe(result?.Principal);
} }
else if (req.Path == new PathString("/authenticateFacebook")) else if (req.Path == new PathString("/authenticateFacebook"))
{ {
var result = await context.AuthenticateAsync("Facebook"); var result = await context.AuthenticateAsync("Facebook");
res.Describe(result?.Ticket?.Principal); res.Describe(result?.Principal);
} }
else if (req.Path == new PathString("/unauthorized")) else if (req.Path == new PathString("/unauthorized"))
{ {
@ -1024,15 +1024,15 @@ namespace Microsoft.AspNetCore.Authentication.Google
} }
else if (req.Path == new PathString("/signIn")) else if (req.Path == new PathString("/signIn"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignInAsync("Google", new ClaimsPrincipal())); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("Google", new ClaimsPrincipal()));
} }
else if (req.Path == new PathString("/signOut")) else if (req.Path == new PathString("/signOut"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignOutAsync("Google")); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignOutAsync("Google"));
} }
else if (req.Path == new PathString("/forbid")) else if (req.Path == new PathString("/forbid"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.ForbidAsync("Google")); await Assert.ThrowsAsync<InvalidOperationException>(() => context.ForbidAsync("Google"));
} }
else if (testpath != null) else if (testpath != null)
{ {
@ -1050,12 +1050,12 @@ namespace Microsoft.AspNetCore.Authentication.Google
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.DefaultAuthenticateScheme = TestExtensions.CookieAuthenticationScheme; o.DefaultAuthenticateScheme = TestExtensions.CookieAuthenticationScheme;
o.DefaultSignInScheme = TestExtensions.CookieAuthenticationScheme;
o.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme; o.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
}); });
services.AddCookieAuthentication(TestExtensions.CookieAuthenticationScheme); services.AddAuthentication()
services.AddGoogleAuthentication(configureOptions); .AddCookie(TestExtensions.CookieAuthenticationScheme)
services.AddFacebookAuthentication(o => .AddGoogle(configureOptions)
.AddFacebook(o =>
{ {
o.AppId = "Test AppId"; o.AppId = "Test AppId";
o.AppSecret = "Test AppSecrent"; o.AppSecret = "Test AppSecrent";

View File

@ -127,11 +127,8 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob") new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
}; };
context.Ticket = new AuthenticationTicket( context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)), context.Success();
new AuthenticationProperties(), context.Scheme.Name);
context.HandleResponse();
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
} }
@ -338,7 +335,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
// Retrieve the NameIdentifier claim from the identity // Retrieve the NameIdentifier claim from the identity
// returned by the custom security token validator. // returned by the custom security token validator.
var identity = (ClaimsIdentity)context.Ticket.Principal.Identity; var identity = (ClaimsIdentity)context.Principal.Identity;
var identifier = identity.FindFirst(ClaimTypes.NameIdentifier); var identifier = identity.FindFirst(ClaimTypes.NameIdentifier);
Assert.Equal("Bob le Tout Puissant", identifier.Value); Assert.Equal("Bob le Tout Puissant", identifier.Value);
@ -396,7 +393,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
OnMessageReceived = context => OnMessageReceived = context =>
{ {
context.Skip(); context.NoResult();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnTokenValidated = context => OnTokenValidated = context =>
@ -420,7 +417,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
[Fact] [Fact]
public async Task EventOnMessageReceivedHandled_NoMoreEventsExecuted() public async Task EventOnMessageReceivedReject_NoMoreEventsExecuted()
{ {
var server = CreateServer(options => var server = CreateServer(options =>
{ {
@ -428,7 +425,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
OnMessageReceived = context => OnMessageReceived = context =>
{ {
context.HandleResponse(); context.Fail("Authentication was aborted from user code.");
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
}, },
@ -447,9 +444,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}; };
}); });
var response = await SendAsync(server, "http://example.com/checkforerrors", "Bearer Token"); var exception = await Assert.ThrowsAsync<Exception>(delegate
Assert.Equal(HttpStatusCode.Accepted, response.Response.StatusCode); {
Assert.Equal(string.Empty, response.ResponseText); return SendAsync(server, "http://example.com/checkforerrors", "Bearer Token");
});
Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message);
} }
[Fact] [Fact]
@ -461,7 +461,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
OnTokenValidated = context => OnTokenValidated = context =>
{ {
context.Skip(); context.NoResult();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnAuthenticationFailed = context => OnAuthenticationFailed = context =>
@ -483,7 +483,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
[Fact] [Fact]
public async Task EventOnTokenValidatedHandled_NoMoreEventsExecuted() public async Task EventOnTokenValidatedReject_NoMoreEventsExecuted()
{ {
var server = CreateServer(options => var server = CreateServer(options =>
{ {
@ -491,7 +491,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
OnTokenValidated = context => OnTokenValidated = context =>
{ {
context.HandleResponse(); context.Fail("Authentication was aborted from user code.");
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
}, },
@ -508,9 +508,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT")); options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT"));
}); });
var response = await SendAsync(server, "http://example.com/checkforerrors", "Bearer Token"); var exception = await Assert.ThrowsAsync<Exception>(delegate
Assert.Equal(HttpStatusCode.Accepted, response.Response.StatusCode); {
Assert.Equal(string.Empty, response.ResponseText); return SendAsync(server, "http://example.com/checkforerrors", "Bearer Token");
});
Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message);
} }
[Fact] [Fact]
@ -526,7 +529,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}, },
OnAuthenticationFailed = context => OnAuthenticationFailed = context =>
{ {
context.Skip(); context.NoResult();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnChallenge = context => OnChallenge = context =>
@ -544,7 +547,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
[Fact] [Fact]
public async Task EventOnAuthenticationFailedHandled_NoMoreEventsExecuted() public async Task EventOnAuthenticationFailedReject_NoMoreEventsExecuted()
{ {
var server = CreateServer(options => var server = CreateServer(options =>
{ {
@ -556,7 +559,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}, },
OnAuthenticationFailed = context => OnAuthenticationFailed = context =>
{ {
context.HandleResponse(); context.Fail("Authentication was aborted from user code.");
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
}, },
@ -569,9 +572,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT")); options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT"));
}); });
var response = await SendAsync(server, "http://example.com/checkforerrors", "Bearer Token"); var exception = await Assert.ThrowsAsync<Exception>(delegate
Assert.Equal(HttpStatusCode.Accepted, response.Response.StatusCode); {
Assert.Equal(string.Empty, response.ResponseText); return SendAsync(server, "http://example.com/checkforerrors", "Bearer Token");
});
Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message);
} }
[Fact] [Fact]
@ -583,7 +589,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
OnChallenge = context => OnChallenge = context =>
{ {
context.Skip(); context.HandleResponse();
return Task.FromResult(0); return Task.FromResult(0);
}, },
}; };
@ -595,29 +601,6 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Assert.Equal(string.Empty, response.ResponseText); Assert.Equal(string.Empty, response.ResponseText);
} }
[Fact]
public async Task EventOnChallengeHandled_ResponseNotModified()
{
var server = CreateServer(o =>
{
o.Events = new JwtBearerEvents()
{
OnChallenge = context =>
{
context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0);
},
};
});
var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token");
Assert.Equal(HttpStatusCode.Accepted, response.Response.StatusCode);
Assert.Empty(response.Response.Headers.WwwAuthenticate);
Assert.Equal(string.Empty, response.ResponseText);
}
class InvalidTokenValidator : ISecurityTokenValidator class InvalidTokenValidator : ISecurityTokenValidator
{ {
public InvalidTokenValidator() public InvalidTokenValidator()
@ -752,11 +735,11 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
else if (context.Request.Path == new PathString("/signIn")) else if (context.Request.Path == new PathString("/signIn"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignInAsync(JwtBearerDefaults.AuthenticationScheme, new ClaimsPrincipal())); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync(JwtBearerDefaults.AuthenticationScheme, new ClaimsPrincipal()));
} }
else if (context.Request.Path == new PathString("/signOut")) else if (context.Request.Path == new PathString("/signOut"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignOutAsync(JwtBearerDefaults.AuthenticationScheme)); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignOutAsync(JwtBearerDefaults.AuthenticationScheme));
} }
else else
{ {

View File

@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
OnCreatingTicket = context => OnCreatingTicket = context =>
{ {
var refreshToken = context.RefreshToken; var refreshToken = context.RefreshToken;
context.Ticket.Principal.AddIdentity(new ClaimsIdentity(new Claim[] { new Claim("RefreshToken", refreshToken, ClaimValueTypes.String, "Microsoft") }, "Microsoft")); context.Principal.AddIdentity(new ClaimsIdentity(new Claim[] { new Claim("RefreshToken", refreshToken, ClaimValueTypes.String, "Microsoft") }, "Microsoft"));
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
} }
}; };
@ -205,15 +205,15 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
} }
else if (req.Path == new PathString("/signIn")) else if (req.Path == new PathString("/signIn"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignInAsync("Microsoft", new ClaimsPrincipal())); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("Microsoft", new ClaimsPrincipal()));
} }
else if (req.Path == new PathString("/signOut")) else if (req.Path == new PathString("/signOut"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignOutAsync("Microsoft")); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignOutAsync("Microsoft"));
} }
else if (req.Path == new PathString("/forbid")) else if (req.Path == new PathString("/forbid"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.ForbidAsync("Microsoft")); await Assert.ThrowsAsync<InvalidOperationException>(() => context.ForbidAsync("Microsoft"));
} }
else else
{ {
@ -226,10 +226,10 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.DefaultAuthenticateScheme = TestExtensions.CookieAuthenticationScheme; o.DefaultAuthenticateScheme = TestExtensions.CookieAuthenticationScheme;
o.DefaultSignInScheme = TestExtensions.CookieAuthenticationScheme;
}); });
services.AddCookieAuthentication(TestExtensions.CookieAuthenticationScheme, o => { }); services.AddAuthentication()
services.AddMicrosoftAccountAuthentication(configureOptions); .AddCookie(TestExtensions.CookieAuthenticationScheme, o => { })
.AddMicrosoftAccount(configureOptions);
}); });
return new TestServer(builder); return new TestServer(builder);
} }

View File

@ -143,29 +143,6 @@ namespace Microsoft.AspNetCore.Authentication.OAuth
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
} }
[Fact]
public async Task ThrowsIfSignInSchemeMissing()
{
var server = CreateServer(
app => { },
services => services.AddOAuthAuthentication("weeblie", o =>
{
o.ClientId = "Whatever;";
o.ClientSecret = "Whatever;";
o.CallbackPath = "/";
o.TokenEndpoint = "/";
o.AuthorizationEndpoint = "/";
}),
context =>
{
// REVIEW: Gross.
Assert.Throws<ArgumentException>("SignInScheme", () => context.ChallengeAsync("weeblie").GetAwaiter().GetResult());
return true;
});
var transaction = await server.SendAsync("http://example.com/challenge");
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
}
[Fact] [Fact]
public async Task RedirectToIdentityProvider_SetsCorrelationIdCookiePath_ToCallBackPath() public async Task RedirectToIdentityProvider_SetsCorrelationIdCookiePath_ToCallBackPath()
{ {

View File

@ -335,7 +335,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
// This test can be further refined. When one auth handler skips, the authentication responsibility // This test can be further refined. When one auth handler skips, the authentication responsibility
// will be flowed to the next one. A dummy auth handler can be added to ensure the correct logic. // will be flowed to the next one. A dummy auth handler can be added to ensure the correct logic.
[Fact] [Fact]
public async Task OnRedirectToIdentityProviderEventSkipResponse() public async Task OnRedirectToIdentityProviderEventHandleResponse()
{ {
var settings = new TestSettings( var settings = new TestSettings(
opts => opts =>
@ -346,7 +346,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
OnRedirectToIdentityProvider = context => OnRedirectToIdentityProvider = context =>
{ {
context.Skip(); context.HandleResponse();
return Task.FromResult(0); return Task.FromResult(0);
} }
}; };

View File

@ -22,8 +22,9 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication(); services.AddAuthentication()
services.AddOpenIdConnectAuthentication(o => .AddCookie()
.AddOpenIdConnect(o =>
{ {
o.Authority = TestServerBuilder.DefaultAuthority; o.Authority = TestServerBuilder.DefaultAuthority;
o.ClientId = Guid.NewGuid().ToString(); o.ClientId = Guid.NewGuid().ToString();
@ -45,19 +46,6 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
} }
[Fact]
public Task ThrowsWhenSignInSchemeIsMissing()
{
return TestConfigurationException<ArgumentException>(
o =>
{
o.ClientId = "Test Id";
o.Authority = TestServerBuilder.DefaultAuthority;
o.CallbackPath = "/";
},
ex => Assert.Equal("SignInScheme", ex.ParamName));
}
[Fact] [Fact]
public Task ThrowsWhenClientIdIsMissing() public Task ThrowsWhenClientIdIsMissing()
{ {
@ -119,8 +107,9 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication(); services.AddAuthentication()
services.AddOpenIdConnectAuthentication(options); .AddCookie()
.AddOpenIdConnect(options);
}) })
.Configure(app => app.UseAuthentication()); .Configure(app => app.UseAuthentication());

View File

@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
private readonly Func<UserInformationReceivedContext, Task> UserNotImpl = context => { throw new NotImplementedException("User"); }; private readonly Func<UserInformationReceivedContext, Task> UserNotImpl = context => { throw new NotImplementedException("User"); };
private readonly Func<AuthenticationFailedContext, Task> FailedNotImpl = context => { throw new NotImplementedException("Failed", context.Exception); }; private readonly Func<AuthenticationFailedContext, Task> FailedNotImpl = context => { throw new NotImplementedException("Failed", context.Exception); };
private readonly Func<TicketReceivedContext, Task> TicketNotImpl = context => { throw new NotImplementedException("Ticket"); }; private readonly Func<TicketReceivedContext, Task> TicketNotImpl = context => { throw new NotImplementedException("Ticket"); };
private readonly Func<FailureContext, Task> FailureNotImpl = context => { throw new NotImplementedException("Failure", context.Failure); }; private readonly Func<RemoteFailureContext, Task> FailureNotImpl = context => { throw new NotImplementedException("Failure", context.Failure); };
private readonly Func<RedirectContext, Task> RedirectNotImpl = context => { throw new NotImplementedException("Redirect"); }; private readonly Func<RedirectContext, Task> RedirectNotImpl = context => { throw new NotImplementedException("Redirect"); };
private readonly Func<RemoteSignOutContext, Task> RemoteSignOutNotImpl = context => { throw new NotImplementedException("Remote"); }; private readonly Func<RemoteSignOutContext, Task> RemoteSignOutNotImpl = context => { throw new NotImplementedException("Remote"); };
private readonly RequestDelegate AppNotImpl = context => { throw new NotImplementedException("App"); }; private readonly RequestDelegate AppNotImpl = context => { throw new NotImplementedException("App"); };
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnMessageReceived = context => OnMessageReceived = context =>
{ {
messageReceived = true; messageReceived = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnTokenValidated = TokenNotImpl, OnTokenValidated = TokenNotImpl,
@ -75,6 +75,51 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(messageReceived); Assert.True(messageReceived);
} }
[Fact]
public async Task OnMessageReceived_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnTokenValidated = TokenNotImpl,
OnAuthorizationCodeReceived = CodeNotImpl,
OnTokenResponseReceived = TokenResponseNotImpl,
OnUserInformationReceived = UserNotImpl,
OnAuthenticationFailed = FailedNotImpl,
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnMessageReceived_Handled_NoMoreEventsRun() public async Task OnMessageReceived_Handled_NoMoreEventsRun()
{ {
@ -124,7 +169,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenValidated = context => OnTokenValidated = context =>
{ {
tokenValidated = true; tokenValidated = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnAuthorizationCodeReceived = CodeNotImpl, OnAuthorizationCodeReceived = CodeNotImpl,
@ -151,6 +196,57 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(tokenValidated); Assert.True(tokenValidated);
} }
[Fact]
public async Task OnTokenValidated_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var tokenValidated = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
return Task.FromResult(0);
},
OnTokenValidated = context =>
{
tokenValidated = true;
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = CodeNotImpl,
OnTokenResponseReceived = TokenResponseNotImpl,
OnUserInformationReceived = UserNotImpl,
OnAuthenticationFailed = FailedNotImpl,
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(tokenValidated);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnTokenValidated_HandledWithoutTicket_NoMoreEventsRun() public async Task OnTokenValidated_HandledWithoutTicket_NoMoreEventsRun()
{ {
@ -167,7 +263,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
tokenValidated = true; tokenValidated = true;
context.HandleResponse(); context.HandleResponse();
context.Ticket = null; context.Principal = null;
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
}, },
@ -209,8 +305,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenValidated = context => OnTokenValidated = context =>
{ {
tokenValidated = true; tokenValidated = true;
context.HandleResponse(); context.Success();
// context.Ticket = null;
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnAuthorizationCodeReceived = CodeNotImpl, OnAuthorizationCodeReceived = CodeNotImpl,
@ -262,7 +357,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnAuthorizationCodeReceived = context => OnAuthorizationCodeReceived = context =>
{ {
codeReceived = true; codeReceived = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnTokenResponseReceived = TokenResponseNotImpl, OnTokenResponseReceived = TokenResponseNotImpl,
@ -289,6 +384,63 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(codeReceived); Assert.True(codeReceived);
} }
[Fact]
public async Task OnAuthorizationCodeReceived_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var tokenValidated = false;
var codeReceived = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
return Task.FromResult(0);
},
OnTokenValidated = context =>
{
tokenValidated = true;
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = context =>
{
codeReceived = true;
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnTokenResponseReceived = TokenResponseNotImpl,
OnUserInformationReceived = UserNotImpl,
OnAuthenticationFailed = FailedNotImpl,
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(tokenValidated);
Assert.True(codeReceived);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnAuthorizationCodeReceived_HandledWithoutTicket_NoMoreEventsRun() public async Task OnAuthorizationCodeReceived_HandledWithoutTicket_NoMoreEventsRun()
{ {
@ -311,7 +463,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
codeReceived = true; codeReceived = true;
context.HandleResponse(); context.HandleResponse();
context.Ticket = null; context.Principal = null;
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
}, },
@ -358,8 +510,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnAuthorizationCodeReceived = context => OnAuthorizationCodeReceived = context =>
{ {
codeReceived = true; codeReceived = true;
context.HandleResponse(); context.Success();
// context.Ticket = null;
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnTokenResponseReceived = TokenResponseNotImpl, OnTokenResponseReceived = TokenResponseNotImpl,
@ -417,7 +568,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenResponseReceived = context => OnTokenResponseReceived = context =>
{ {
tokenResponseReceived = true; tokenResponseReceived = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnUserInformationReceived = UserNotImpl, OnUserInformationReceived = UserNotImpl,
@ -444,6 +595,69 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(tokenResponseReceived); Assert.True(tokenResponseReceived);
} }
[Fact]
public async Task OnTokenResponseReceived_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var tokenValidated = false;
var codeReceived = false;
var tokenResponseReceived = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
return Task.FromResult(0);
},
OnTokenValidated = context =>
{
tokenValidated = true;
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = context =>
{
codeReceived = true;
return Task.FromResult(0);
},
OnTokenResponseReceived = context =>
{
tokenResponseReceived = true;
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnUserInformationReceived = UserNotImpl,
OnAuthenticationFailed = FailedNotImpl,
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(tokenValidated);
Assert.True(codeReceived);
Assert.True(tokenResponseReceived);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnTokenResponseReceived_HandledWithoutTicket_NoMoreEventsRun() public async Task OnTokenResponseReceived_HandledWithoutTicket_NoMoreEventsRun()
{ {
@ -471,7 +685,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenResponseReceived = context => OnTokenResponseReceived = context =>
{ {
tokenResponseReceived = true; tokenResponseReceived = true;
context.Ticket = null; context.Principal = null;
context.HandleResponse(); context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
@ -525,8 +739,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenResponseReceived = context => OnTokenResponseReceived = context =>
{ {
tokenResponseReceived = true; tokenResponseReceived = true;
// context.Ticket = null; context.Success();
context.HandleResponse();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnUserInformationReceived = UserNotImpl, OnUserInformationReceived = UserNotImpl,
@ -584,7 +797,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenValidated = context => OnTokenValidated = context =>
{ {
tokenValidated = true; tokenValidated = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnUserInformationReceived = UserNotImpl, OnUserInformationReceived = UserNotImpl,
@ -611,6 +824,69 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(tokenValidated); Assert.True(tokenValidated);
} }
[Fact]
public async Task OnTokenValidatedBackchannel_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var codeReceived = false;
var tokenResponseReceived = false;
var tokenValidated = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = context =>
{
codeReceived = true;
return Task.FromResult(0);
},
OnTokenResponseReceived = context =>
{
tokenResponseReceived = true;
return Task.FromResult(0);
},
OnTokenValidated = context =>
{
tokenValidated = true;
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnUserInformationReceived = UserNotImpl,
OnAuthenticationFailed = FailedNotImpl,
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "state=protected_state&code=my_code");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(codeReceived);
Assert.True(tokenResponseReceived);
Assert.True(tokenValidated);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnTokenValidatedBackchannel_HandledWithoutTicket_NoMoreEventsRun() public async Task OnTokenValidatedBackchannel_HandledWithoutTicket_NoMoreEventsRun()
{ {
@ -638,7 +914,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenValidated = context => OnTokenValidated = context =>
{ {
tokenValidated = true; tokenValidated = true;
context.Ticket = null; context.Principal = null;
context.HandleResponse(); context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
@ -692,8 +968,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTokenValidated = context => OnTokenValidated = context =>
{ {
tokenValidated = true; tokenValidated = true;
// context.Ticket = null; context.Success();
context.HandleResponse();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnUserInformationReceived = UserNotImpl, OnUserInformationReceived = UserNotImpl,
@ -757,7 +1032,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnUserInformationReceived = context => OnUserInformationReceived = context =>
{ {
userInfoReceived = true; userInfoReceived = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnAuthenticationFailed = FailedNotImpl, OnAuthenticationFailed = FailedNotImpl,
@ -784,6 +1059,75 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(userInfoReceived); Assert.True(userInfoReceived);
} }
[Fact]
public async Task OnUserInformationReceived_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var tokenValidated = false;
var codeReceived = false;
var tokenResponseReceived = false;
var userInfoReceived = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
return Task.FromResult(0);
},
OnTokenValidated = context =>
{
tokenValidated = true;
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = context =>
{
codeReceived = true;
return Task.FromResult(0);
},
OnTokenResponseReceived = context =>
{
tokenResponseReceived = true;
return Task.FromResult(0);
},
OnUserInformationReceived = context =>
{
userInfoReceived = true;
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnAuthenticationFailed = FailedNotImpl,
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(tokenValidated);
Assert.True(codeReceived);
Assert.True(tokenResponseReceived);
Assert.True(userInfoReceived);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnUserInformationReceived_HandledWithoutTicket_NoMoreEventsRun() public async Task OnUserInformationReceived_HandledWithoutTicket_NoMoreEventsRun()
{ {
@ -817,7 +1161,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnUserInformationReceived = context => OnUserInformationReceived = context =>
{ {
userInfoReceived = true; userInfoReceived = true;
context.Ticket = null; context.Principal = null;
context.HandleResponse(); context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
@ -878,7 +1222,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
userInfoReceived = true; userInfoReceived = true;
// context.Ticket = null; // context.Ticket = null;
context.HandleResponse(); context.Success();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnAuthenticationFailed = FailedNotImpl, OnAuthenticationFailed = FailedNotImpl,
@ -949,7 +1293,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
authFailed = true; authFailed = true;
Assert.Equal("TestException", context.Exception.Message); Assert.Equal("TestException", context.Exception.Message);
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnRemoteFailure = FailureNotImpl, OnRemoteFailure = FailureNotImpl,
@ -976,6 +1320,82 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
Assert.True(authFailed); Assert.True(authFailed);
} }
[Fact]
public async Task OnAuthenticationFailed_Reject_NoMoreEventsRun()
{
var messageReceived = false;
var tokenValidated = false;
var codeReceived = false;
var tokenResponseReceived = false;
var userInfoReceived = false;
var authFailed = false;
var remoteFailure = false;
var server = CreateServer(new OpenIdConnectEvents()
{
OnMessageReceived = context =>
{
messageReceived = true;
return Task.FromResult(0);
},
OnTokenValidated = context =>
{
tokenValidated = true;
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = context =>
{
codeReceived = true;
return Task.FromResult(0);
},
OnTokenResponseReceived = context =>
{
tokenResponseReceived = true;
return Task.FromResult(0);
},
OnUserInformationReceived = context =>
{
userInfoReceived = true;
throw new NotImplementedException("TestException");
},
OnAuthenticationFailed = context =>
{
authFailed = true;
Assert.Equal("TestException", context.Exception.Message);
context.Fail("Authentication was aborted from user code.");
return Task.FromResult(0);
},
OnRemoteFailure = context =>
{
remoteFailure = true;
return Task.FromResult(0);
},
OnTicketReceived = TicketNotImpl,
OnRedirectToIdentityProvider = RedirectNotImpl,
OnRedirectToIdentityProviderForSignOut = RedirectNotImpl,
OnRemoteSignOut = RemoteSignOutNotImpl,
},
context =>
{
return context.Response.WriteAsync(context.Request.Path);
});
var exception = await Assert.ThrowsAsync<Exception>(delegate
{
return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code");
});
Assert.Equal("Authentication was aborted from user code.", exception.Message);
Assert.True(messageReceived);
Assert.True(tokenValidated);
Assert.True(codeReceived);
Assert.True(tokenResponseReceived);
Assert.True(userInfoReceived);
Assert.True(authFailed);
Assert.True(remoteFailure);
}
[Fact] [Fact]
public async Task OnAuthenticationFailed_HandledWithoutTicket_NoMoreEventsRun() public async Task OnAuthenticationFailed_HandledWithoutTicket_NoMoreEventsRun()
{ {
@ -1016,7 +1436,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
authFailed = true; authFailed = true;
Assert.Equal("TestException", context.Exception.Message); Assert.Equal("TestException", context.Exception.Message);
Assert.Null(context.Ticket); Assert.Null(context.Principal);
context.HandleResponse(); context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status202Accepted; context.Response.StatusCode = StatusCodes.Status202Accepted;
return Task.FromResult(0); return Task.FromResult(0);
@ -1083,7 +1503,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
authFailed = true; authFailed = true;
Assert.Equal("TestException", context.Exception.Message); Assert.Equal("TestException", context.Exception.Message);
Assert.Null(context.Ticket); Assert.Null(context.Principal);
var claims = new[] var claims = new[]
{ {
@ -1092,11 +1512,8 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob") new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
}; };
context.Ticket = new AuthenticationTicket( context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)), context.Success();
new AuthenticationProperties(), context.Scheme.Name);
context.HandleResponse();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnRemoteFailure = FailureNotImpl, OnRemoteFailure = FailureNotImpl,
@ -1174,7 +1591,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
{ {
remoteFailure = true; remoteFailure = true;
Assert.Equal("TestException", context.Failure.Message); Assert.Equal("TestException", context.Failure.Message);
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnTicketReceived = TicketNotImpl, OnTicketReceived = TicketNotImpl,
@ -1314,7 +1731,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
OnTicketReceived = context => OnTicketReceived = context =>
{ {
ticektReceived = true; ticektReceived = true;
context.Skip(); context.SkipHandler();
return Task.FromResult(0); return Task.FromResult(0);
}, },
@ -1408,8 +1825,9 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication(); services.AddAuthentication()
services.AddOpenIdConnectAuthentication(o => .AddCookie()
.AddOpenIdConnect(o =>
{ {
o.Events = events; o.Events = events;
o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

View File

@ -2,7 +2,6 @@
// 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.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -11,10 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Xunit; using Xunit;

View File

@ -112,10 +112,10 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect
services.AddAuthentication(o => services.AddAuthentication(o =>
{ {
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}); });
services.AddCookieAuthentication(); services.AddAuthentication()
services.AddOpenIdConnectAuthentication(options); .AddCookie()
.AddOpenIdConnect(options);
}); });
return new TestServer(builder); return new TestServer(builder);

View File

@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.Authentication
// return Task.FromResult(0); // return Task.FromResult(0);
// } // }
// public Task ChallengeAsync(ChallengeContext context) // public Task ChallengeAsync(AuthenticationProperties properties)
// { // {
// throw new NotImplementedException(); // throw new NotImplementedException();
// } // }
@ -144,12 +144,12 @@ namespace Microsoft.AspNetCore.Authentication
// throw new NotImplementedException(); // throw new NotImplementedException();
// } // }
// public Task SignInAsync(SignInContext context) // public Task SignInAsync(ClaimsPrincipal principal, AuthenticationProperties properties)
// { // {
// throw new NotImplementedException(); // throw new NotImplementedException();
// } // }
// public Task SignOutAsync(SignOutContext context) // public Task SignOutAsync(AuthenticationProperties properties)
// { // {
// throw new NotImplementedException(); // throw new NotImplementedException();
// } // }

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. // Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Security.Claims; using System.Security.Claims;
@ -13,7 +12,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Authentication.Twitter namespace Microsoft.AspNetCore.Authentication.Twitter
@ -177,15 +175,15 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
var res = context.Response; var res = context.Response;
if (req.Path == new PathString("/signIn")) if (req.Path == new PathString("/signIn"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignInAsync("Twitter", new ClaimsPrincipal())); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync("Twitter", new ClaimsPrincipal()));
} }
else if (req.Path == new PathString("/signOut")) else if (req.Path == new PathString("/signOut"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.SignOutAsync("Twitter")); await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignOutAsync("Twitter"));
} }
else if (req.Path == new PathString("/forbid")) else if (req.Path == new PathString("/forbid"))
{ {
await Assert.ThrowsAsync<NotSupportedException>(() => context.ForbidAsync("Twitter")); await Assert.ThrowsAsync<InvalidOperationException>(() => context.ForbidAsync("Twitter"));
} }
else if (handler == null || !handler(context)) else if (handler == null || !handler(context))
{ {
@ -195,13 +193,14 @@ namespace Microsoft.AspNetCore.Authentication.Twitter
}) })
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication("External", _ => { });
Action<TwitterOptions> wrapOptions = o => Action<TwitterOptions> wrapOptions = o =>
{ {
o.SignInScheme = "External"; o.SignInScheme = "External";
options(o); options(o);
}; };
services.AddTwitterAuthentication(wrapOptions); services.AddAuthentication()
.AddCookie("External", _ => { })
.AddTwitter(wrapOptions);
}); });
return new TestServer(builder); return new TestServer(builder);
} }

View File

@ -312,7 +312,7 @@ namespace Microsoft.AspNetCore.CookiePolicy.Test
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication(o => services.AddAuthentication().AddCookie(o =>
{ {
o.CookieName = "TestCookie"; o.CookieName = "TestCookie";
o.CookieHttpOnly = false; o.CookieHttpOnly = false;
@ -352,7 +352,7 @@ namespace Microsoft.AspNetCore.CookiePolicy.Test
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddCookieAuthentication(o => services.AddAuthentication().AddCookie(o =>
{ {
o.CookieName = "TestCookie"; o.CookieName = "TestCookie";
o.CookieHttpOnly = false; o.CookieHttpOnly = false;

View File

@ -68,7 +68,7 @@ namespace Microsoft.Owin.Security.Interop
await context.Response.WriteAsync(result.Ticket.Principal.Identity.Name); await context.Response.WriteAsync(result.Ticket.Principal.Identity.Name);
}); });
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.DataProtectionProvider = dataProtection));
var newServer = new AspNetCore.TestHost.TestServer(builder); var newServer = new AspNetCore.TestHost.TestServer(builder);
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login"); var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login");
@ -123,7 +123,7 @@ namespace Microsoft.Owin.Security.Interop
await context.Response.WriteAsync(result.Ticket.Principal.Identity.Name); await context.Response.WriteAsync(result.Ticket.Principal.Identity.Name);
}); });
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.DataProtectionProvider = dataProtection));
var newServer = new AspNetCore.TestHost.TestServer(builder); var newServer = new AspNetCore.TestHost.TestServer(builder);
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login"); var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/login");
@ -155,7 +155,7 @@ namespace Microsoft.Owin.Security.Interop
app.UseAuthentication(); app.UseAuthentication();
app.Run(context => context.SignInAsync("Cookies", user)); app.Run(context => context.SignInAsync("Cookies", user));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.DataProtectionProvider = dataProtection));
var newServer = new AspNetCore.TestHost.TestServer(builder); var newServer = new AspNetCore.TestHost.TestServer(builder);
var cookies = await SendAndGetCookies(newServer, "http://example.com/login"); var cookies = await SendAndGetCookies(newServer, "http://example.com/login");
@ -202,7 +202,7 @@ namespace Microsoft.Owin.Security.Interop
app.UseAuthentication(); app.UseAuthentication();
app.Run(context => context.SignInAsync("Cookies", user)); app.Run(context => context.SignInAsync("Cookies", user));
}) })
.ConfigureServices(services => services.AddCookieAuthentication(o => o.DataProtectionProvider = dataProtection)); .ConfigureServices(services => services.AddAuthentication().AddCookie(o => o.DataProtectionProvider = dataProtection));
var newServer = new AspNetCore.TestHost.TestServer(builder); var newServer = new AspNetCore.TestHost.TestServer(builder);
var cookies = await SendAndGetCookies(newServer, "http://example.com/login"); var cookies = await SendAndGetCookies(newServer, "http://example.com/login");