Note: this issue is closed, you should use #1338
Summary:
The old 1.0 Authentication stack no longer will work, and is obsolete in 2.0. All authentication related functionality must be migrated to the 2.0 stack, any interop between old and new must be side by side apps, as opposed to mixing 1.0 auth code with 2.0 auth code in the same app. Cookie authentication will interop, so 1.0 Cookies and 2.0 Cookies will be valid in both apps if configured properly. The main motivation was to move to a more flexible service based IAuthenticationService and away from the old middleware/IAuthenticationManager design that came over from Microsoft.Owin.
IAuthenticationManager(aka httpContext.Authentication) is now obsolete
This was the main entry point into the old auth system. This has now been replaced with a new set of HttpContext extensions that live in the Microsoft.AspNetCore.Authentication namespace and remain very similar:
// Add using to pickup the new extension methods
using Microsoft.AspNetCore.Authentication;
// Update by just removing the .Authentication
context.Authentication.AuthenticateAsync => context.AuthenticateAsync
context.Authentication.ChallengeAsync => context.ChallengeAsync
Configure(): UseXyzAuthentication has been replaced by ConfigureService(): AddXyz()
In Auth 1.0, every auth scheme had its own middleware, and startup looked something like this:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) {
app.UseIdentity();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{ LoginPath = new PathString("/login") });
app.UseFacebookAuthentication(new FacebookOptions
{ AppId = Configuration["facebook:appid"], AppSecret = Configuration["facebook:appsecret"] });
}
In Auth 2.0, there is now only a single Authentication middleware, and each authentication scheme is registered during ConfigureServices, and UseIdentity() is no longer required (since it was just calling UseCookie 4 times underneath the covers)
public void ConfigureServices(IServiceCollection services) {
services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o => o.LoginPath = new PathString("/login"))
.AddFacebook(o =>
{
o.AppId = Configuration["facebook:appid"];
o.AppSecret = Configuration["facebook:appsecret"];
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) {
app.UseAuthentication();
}
New Microsoft.AspNetCore.Authentication.Core/Abstractions
All of the old Authentication namespaces in HttpAbstractions have been deprecated. The new Auth 2.0 stack lives in two new packages inside the HttpAbstractions repo: Microsoft.AspNetCore.Authentication.Core/Abstractions.
Brief overview:
IAuthenticationService: used by the HttpContext extension methods to expose the 5 main operations Authenticate/Challenge/Forbid/SignIn/SignOut
IAuthenticationHandler: Defines the required operations for all handlers: Authenticate/Challenge/Forbid
IAuthenticationSignIn/OutHandler: Implemented to add the SignIn/SignOut methods respectively
IAuthenticationRequestHandler: Implemented by handlers that need to participate in request handler, i.e. remote authentication schemes like OAuth/OIDC that need to process 3rd party auth responses.
AuthenticationScheme: represents a logical named authentication scheme to target for any given IAuthenticationService method, it binds the scheme name (and optional display name) to an IAuthenticationHandler which implements the scheme specific logic.
IAuthenticationSchemeProvider: responsible for managing which schemes are supported, and what the defaults are for each operation (the default implementation just reads from the AuthenticationOptions)
IAuthenticationHandlerProvider: responsible for returning the correct handler instance for a given scheme and request.
IAuthenticationFeature: used to capture the original request path/pathbase so redirects can be computed property after an app.Map()
Types that are mostly unchanged, just with new homes:
AuthenticationProperties: metadata for authentication operations.
AuthenticationTicket: used to store a claims principal (user) + authentication properties
AuthenticateResult: return value for AuthenticateAsync, contains either a ticket, or a failure
Security repo: Microsoft.AspNetCore.Authentication / AuthenticationHandler changes
All of the core abstractions and services for authentication live in HttpAbstracions, but there's an additional layer of base classes/functionality targeted towards implementation of AuthenticationHandlers. This is also where the AuthenticationMiddleware lives. The handlers themselves for the various implementations aren't drastically different, but there were a fair amount of changes
Microsoft.AspNetCore.Authentication
Overview:
AuthenticationMiddleware: UseAuthentication() adds this middleware which does two things. By default It will automatically Authenticate using AuthenticationOptions.DefaultAuthenticateScheme to set httpContext.User if specified. It also will give IAuthenticationRequestHandler's a chance to handle the request.
AuthenticationSchemeOptions: Base class for options used with the AuthenticationHandler base class, it defines two common properties in Events and ClaimsIssuer, as well as a virtual Validate() method that will be called on every request by the handler.
AuthenticationHandler<TOptions>: Abstract base class for handlers who must implement HandleAuthenticateAsync. The rest of IAuthenticationHandler is implemented with some reasonable defaults, Challenge(401)/Forbid(403), and logic to handle per request initialization using IOptionsMonitor<TOptions>.Get(authenticationScheme.Name) to resolve the handler's options.
RemoteAuthenticationHandler<TOptions>: Adds the abstract HandleRemoteAuthenticateAsync and implements HandleAuthenticateAsync to call this method. This is meant to be used for 3rd party authentication, i.e. OAuth/OIDC. It adds an additional constraint to TOptions that requires them to be RemoteAuthenticationOptions which adds a bunch of settings like CallbackPath, CorrelationCookie, Backchannel which are needed to talk to a remote authentication provider.
AuthenticationBuilder: is a new class which is used to group all of the AddXyz() authentication methods. This is returned by services.AddAuthentication() and is where specific authentication methods are expected to add themselves as extension methods, i.e. AddCookie(), AddGoogle(), AddFacebook().
Event changes overview
- Thanks mostly to @PinpointTownes determination to improve the events story, we've refactored and renamed some things to improve the events experience (caveat: if you don't like the names we picked, don't blame @PinpointTownes we overruled him on naming)
At a high level, there 3 main kinds of events:
-
BaseContext events which are the simplest and just expose properties with no real control flow.
-
ResultContext events which revolve around producing AuthenticateResults which expose:
Success(): used to indicate that authentication was successful and to use the Principal/Properties in the event to construct the result.
NoResult(): used to indicate no authentication result is to be returned.
Fail(): used to return a failure.
HandleRequestContext events are used in the IAuthenticationRequestHandler/HandleRemoteAuthenticate methods and adds two more methods:
-
HandleResponse(): used to indicate that the response was generated and the AuthenticationMiddleware should not invoke the rest of the middlewares in the pipeline after it.
-
SkipHandler() used to indicate that this handler is done with the request, but subsequent handlers will be called, as well as any other middleware in the pipeline if none of those handlers handle the request.
AutomaticAuthentication/Challenge have been replaced by Default[Authenticate/Challenge]Scheme
AutomaticAuthentication/Challenge were intended to only be set on one authentication scheme, but there was no good way to enforce this in 1.0. These have been removed as flags on the individual AuthenticationOptions, and have been moved into the base AuthenticationOptions which can be configured in the call to AddAuthentication(authenticationOptions => authenticationOptions.DefaultScheme = "Cookies").
There are now overloads that use the default schemes for each method in IAuthenticationService
DefaultScheme: if specified, all the other defaults will fallback to this value
DefaultAuthenticateScheme: if specified, AuthenticateAsync() will use this scheme, and also the
AuthenticationMiddleware added by UseAuthentication() will use this scheme to set context.User automatically. (Corresponds to AutomaticAuthentication)
DefaultChallengeScheme if specified, ChallengeAsync() will use this scheme, [Authorize] with policies that don't specify schemes will also use this
DefaultSignInScheme is used by SignInAsync() and also by all of the remote auth schemes like Google/Facebook/OIDC/OAuth, typically this would be set to a cookie.
DefaultSignOutScheme is used by SignOutAsync() falls back to DefaultSignInScheme
DefaultForbidScheme is used by ForbidAsync(), falls back to DefaultChallengeScheme
"Windows" Authentication(HttpSys/IISIntegration)
The host behavior hasn't changed too much, but now they each register a single "Windows" authentication scheme. Also IISIntegration now conditionally registers the handler only if windows auth is enabled in IIS (if you have the latest version of ANCM, otherwise it's always registered as before).
Authorization changes
IAuthorizationService.AuthorizeAsync now returns AuthorizationResult instead of bool
In order to enable scenarios around authorization failures, IAuthorizationService now returns a result object which allows access to the reasons why AuthorizeAsync failed (either context.Fail(), or a list of failed requirements)
Removal of ChallengeBehavior => new PolicyEvaluator
In Auth 1.0, there was a ChallengeBehavior enum that was used to specify either Automatic/Unauthorized/Forbid behaviors to signal to the auth middleware what behavior the caller wanted. Automatic was the default and would go down the Forbid(403) code path if the middleware already had an authentication ticket, otherwise would result in Unauthorized(401).
In Auth 2.0, this behavior has been moved into a new Authorization.Policy package, which introduces the IPolicyEvaluator which uses both IAuthenticationService (when requested via policy.AuthenticationSchemes), and IAuthorizationService to decide whether to return a tri state PolicyAuthorizationResult (Succeeded/Challenged/Forbidden).
Overview of [Authorize]
The [Authorize] attribute hasn't changed much, but the there were some implementation details that have changed significantly in MVC's AuthorizeFilter, and here's an overview of how things work:
AuthorizeFilter source
- An effective policy is computed by combining all of the requested policies/requirements from all relevant
[Authorize] attributes on the controller/method/globally.
IPolicyEvaluator.AuthenticateAsync(policy, httpContext) is called, by default, if the has specified any policy.AuthenticationSchemes, AuthenticateAsync will be called on each scheme, and each resulting ClaimsPrincipal will be merged together into a single ClaimsPrincipal set on context.User. If no schemes were specified, the evaluator will attempt to use context.User if it contains an authenticated user. This is usually the normal code path, as DefaultScheme/DefaultAuthenticateScheme will be set to the main application cookie, and the AuthenticationMiddleware will have already set context.User using this scheme's AuthenticateAsync()
Authenticate logic
- If
AllowAnoynmous was specified, authorization is skipped, and the filter logic short circuits and is done.
- Finally,
IPolicyEvaluator.AuthenticateAsync(policy, authenticationResult, httpContext) is called with the result from step 2. This just basically turns into a call to IAuthorizationService.AuthorizeAsync, and the result is used to determine the appropriate Challenge/ForbidResult if needed.
Claims Transformation
Simpler, new IClaimsTransformation service with a single method:
Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
We call this on any successful AuthenticateAsync call.
services.AddSingleton<IClaimsTransformation, ClaimsTransformer>();
private class ClaimsTransformer : IClaimsTransformation {
// Can consume services from DI as needed, including scoped DbContexts
public ClaimsTransformer(IHttpContextAccessor httpAccessor) { }
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal p) {
p.AddIdentity(new ClaimsIdentity());
return Task.FromResult(p);
}
}
Known issues/breaking changes:
- In 1.0, it was possible to configure different Authentication middleware with branching, this is no longer possible with a single middleware and shared services across all branches. A workaround could be to use different schemes/options for each branch.
Note: this issue is closed, you should use #1338
Summary:
The old 1.0 Authentication stack no longer will work, and is obsolete in 2.0. All authentication related functionality must be migrated to the 2.0 stack, any interop between old and new must be side by side apps, as opposed to mixing 1.0 auth code with 2.0 auth code in the same app. Cookie authentication will interop, so 1.0 Cookies and 2.0 Cookies will be valid in both apps if configured properly. The main motivation was to move to a more flexible service based
IAuthenticationServiceand away from the old middleware/IAuthenticationManagerdesign that came over from Microsoft.Owin.IAuthenticationManager(akahttpContext.Authentication) is now obsoleteThis was the main entry point into the old auth system. This has now been replaced with a new set of
HttpContextextensions that live in theMicrosoft.AspNetCore.Authenticationnamespace and remain very similar:Configure():
UseXyzAuthenticationhas been replaced by ConfigureService(): AddXyz()In Auth 1.0, every auth scheme had its own middleware, and startup looked something like this:
In Auth 2.0, there is now only a single Authentication middleware, and each authentication scheme is registered during
ConfigureServices, andUseIdentity()is no longer required (since it was just calling UseCookie 4 times underneath the covers)New
Microsoft.AspNetCore.Authentication.Core/AbstractionsAll of the old Authentication namespaces in HttpAbstractions have been deprecated. The new Auth 2.0 stack lives in two new packages inside the HttpAbstractions repo:
Microsoft.AspNetCore.Authentication.Core/Abstractions.Brief overview:
IAuthenticationService: used by theHttpContextextension methods to expose the 5 main operationsAuthenticate/Challenge/Forbid/SignIn/SignOutIAuthenticationHandler: Defines the required operations for all handlers: Authenticate/Challenge/ForbidIAuthenticationSignIn/OutHandler: Implemented to add the SignIn/SignOut methods respectivelyIAuthenticationRequestHandler: Implemented by handlers that need to participate in request handler, i.e. remote authentication schemes like OAuth/OIDC that need to process 3rd party auth responses.AuthenticationScheme: represents a logical named authentication scheme to target for any givenIAuthenticationServicemethod, it binds the scheme name (and optional display name) to anIAuthenticationHandlerwhich implements the scheme specific logic.IAuthenticationSchemeProvider: responsible for managing which schemes are supported, and what the defaults are for each operation (the default implementation just reads from theAuthenticationOptions)IAuthenticationHandlerProvider: responsible for returning the correct handler instance for a given scheme and request.IAuthenticationFeature: used to capture the original request path/pathbase so redirects can be computed property after anapp.Map()Types that are mostly unchanged, just with new homes:
AuthenticationProperties: metadata for authentication operations.AuthenticationTicket: used to store a claims principal (user) + authentication propertiesAuthenticateResult: return value forAuthenticateAsync, contains either a ticket, or a failureSecurity repo:
Microsoft.AspNetCore.Authentication/ AuthenticationHandler changesAll of the core abstractions and services for authentication live in HttpAbstracions, but there's an additional layer of base classes/functionality targeted towards implementation of AuthenticationHandlers. This is also where the AuthenticationMiddleware lives. The handlers themselves for the various implementations aren't drastically different, but there were a fair amount of changes
Microsoft.AspNetCore.AuthenticationOverview:
AuthenticationMiddleware:UseAuthentication()adds this middleware which does two things. By default It will automatically Authenticate usingAuthenticationOptions.DefaultAuthenticateSchemeto sethttpContext.Userif specified. It also will giveIAuthenticationRequestHandler'sa chance to handle the request.AuthenticationSchemeOptions: Base class for options used with theAuthenticationHandlerbase class, it defines two common properties in Events and ClaimsIssuer, as well as a virtualValidate()method that will be called on every request by the handler.AuthenticationHandler<TOptions>: Abstract base class for handlers who must implementHandleAuthenticateAsync. The rest ofIAuthenticationHandleris implemented with some reasonable defaults, Challenge(401)/Forbid(403), and logic to handle per request initialization usingIOptionsMonitor<TOptions>.Get(authenticationScheme.Name)to resolve the handler's options.RemoteAuthenticationHandler<TOptions>: Adds the abstractHandleRemoteAuthenticateAsyncand implementsHandleAuthenticateAsyncto call this method. This is meant to be used for 3rd party authentication, i.e. OAuth/OIDC. It adds an additional constraint to TOptions that requires them to beRemoteAuthenticationOptionswhich adds a bunch of settings like CallbackPath, CorrelationCookie, Backchannel which are needed to talk to a remote authentication provider.AuthenticationBuilder: is a new class which is used to group all of the AddXyz() authentication methods. This is returned byservices.AddAuthentication()and is where specific authentication methods are expected to add themselves as extension methods, i.e. AddCookie(), AddGoogle(), AddFacebook().Event changes overview
At a high level, there 3 main kinds of events:
BaseContextevents which are the simplest and just expose properties with no real control flow.ResultContextevents which revolve around producingAuthenticateResults which expose:Success(): used to indicate that authentication was successful and to use the Principal/Properties in the event to construct the result.NoResult(): used to indicate no authentication result is to be returned.Fail(): used to return a failure.HandleRequestContextevents are used in the IAuthenticationRequestHandler/HandleRemoteAuthenticate methods and adds two more methods:HandleResponse(): used to indicate that the response was generated and the AuthenticationMiddleware should not invoke the rest of the middlewares in the pipeline after it.SkipHandler()used to indicate that this handler is done with the request, but subsequent handlers will be called, as well as any other middleware in the pipeline if none of those handlers handle the request.AutomaticAuthentication/Challengehave been replaced byDefault[Authenticate/Challenge]SchemeAutomaticAuthentication/Challengewere intended to only be set on one authentication scheme, but there was no good way to enforce this in 1.0. These have been removed as flags on the individual AuthenticationOptions, and have been moved into the base AuthenticationOptions which can be configured in the call toAddAuthentication(authenticationOptions => authenticationOptions.DefaultScheme = "Cookies").There are now overloads that use the default schemes for each method in
IAuthenticationServiceDefaultScheme: if specified, all the other defaults will fallback to this valueDefaultAuthenticateScheme: if specified,AuthenticateAsync()will use this scheme, and also theAuthenticationMiddlewareadded byUseAuthentication()will use this scheme to setcontext.Userautomatically. (Corresponds to AutomaticAuthentication)DefaultChallengeSchemeif specified,ChallengeAsync()will use this scheme,[Authorize]with policies that don't specify schemes will also use thisDefaultSignInSchemeis used bySignInAsync()and also by all of the remote auth schemes like Google/Facebook/OIDC/OAuth, typically this would be set to a cookie.DefaultSignOutSchemeis used bySignOutAsync()falls back toDefaultSignInSchemeDefaultForbidSchemeis used byForbidAsync(), falls back toDefaultChallengeScheme"Windows" Authentication(HttpSys/IISIntegration)
The host behavior hasn't changed too much, but now they each register a single "Windows" authentication scheme. Also IISIntegration now conditionally registers the handler only if windows auth is enabled in IIS (if you have the latest version of ANCM, otherwise it's always registered as before).
Authorization changes
IAuthorizationService.AuthorizeAsyncnow returnsAuthorizationResultinstead of boolIn order to enable scenarios around authorization failures,
IAuthorizationServicenow returns a result object which allows access to the reasons whyAuthorizeAsyncfailed (eithercontext.Fail(), or a list of failed requirements)Removal of
ChallengeBehavior=> newPolicyEvaluatorIn Auth 1.0, there was a
ChallengeBehaviorenum that was used to specify either Automatic/Unauthorized/Forbid behaviors to signal to the auth middleware what behavior the caller wanted. Automatic was the default and would go down the Forbid(403) code path if the middleware already had an authentication ticket, otherwise would result in Unauthorized(401).In Auth 2.0, this behavior has been moved into a new Authorization.Policy package, which introduces the
IPolicyEvaluatorwhich uses bothIAuthenticationService(when requested via policy.AuthenticationSchemes), andIAuthorizationServiceto decide whether to return a tri statePolicyAuthorizationResult(Succeeded/Challenged/Forbidden).Overview of
[Authorize]The
[Authorize]attribute hasn't changed much, but the there were some implementation details that have changed significantly in MVC'sAuthorizeFilter, and here's an overview of how things work:AuthorizeFilter source
[Authorize]attributes on the controller/method/globally.IPolicyEvaluator.AuthenticateAsync(policy, httpContext)is called, by default, if the has specified anypolicy.AuthenticationSchemes,AuthenticateAsyncwill be called on each scheme, and each resultingClaimsPrincipalwill be merged together into a single ClaimsPrincipal set on context.User. If no schemes were specified, the evaluator will attempt to use context.User if it contains an authenticated user. This is usually the normal code path, asDefaultScheme/DefaultAuthenticateSchemewill be set to the main application cookie, and theAuthenticationMiddlewarewill have already set context.User using this scheme'sAuthenticateAsync()Authenticate logic
AllowAnoynmouswas specified, authorization is skipped, and the filter logic short circuits and is done.IPolicyEvaluator.AuthenticateAsync(policy, authenticationResult, httpContext)is called with the result from step 2. This just basically turns into a call toIAuthorizationService.AuthorizeAsync, and the result is used to determine the appropriateChallenge/ForbidResultif needed.Claims Transformation
Simpler, new
IClaimsTransformationservice with a single method:Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)We call this on any successful
AuthenticateAsynccall.Known issues/breaking changes: