#690 OIDC & JWT event refactoring.

This commit is contained in:
Chris R 2016-03-15 15:19:25 -07:00
parent 6a0e58e3ff
commit 3f596108aa
26 changed files with 278 additions and 490 deletions

View File

@ -12,7 +12,7 @@
"commandName": "IISExpress", "commandName": "IISExpress",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"ASPNET_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
}, },
"web": { "web": {

View File

@ -61,8 +61,6 @@ namespace JwtBearerSample
app.UseJwtBearerAuthentication(new JwtBearerOptions app.UseJwtBearerAuthentication(new JwtBearerOptions
{ {
AutomaticAuthenticate = true,
AutomaticChallenge = true,
// You also need to update /wwwroot/app/scripts/app.js // You also need to update /wwwroot/app/scripts/app.js
Authority = Configuration["jwt:authority"], Authority = Configuration["jwt:authority"],
Audience = Configuration["jwt:audience"] Audience = Configuration["jwt:audience"]
@ -74,14 +72,14 @@ namespace JwtBearerSample
// Use this if options.AutomaticAuthenticate = false // Use this if options.AutomaticAuthenticate = false
// var user = await context.Authentication.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); // var user = await context.Authentication.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
var user = context.User; // We can do this because of options.AutomaticAuthenticate = true; above. var user = context.User; // We can do this because of options.AutomaticAuthenticate = true;
if (user?.Identity?.IsAuthenticated ?? false) if (user?.Identity?.IsAuthenticated ?? false)
{ {
await next(); await next();
} }
else else
{ {
// We can do this because of options.AutomaticChallenge = true; above // We can do this because of options.AutomaticChallenge = true;
await context.Authentication.ChallengeAsync(); await context.Authentication.ChallengeAsync();
} }
}); });

View File

@ -15,7 +15,7 @@
"web": "JwtBearerSample" "web": "JwtBearerSample"
}, },
"frameworks": { "frameworks": {
"dnx451": {}, "dnx451": { },
"netstandardapp1.5": { "netstandardapp1.5": {
"imports": [ "imports": [
"dnxcore50" "dnxcore50"
@ -30,5 +30,9 @@
"**.user", "**.user",
"**.vspscc" "**.vspscc"
], ],
"content": [
"project.json",
"wwwroot"
],
"userSecretsId": "aspnet5-JwtBearerSample-20151210102827" "userSecretsId": "aspnet5-JwtBearerSample-20151210102827"
} }

View File

@ -12,7 +12,7 @@
"commandName": "IISExpress", "commandName": "IISExpress",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"Hosting:Environment": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
}, },
"web": { "web": {

View File

@ -64,10 +64,7 @@ namespace OpenIdConnect.AzureAdSample
app.UseIISPlatformHandler(); app.UseIISPlatformHandler();
app.UseCookieAuthentication(new CookieAuthenticationOptions app.UseCookieAuthentication(new CookieAuthenticationOptions());
{
AutomaticAuthenticate = true
});
var clientId = Configuration["oidc:clientid"]; var clientId = Configuration["oidc:clientid"];
var clientSecret = Configuration["oidc:clientsecret"]; var clientSecret = Configuration["oidc:clientsecret"];

View File

@ -18,5 +18,8 @@
"commands": { "commands": {
"web": "OpenIdConnect.AzureAdSample" "web": "OpenIdConnect.AzureAdSample"
}, },
"content": [
"project.json"
],
"userSecretsId": "aspnet5-OpenIdConnectSample-20151210110318" "userSecretsId": "aspnet5-OpenIdConnectSample-20151210110318"
} }

View File

@ -12,7 +12,7 @@
"commandName": "IISExpress", "commandName": "IISExpress",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"ASPNET_ENV": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
}, },
"web": { "web": {

View File

@ -59,10 +59,7 @@ namespace OpenIdConnectSample
app.UseIISPlatformHandler(); app.UseIISPlatformHandler();
app.UseCookieAuthentication(new CookieAuthenticationOptions app.UseCookieAuthentication(new CookieAuthenticationOptions());
{
AutomaticAuthenticate = true
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{ {

View File

@ -9,7 +9,7 @@
"Microsoft.NETCore.Platforms": "1.0.1-*" "Microsoft.NETCore.Platforms": "1.0.1-*"
}, },
"frameworks": { "frameworks": {
"dnx451": {}, "dnx451": { },
"netstandardapp1.5": { "netstandardapp1.5": {
"imports": [ "imports": [
"dnxcore50" "dnxcore50"
@ -22,5 +22,8 @@
"commands": { "commands": {
"web": "OpenIdConnectSample" "web": "OpenIdConnectSample"
}, },
"content": [
"project.json"
],
"userSecretsId": "aspnet5-OpenIdConnectSample-20151210110318" "userSecretsId": "aspnet5-OpenIdConnectSample-20151210110318"
} }

View File

@ -18,17 +18,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
/// <summary> /// <summary>
/// Invoked when a protocol message is first received. /// Invoked when a protocol message is first received.
/// </summary> /// </summary>
Task ReceivingToken(ReceivingTokenContext context); Task MessageReceived(MessageReceivedContext context);
/// <summary>
/// Invoked with the security token that has been extracted from the protocol message.
/// </summary>
Task ReceivedToken(ReceivedTokenContext context);
/// <summary> /// <summary>
/// Invoked after the security token has passed validation and a ClaimsIdentity has been generated. /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
/// </summary> /// </summary>
Task ValidatedToken(ValidatedTokenContext context); Task TokenValidated(TokenValidatedContext context);
/// <summary> /// <summary>
/// Invoked to apply a challenge sent back to the caller. /// Invoked to apply a challenge sent back to the caller.

View File

@ -19,17 +19,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
/// <summary> /// <summary>
/// Invoked when a protocol message is first received. /// Invoked when a protocol message is first received.
/// </summary> /// </summary>
public Func<ReceivingTokenContext, Task> OnReceivingToken { get; set; } = context => Task.FromResult(0); public Func<MessageReceivedContext, Task> OnMessageReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked with the security token that has been extracted from the protocol message.
/// </summary>
public Func<ReceivedTokenContext, Task> OnReceivedToken { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked after the security token has passed validation and a ClaimsIdentity has been generated. /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
/// </summary> /// </summary>
public Func<ValidatedTokenContext, Task> OnValidatedToken { get; set; } = context => Task.FromResult(0); public Func<TokenValidatedContext, Task> OnTokenValidated { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked before a challenge is sent back to the caller. /// Invoked before a challenge is sent back to the caller.
@ -38,11 +33,9 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context); public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
public virtual Task ReceivingToken(ReceivingTokenContext context) => OnReceivingToken(context); public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
public virtual Task ReceivedToken(ReceivedTokenContext context) => OnReceivedToken(context); public virtual Task TokenValidated(TokenValidatedContext context) => OnTokenValidated(context);
public virtual Task ValidatedToken(ValidatedTokenContext context) => OnValidatedToken(context);
public virtual Task Challenge(JwtBearerChallengeContext context) => OnChallenge(context); public virtual Task Challenge(JwtBearerChallengeContext context) => OnChallenge(context);
} }

View File

@ -6,9 +6,9 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.JwtBearer namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
public class ReceivingTokenContext : BaseJwtBearerContext public class MessageReceivedContext : BaseJwtBearerContext
{ {
public ReceivingTokenContext(HttpContext context, JwtBearerOptions options) public MessageReceivedContext(HttpContext context, JwtBearerOptions options)
: base(context, options) : base(context, options)
{ {
} }

View File

@ -1,18 +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.JwtBearer
{
public class ReceivedTokenContext : BaseJwtBearerContext
{
public ReceivedTokenContext(HttpContext context, JwtBearerOptions options)
: base(context, options)
{
}
public string Token { get; set; }
}
}

View File

@ -3,14 +3,17 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.AspNetCore.Authentication.JwtBearer namespace Microsoft.AspNetCore.Authentication.JwtBearer
{ {
public class ValidatedTokenContext : BaseJwtBearerContext public class TokenValidatedContext : BaseJwtBearerContext
{ {
public ValidatedTokenContext(HttpContext context, JwtBearerOptions options) public TokenValidatedContext(HttpContext context, JwtBearerOptions options)
: base(context, options) : base(context, options)
{ {
} }
public SecurityToken SecurityToken { get; set; }
} }
} }

View File

@ -28,24 +28,21 @@ 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
var receivingTokenContext = new ReceivingTokenContext(Context, Options); var messageReceivedContext = new MessageReceivedContext(Context, Options);
// event can set the token // event can set the token
await Options.Events.ReceivingToken(receivingTokenContext); await Options.Events.MessageReceived(messageReceivedContext);
if (receivingTokenContext.HandledResponse) if (messageReceivedContext.CheckEventResult(out result))
{ {
return AuthenticateResult.Success(receivingTokenContext.Ticket); return result;
}
if (receivingTokenContext.Skipped)
{
return AuthenticateResult.Skip();
} }
// If application retrieved token from somewhere else, use that. // If application retrieved token from somewhere else, use that.
token = receivingTokenContext.Token; token = messageReceivedContext.Token;
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
@ -69,22 +66,6 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
} }
// notify user token was received
var receivedTokenContext = new ReceivedTokenContext(Context, Options)
{
Token = token,
};
await Options.Events.ReceivedToken(receivedTokenContext);
if (receivedTokenContext.HandledResponse)
{
return AuthenticateResult.Success(receivedTokenContext.Ticket);
}
if (receivedTokenContext.Skipped)
{
return AuthenticateResult.Skip();
}
if (_configuration == null && Options.ConfigurationManager != null) if (_configuration == null && Options.ConfigurationManager != null)
{ {
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
@ -138,20 +119,18 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Logger.TokenValidationSucceeded(); Logger.TokenValidationSucceeded();
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme);
var validatedTokenContext = new ValidatedTokenContext(Context, Options) var tokenValidatedContext = new TokenValidatedContext(Context, Options)
{ {
Ticket = ticket Ticket = ticket,
SecurityToken = validatedToken,
}; };
await Options.Events.ValidatedToken(validatedTokenContext); await Options.Events.TokenValidated(tokenValidatedContext);
if (validatedTokenContext.HandledResponse) if (tokenValidatedContext.CheckEventResult(out result))
{ {
return AuthenticateResult.Success(validatedTokenContext.Ticket); return result;
}
if (validatedTokenContext.Skipped)
{
return AuthenticateResult.Skip();
} }
ticket = tokenValidatedContext.Ticket;
if (Options.SaveToken) if (Options.SaveToken)
{ {
@ -173,13 +152,9 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}; };
await Options.Events.AuthenticationFailed(authenticationFailedContext); await Options.Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.HandledResponse) if (authenticationFailedContext.CheckEventResult(out result))
{ {
return AuthenticateResult.Success(authenticationFailedContext.Ticket); return result;
}
if (authenticationFailedContext.Skipped)
{
return AuthenticateResult.Skip();
} }
return AuthenticateResult.Fail(authenticationFailedContext.Exception); return AuthenticateResult.Fail(authenticationFailedContext.Exception);
@ -197,13 +172,9 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}; };
await Options.Events.AuthenticationFailed(authenticationFailedContext); await Options.Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.HandledResponse) if (authenticationFailedContext.CheckEventResult(out result))
{ {
return AuthenticateResult.Success(authenticationFailedContext.Ticket); return result;
}
if (authenticationFailedContext.Skipped)
{
return AuthenticateResult.Skip();
} }
throw; throw;

View File

@ -25,6 +25,8 @@ namespace Microsoft.AspNetCore.Builder
public JwtBearerOptions() : base() public JwtBearerOptions() : base()
{ {
AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme; AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;
AutomaticAuthenticate = true;
AutomaticChallenge = true;
} }
/// <summary> /// <summary>

View File

@ -1,20 +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;
using Microsoft.AspNetCore.Http.Authentication;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
public class AuthorizationResponseReceivedContext : BaseOpenIdConnectContext
{
public AuthorizationResponseReceivedContext(HttpContext context, OpenIdConnectOptions options, AuthenticationProperties properties)
: base(context, options)
{
Properties = properties;
}
public AuthenticationProperties Properties { get; }
}
}

View File

@ -15,21 +15,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary> /// </summary>
Task AuthenticationFailed(AuthenticationFailedContext context); Task AuthenticationFailed(AuthenticationFailedContext context);
/// <summary>
/// Invoked after the id token has passed validation and a ClaimsIdentity has been generated.
/// </summary>
Task AuthenticationValidated(AuthenticationValidatedContext context);
/// <summary> /// <summary>
/// Invoked after security token validation if an authorization code is present in the protocol message. /// Invoked after security token validation if an authorization code is present in the protocol message.
/// </summary> /// </summary>
Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context); Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context);
/// <summary>
/// Invoked when an authorization response is received.
/// </summary>
Task AuthorizationResponseReceived(AuthorizationResponseReceivedContext context);
/// <summary> /// <summary>
/// Invoked when a protocol message is first received. /// Invoked when a protocol message is first received.
/// </summary> /// </summary>
@ -38,18 +28,23 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary> /// <summary>
/// Invoked before redirecting to the identity provider to authenticate. /// Invoked before redirecting to the identity provider to authenticate.
/// </summary> /// </summary>
Task RedirectToAuthenticationEndpoint(RedirectContext context); Task RedirectToIdentityProvider(RedirectContext context);
/// <summary> /// <summary>
/// Invoked before redirecting to the identity provider to sign out. /// Invoked before redirecting to the identity provider to sign out.
/// </summary> /// </summary>
Task RedirectToEndSessionEndpoint(RedirectContext context); Task RedirectToIdentityProviderForSignOut(RedirectContext context);
/// <summary> /// <summary>
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint. /// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// </summary> /// </summary>
Task TokenResponseReceived(TokenResponseReceivedContext context); Task TokenResponseReceived(TokenResponseReceivedContext context);
/// <summary>
/// Invoked when an IdToken has been validated and produced an AuthenticationTicket.
/// </summary>
Task TokenValidated(TokenValidatedContext context);
/// <summary> /// <summary>
/// Invoked when user information is retrieved from the UserInfoEndpoint. /// Invoked when user information is retrieved from the UserInfoEndpoint.
/// </summary> /// </summary>

View File

@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
@ -17,5 +18,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// 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

@ -16,21 +16,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// </summary> /// </summary>
public Func<AuthenticationFailedContext, Task> OnAuthenticationFailed { get; set; } = context => Task.FromResult(0); public Func<AuthenticationFailedContext, Task> OnAuthenticationFailed { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked after the id token has passed validation and a ClaimsIdentity has been generated.
/// </summary>
public Func<AuthenticationValidatedContext, Task> OnAuthenticationValidated { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked after security token validation if an authorization code is present in the protocol message. /// Invoked after security token validation if an authorization code is present in the protocol message.
/// </summary> /// </summary>
public Func<AuthorizationCodeReceivedContext, Task> OnAuthorizationCodeReceived { get; set; } = context => Task.FromResult(0); public Func<AuthorizationCodeReceivedContext, Task> OnAuthorizationCodeReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked when an authorization response is received.
/// </summary>
public Func<AuthorizationResponseReceivedContext, Task> OnAuthorizationResponseReceived { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked when a protocol message is first received. /// Invoked when a protocol message is first received.
/// </summary> /// </summary>
@ -39,18 +29,23 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary> /// <summary>
/// Invoked before redirecting to the identity provider to authenticate. /// Invoked before redirecting to the identity provider to authenticate.
/// </summary> /// </summary>
public Func<RedirectContext, Task> OnRedirectToAuthenticationEndpoint { get; set; } = context => Task.FromResult(0); public Func<RedirectContext, Task> OnRedirectToIdentityProvider { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked before redirecting to the identity provider to sign out. /// Invoked before redirecting to the identity provider to sign out.
/// </summary> /// </summary>
public Func<RedirectContext, Task> OnRedirectToEndSessionEndpoint { get; set; } = context => Task.FromResult(0); public Func<RedirectContext, Task> OnRedirectToIdentityProviderForSignOut { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint. /// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// </summary> /// </summary>
public Func<TokenResponseReceivedContext, Task> OnTokenResponseReceived { get; set; } = context => Task.FromResult(0); public Func<TokenResponseReceivedContext, Task> OnTokenResponseReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked when an IdToken has been validated and produced an AuthenticationTicket.
/// </summary>
public Func<TokenValidatedContext, Task> OnTokenValidated { get; set; } = context => Task.FromResult(0);
/// <summary> /// <summary>
/// Invoked when user information is retrieved from the UserInfoEndpoint. /// Invoked when user information is retrieved from the UserInfoEndpoint.
/// </summary> /// </summary>
@ -58,20 +53,18 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context); public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
public virtual Task AuthenticationValidated(AuthenticationValidatedContext context) => OnAuthenticationValidated(context);
public virtual Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context) => OnAuthorizationCodeReceived(context); public virtual Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context) => OnAuthorizationCodeReceived(context);
public virtual Task AuthorizationResponseReceived(AuthorizationResponseReceivedContext context) => OnAuthorizationResponseReceived(context);
public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context); public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
public virtual Task RedirectToAuthenticationEndpoint(RedirectContext context) => OnRedirectToAuthenticationEndpoint(context); public virtual Task RedirectToIdentityProvider(RedirectContext context) => OnRedirectToIdentityProvider(context);
public virtual Task RedirectToEndSessionEndpoint(RedirectContext context) => OnRedirectToEndSessionEndpoint(context); public virtual Task RedirectToIdentityProviderForSignOut(RedirectContext context) => OnRedirectToIdentityProviderForSignOut(context);
public virtual Task TokenResponseReceived(TokenResponseReceivedContext context) => OnTokenResponseReceived(context); public virtual Task TokenResponseReceived(TokenResponseReceivedContext context) => OnTokenResponseReceived(context);
public virtual Task TokenValidated(TokenValidatedContext context) => OnTokenValidated(context);
public virtual Task UserInformationReceived(UserInformationReceivedContext context) => OnUserInformationReceived(context); public virtual Task UserInformationReceived(UserInformationReceivedContext context) => OnUserInformationReceived(context);
} }
} }

View File

@ -1,6 +1,8 @@
// 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.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Http.Authentication;
@ -8,16 +10,22 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
public class AuthenticationValidatedContext : BaseOpenIdConnectContext public class TokenValidatedContext : BaseOpenIdConnectContext
{ {
public AuthenticationValidatedContext(HttpContext context, OpenIdConnectOptions options, AuthenticationProperties properties) /// <summary>
/// Creates a <see cref="TokenValidatedContext"/>
/// </summary>
public TokenValidatedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options) : base(context, options)
{ {
Properties = properties;
} }
public AuthenticationProperties Properties { get; } public AuthenticationProperties Properties { get; set; }
public JwtSecurityToken SecurityToken { get; set; }
public OpenIdConnectMessage TokenEndpointResponse { get; set; } public OpenIdConnectMessage TokenEndpointResponse { get; set; }
public string Nonce { get; set; }
} }
} }

View File

@ -7,10 +7,10 @@ namespace Microsoft.Extensions.Logging
{ {
internal static class LoggingExtensions internal static class LoggingExtensions
{ {
private static Action<ILogger, Exception> _redirectToEndSessionEndpointHandledResponse; private static Action<ILogger, Exception> _redirectToIdentityProviderForSignOutHandledResponse;
private static Action<ILogger, Exception> _redirectToEndSessionEndpointSkipped; private static Action<ILogger, Exception> _redirectToIdentityProviderForSignOutSkipped;
private static Action<ILogger, Exception> _redirectToAuthenticationEndpointHandledResponse; private static Action<ILogger, Exception> _redirectToIdentityProviderHandledResponse;
private static Action<ILogger, Exception> _redirectToAuthenticationEndpointSkipped; 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;
@ -19,20 +19,17 @@ namespace Microsoft.Extensions.Logging
private static Action<ILogger, string, Exception> _messageReceived; private static Action<ILogger, string, Exception> _messageReceived;
private static Action<ILogger, Exception> _messageReceivedContextHandledResponse; private static Action<ILogger, Exception> _messageReceivedContextHandledResponse;
private static Action<ILogger, Exception> _messageReceivedContextSkipped; private static Action<ILogger, Exception> _messageReceivedContextSkipped;
private static Action<ILogger, Exception> _authorizationResponseReceived;
private static Action<ILogger, Exception> _authorizationCodeReceived; private static Action<ILogger, Exception> _authorizationCodeReceived;
private static Action<ILogger, Exception> _configurationManagerRequestRefreshCalled; private static Action<ILogger, Exception> _configurationManagerRequestRefreshCalled;
private static Action<ILogger, Exception> _tokenResponseReceived; private static Action<ILogger, Exception> _tokenResponseReceived;
private static Action<ILogger, Exception> _authorizationResponseReceivedHandledResponse; private static Action<ILogger, Exception> _tokenValidatedHandledResponse;
private static Action<ILogger, Exception> _authorizationResponseReceivedSkipped; private static Action<ILogger, Exception> _tokenValidatedSkipped;
private static Action<ILogger, Exception> _authenticationFailedContextHandledResponse; private static Action<ILogger, Exception> _authenticationFailedContextHandledResponse;
private static Action<ILogger, Exception> _authenticationFailedContextSkipped; private static Action<ILogger, Exception> _authenticationFailedContextSkipped;
private static Action<ILogger, Exception> _authorizationCodeReceivedContextHandledResponse; private static Action<ILogger, Exception> _authorizationCodeReceivedContextHandledResponse;
private static Action<ILogger, Exception> _authorizationCodeReceivedContextSkipped; private static Action<ILogger, Exception> _authorizationCodeReceivedContextSkipped;
private static Action<ILogger, Exception> _authorizationCodeRedeemedContextHandledResponse; private static Action<ILogger, Exception> _tokenResponseReceivedHandledResponse;
private static Action<ILogger, Exception> _authorizationCodeRedeemedContextSkipped; private static Action<ILogger, Exception> _tokenResponseReceivedSkipped;
private static Action<ILogger, Exception> _authenticationValidatedHandledResponse;
private static Action<ILogger, Exception> _authenticationValidatedtSkipped;
private static Action<ILogger, string, Exception> _userInformationReceived; private static Action<ILogger, string, Exception> _userInformationReceived;
private static Action<ILogger, Exception> _userInformationReceivedHandledResponse; private static Action<ILogger, Exception> _userInformationReceivedHandledResponse;
private static Action<ILogger, Exception> _userInformationReceivedSkipped; private static Action<ILogger, Exception> _userInformationReceivedSkipped;
@ -54,14 +51,14 @@ namespace Microsoft.Extensions.Logging
static LoggingExtensions() static LoggingExtensions()
{ {
// Final // Final
_redirectToEndSessionEndpointHandledResponse = LoggerMessage.Define( _redirectToIdentityProviderForSignOutHandledResponse = LoggerMessage.Define(
eventId: 1, eventId: 1,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "RedirectToEndSessionEndpoint.HandledResponse"); formatString: "RedirectToIdentityProviderForSignOut.HandledResponse");
_redirectToEndSessionEndpointSkipped = LoggerMessage.Define( _redirectToIdentityProviderForSignOutSkipped = LoggerMessage.Define(
eventId: 2, eventId: 2,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "RedirectToEndSessionEndpoint.Skipped"); formatString: "RedirectToIdentityProviderForSignOut.Skipped");
_invalidLogoutQueryStringRedirectUrl = LoggerMessage.Define<string>( _invalidLogoutQueryStringRedirectUrl = LoggerMessage.Define<string>(
eventId: 3, eventId: 3,
logLevel: LogLevel.Warning, logLevel: LogLevel.Warning,
@ -74,14 +71,14 @@ namespace Microsoft.Extensions.Logging
eventId: 5, eventId: 5,
logLevel: LogLevel.Trace, logLevel: LogLevel.Trace,
formatString: "Using properties.RedirectUri for 'local redirect' post authentication: '{RedirectUri}'."); formatString: "Using properties.RedirectUri for 'local redirect' post authentication: '{RedirectUri}'.");
_redirectToAuthenticationEndpointHandledResponse = LoggerMessage.Define( _redirectToIdentityProviderHandledResponse = LoggerMessage.Define(
eventId: 6, eventId: 6,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "RedirectToAuthenticationEndpoint.HandledResponse"); formatString: "RedirectToIdentityProvider.HandledResponse");
_redirectToAuthenticationEndpointSkipped = LoggerMessage.Define( _redirectToIdentityProviderSkipped = LoggerMessage.Define(
eventId: 7, eventId: 7,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "RedirectToAuthenticationEndpoint.Skipped"); formatString: "RedirectToIdentityProvider.Skipped");
_invalidAuthenticationRequestUrl = LoggerMessage.Define<string>( _invalidAuthenticationRequestUrl = LoggerMessage.Define<string>(
eventId: 8, eventId: 8,
logLevel: LogLevel.Warning, logLevel: LogLevel.Warning,
@ -106,18 +103,14 @@ namespace Microsoft.Extensions.Logging
eventId: 13, eventId: 13,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "Updating configuration"); formatString: "Updating configuration");
_authorizationResponseReceived = LoggerMessage.Define( _tokenValidatedHandledResponse = LoggerMessage.Define(
eventId: 14,
logLevel: LogLevel.Trace,
formatString: "Authorization response received.");
_authorizationResponseReceivedHandledResponse = LoggerMessage.Define(
eventId: 15, eventId: 15,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "AuthorizationResponseReceived.HandledResponse"); formatString: "TokenValidated.HandledResponse");
_authorizationResponseReceivedSkipped = LoggerMessage.Define( _tokenValidatedSkipped = LoggerMessage.Define(
eventId: 16, eventId: 16,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "AuthorizationResponseReceived.Skipped"); formatString: "TokenValidated.Skipped");
_exceptionProcessingMessage = LoggerMessage.Define( _exceptionProcessingMessage = LoggerMessage.Define(
eventId: 17, eventId: 17,
logLevel: LogLevel.Error, logLevel: LogLevel.Error,
@ -174,22 +167,14 @@ namespace Microsoft.Extensions.Logging
eventId: 30, eventId: 30,
logLevel: LogLevel.Trace, logLevel: LogLevel.Trace,
formatString: "Token response received."); formatString: "Token response received.");
_authorizationCodeRedeemedContextHandledResponse = LoggerMessage.Define( _tokenResponseReceivedHandledResponse = LoggerMessage.Define(
eventId: 31, eventId: 31,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "AuthorizationCodeRedeemedContext.HandledResponse"); formatString: "TokenResponseReceived.HandledResponse");
_authorizationCodeRedeemedContextSkipped = LoggerMessage.Define( _tokenResponseReceivedSkipped = LoggerMessage.Define(
eventId: 32, eventId: 32,
logLevel: LogLevel.Debug, logLevel: LogLevel.Debug,
formatString: "AuthorizationCodeRedeemedContext.Skipped"); formatString: "TokenResponseReceived.Skipped");
_authenticationValidatedHandledResponse = LoggerMessage.Define(
eventId: 33,
logLevel: LogLevel.Debug,
formatString: "AuthenticationFailedContext.HandledResponse");
_authenticationValidatedtSkipped = LoggerMessage.Define(
eventId: 34,
logLevel: LogLevel.Debug,
formatString: "AuthenticationFailedContext.Skipped");
_userInformationReceived = LoggerMessage.Define<string>( _userInformationReceived = LoggerMessage.Define<string>(
eventId: 35, eventId: 35,
logLevel: LogLevel.Trace, logLevel: LogLevel.Trace,
@ -258,19 +243,14 @@ namespace Microsoft.Extensions.Logging
_redeemingCodeForTokens(logger, null); _redeemingCodeForTokens(logger, null);
} }
public static void AuthorizationResponseReceived(this ILogger logger) public static void TokenValidatedHandledResponse(this ILogger logger)
{ {
_authorizationResponseReceived(logger, null); _tokenValidatedHandledResponse(logger, null);
} }
public static void AuthorizationResponseReceivedHandledResponse(this ILogger logger) public static void TokenValidatedSkipped(this ILogger logger)
{ {
_authorizationResponseReceivedHandledResponse(logger, null); _tokenValidatedSkipped(logger, null);
}
public static void AuthorizationResponseReceivedSkipped(this ILogger logger)
{
_authorizationResponseReceivedSkipped(logger, null);
} }
public static void AuthorizationCodeReceivedContextHandledResponse(this ILogger logger) public static void AuthorizationCodeReceivedContextHandledResponse(this ILogger logger)
@ -283,24 +263,14 @@ namespace Microsoft.Extensions.Logging
_authorizationCodeReceivedContextSkipped(logger, null); _authorizationCodeReceivedContextSkipped(logger, null);
} }
public static void AuthorizationCodeRedeemedContextHandledResponse(this ILogger logger) public static void TokenResponseReceivedHandledResponse(this ILogger logger)
{ {
_authorizationCodeRedeemedContextHandledResponse(logger, null); _tokenResponseReceivedHandledResponse(logger, null);
} }
public static void AuthorizationCodeRedeemedContextSkipped(this ILogger logger) public static void TokenResponseReceivedSkipped(this ILogger logger)
{ {
_authorizationCodeRedeemedContextSkipped(logger, null); _tokenResponseReceivedSkipped(logger, null);
}
public static void AuthenticationValidatedHandledResponse(this ILogger logger)
{
_authenticationValidatedHandledResponse(logger, null);
}
public static void AuthenticationValidatedSkipped(this ILogger logger)
{
_authenticationValidatedtSkipped(logger, null);
} }
public static void AuthenticationFailedContextHandledResponse(this ILogger logger) public static void AuthenticationFailedContextHandledResponse(this ILogger logger)
@ -328,24 +298,24 @@ namespace Microsoft.Extensions.Logging
_messageReceivedContextSkipped(logger, null); _messageReceivedContextSkipped(logger, null);
} }
public static void RedirectToEndSessionEndpointHandledResponse(this ILogger logger) public static void RedirectToIdentityProviderForSignOutHandledResponse(this ILogger logger)
{ {
_redirectToEndSessionEndpointHandledResponse(logger, null); _redirectToIdentityProviderForSignOutHandledResponse(logger, null);
} }
public static void RedirectToEndSessionEndpointSkipped(this ILogger logger) public static void RedirectToIdentityProviderForSignOutSkipped(this ILogger logger)
{ {
_redirectToEndSessionEndpointSkipped(logger, null); _redirectToIdentityProviderForSignOutSkipped(logger, null);
} }
public static void RedirectToAuthenticationEndpointHandledResponse(this ILogger logger) public static void RedirectToIdentityProviderHandledResponse(this ILogger logger)
{ {
_redirectToAuthenticationEndpointHandledResponse(logger, null); _redirectToIdentityProviderHandledResponse(logger, null);
} }
public static void RedirectToAuthenticationEndpointSkipped(this ILogger logger) public static void RedirectToIdentityProviderSkipped(this ILogger logger)
{ {
_redirectToAuthenticationEndpointSkipped(logger, null); _redirectToIdentityProviderSkipped(logger, null);
} }
public static void UserInformationReceivedHandledResponse(this ILogger logger) public static void UserInformationReceivedHandledResponse(this ILogger logger)

View File

@ -111,15 +111,15 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
ProtocolMessage = message ProtocolMessage = message
}; };
await Options.Events.RedirectToEndSessionEndpoint(redirectContext); await Options.Events.RedirectToIdentityProviderForSignOut(redirectContext);
if (redirectContext.HandledResponse) if (redirectContext.HandledResponse)
{ {
Logger.RedirectToEndSessionEndpointHandledResponse(); Logger.RedirectToIdentityProviderForSignOutHandledResponse();
return; return;
} }
else if (redirectContext.Skipped) else if (redirectContext.Skipped)
{ {
Logger.RedirectToEndSessionEndpointSkipped(); Logger.RedirectToIdentityProviderForSignOutSkipped();
return; return;
} }
@ -169,7 +169,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// Responds to a 401 Challenge. Sends an OpenIdConnect message to the 'identity authority' to obtain an identity. /// Responds to a 401 Challenge. Sends an OpenIdConnect message to the 'identity authority' to obtain an identity.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
/// <remarks>Uses log id's OIDCH-0026 - OIDCH-0050, next num: 37</remarks>
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context) protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
{ {
if (context == null) if (context == null)
@ -230,15 +229,15 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
ProtocolMessage = message ProtocolMessage = message
}; };
await Options.Events.RedirectToAuthenticationEndpoint(redirectContext); await Options.Events.RedirectToIdentityProvider(redirectContext);
if (redirectContext.HandledResponse) if (redirectContext.HandledResponse)
{ {
Logger.RedirectToAuthenticationEndpointHandledResponse(); Logger.RedirectToIdentityProviderHandledResponse();
return true; return true;
} }
else if (redirectContext.Skipped) else if (redirectContext.Skipped)
{ {
Logger.RedirectToAuthenticationEndpointSkipped(); Logger.RedirectToIdentityProviderSkipped();
return false; return false;
} }
@ -350,27 +349,38 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
try try
{ {
var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse); AuthenticationProperties properties = null;
if (CheckEventResult(messageReceivedContext, out result)) if (!string.IsNullOrEmpty(authorizationResponse.State))
{
properties = Options.StateDataFormat.Unprotect(authorizationResponse.State);
}
var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse, properties);
if (messageReceivedContext.CheckEventResult(out result))
{ {
return result; return result;
} }
authorizationResponse = messageReceivedContext.ProtocolMessage; authorizationResponse = messageReceivedContext.ProtocolMessage;
properties = messageReceivedContext.Properties;
// Fail if state is missing, it's required for the correlation id. if (properties == null)
if (string.IsNullOrEmpty(authorizationResponse.State))
{ {
// This wasn't a valid OIDC message, it may not have been intended for us. // Fail if state is missing, it's required for the correlation id.
Logger.NullOrEmptyAuthorizationResponseState(); if (string.IsNullOrEmpty(authorizationResponse.State))
if (Options.SkipUnrecognizedRequests)
{ {
return AuthenticateResult.Skip(); // This wasn't a valid OIDC message, it may not have been intended for us.
Logger.NullOrEmptyAuthorizationResponseState();
if (Options.SkipUnrecognizedRequests)
{
return AuthenticateResult.Skip();
}
return AuthenticateResult.Fail(Resources.MessageStateIsNullOrEmpty);
} }
return AuthenticateResult.Fail(Resources.MessageStateIsNullOrEmpty);
// if state exists and we failed to 'unprotect' this is not a message we should process.
properties = Options.StateDataFormat.Unprotect(authorizationResponse.State);
} }
// if state exists and we failed to 'unprotect' this is not a message we should process.
var properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(authorizationResponse.State));
if (properties == null) if (properties == null)
{ {
Logger.UnableToReadAuthorizationResponseState(); Logger.UnableToReadAuthorizationResponseState();
@ -382,17 +392,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return AuthenticateResult.Fail(Resources.MessageStateIsInvalid); return AuthenticateResult.Fail(Resources.MessageStateIsInvalid);
} }
// if any of the error fields are set, throw error null
if (!string.IsNullOrEmpty(authorizationResponse.Error))
{
Logger.AuthorizationResponseError(
authorizationResponse.Error,
authorizationResponse.ErrorDescription ?? "ErrorDecription null",
authorizationResponse.ErrorUri ?? "ErrorUri null");
return AuthenticateResult.Fail(new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.MessageContainsError, authorizationResponse.Error, authorizationResponse.ErrorDescription ?? "ErrorDecription null", authorizationResponse.ErrorUri ?? "ErrorUri null")));
}
string userstate = null; string userstate = null;
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate); properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate);
authorizationResponse.State = userstate; authorizationResponse.State = userstate;
@ -402,20 +401,25 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return AuthenticateResult.Fail("Correlation failed."); return AuthenticateResult.Fail("Correlation failed.");
} }
// if any of the error fields are set, throw error null
if (!string.IsNullOrEmpty(authorizationResponse.Error))
{
Logger.AuthorizationResponseError(
authorizationResponse.Error,
authorizationResponse.ErrorDescription ?? "ErrorDecription null",
authorizationResponse.ErrorUri ?? "ErrorUri null");
return AuthenticateResult.Fail(new OpenIdConnectProtocolException(
string.Format(CultureInfo.InvariantCulture, Resources.MessageContainsError, authorizationResponse.Error,
authorizationResponse.ErrorDescription ?? "ErrorDecription null", authorizationResponse.ErrorUri ?? "ErrorUri null")));
}
if (_configuration == null && Options.ConfigurationManager != null) if (_configuration == null && Options.ConfigurationManager != null)
{ {
Logger.UpdatingConfiguration(); Logger.UpdatingConfiguration();
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
} }
var authorizationResponseReceivedContext = await RunAuthorizationResponseReceivedEventAsync(authorizationResponse, properties);
if (CheckEventResult(authorizationResponseReceivedContext, out result))
{
return result;
}
authorizationResponse = authorizationResponseReceivedContext.ProtocolMessage;
properties = authorizationResponseReceivedContext.Properties;
PopulateSessionProperties(authorizationResponse, properties); PopulateSessionProperties(authorizationResponse, properties);
AuthenticationTicket ticket = null; AuthenticationTicket ticket = null;
@ -434,6 +438,17 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
nonce = ReadNonceCookie(nonce); nonce = ReadNonceCookie(nonce);
} }
var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, null, properties, ticket, jwt, nonce);
if (tokenValidatedContext.CheckEventResult(out result))
{
return result;
}
authorizationResponse = tokenValidatedContext.ProtocolMessage;
properties = tokenValidatedContext.Properties;
ticket = tokenValidatedContext.Ticket;
jwt = tokenValidatedContext.SecurityToken;
nonce = tokenValidatedContext.Nonce;
} }
Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext()
@ -444,15 +459,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
Nonce = nonce Nonce = nonce
}); });
// TODO: AuthorizationResponseValidated event?
OpenIdConnectMessage tokenEndpointResponse = null; OpenIdConnectMessage tokenEndpointResponse = null;
// 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, properties, ticket, jwt);
if (CheckEventResult(authorizationCodeReceivedContext, out result)) if (authorizationCodeReceivedContext.CheckEventResult(out result))
{ {
return result; return result;
} }
@ -469,13 +482,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest); tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest);
} }
var authorizationCodeRedeemedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, properties); var tokenResponseReceivedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, properties);
if (CheckEventResult(authorizationCodeRedeemedContext, out result)) if (tokenResponseReceivedContext.CheckEventResult(out result))
{ {
return result; return result;
} }
authorizationResponse = authorizationCodeRedeemedContext.ProtocolMessage; authorizationResponse = tokenResponseReceivedContext.ProtocolMessage;
tokenEndpointResponse = authorizationCodeRedeemedContext.TokenEndpointResponse; tokenEndpointResponse = tokenResponseReceivedContext.TokenEndpointResponse;
// We only have to process the IdToken if we didn't already get one in the AuthorizationResponse // We only have to process the IdToken if we didn't already get one in the AuthorizationResponse
if (ticket == null) if (ticket == null)
@ -491,6 +504,18 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{ {
nonce = ReadNonceCookie(nonce); nonce = ReadNonceCookie(nonce);
} }
var tokenValidatedContext = await RunTokenValidatedEventAsync(authorizationResponse, tokenEndpointResponse, properties, ticket, jwt, nonce);
if (tokenValidatedContext.CheckEventResult(out result))
{
return result;
}
authorizationResponse = tokenValidatedContext.ProtocolMessage;
tokenEndpointResponse = tokenValidatedContext.TokenEndpointResponse;
properties = tokenValidatedContext.Properties;
ticket = tokenValidatedContext.Ticket;
jwt = tokenValidatedContext.SecurityToken;
nonce = tokenValidatedContext.Nonce;
} }
// Validate the token response if it wasn't provided manually // Validate the token response if it wasn't provided manually
@ -506,15 +531,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
} }
var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(authorizationResponse, ticket, properties, tokenEndpointResponse);
if (CheckEventResult(authenticationValidatedContext, out result))
{
return result;
}
authorizationResponse = authenticationValidatedContext.ProtocolMessage;
tokenEndpointResponse = authenticationValidatedContext.TokenEndpointResponse;
ticket = authenticationValidatedContext.Ticket;
if (Options.SaveTokens) if (Options.SaveTokens)
{ {
SaveTokens(ticket.Properties, tokenEndpointResponse ?? authorizationResponse); SaveTokens(ticket.Properties, tokenEndpointResponse ?? authorizationResponse);
@ -542,7 +558,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
var authenticationFailedContext = await RunAuthenticationFailedEventAsync(authorizationResponse, exception); var authenticationFailedContext = await RunAuthenticationFailedEventAsync(authorizationResponse, exception);
if (CheckEventResult(authenticationFailedContext, out result)) if (authenticationFailedContext.CheckEventResult(out result))
{ {
return result; return result;
} }
@ -551,22 +567,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
} }
private bool CheckEventResult(BaseControlContext context, out AuthenticateResult result)
{
if (context.HandledResponse)
{
result = AuthenticateResult.Success(context.Ticket);
return true;
}
else if (context.Skipped)
{
result = AuthenticateResult.Skip();
return true;
}
result = null;
return false;
}
private void PopulateSessionProperties(OpenIdConnectMessage message, AuthenticationProperties properties) private void PopulateSessionProperties(OpenIdConnectMessage message, AuthenticationProperties properties)
{ {
if (!string.IsNullOrEmpty(message.SessionState)) if (!string.IsNullOrEmpty(message.SessionState))
@ -643,7 +643,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(ticket, message, user); var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(ticket, message, user);
AuthenticateResult result; AuthenticateResult result;
if (CheckEventResult(userInformationReceivedContext, out result)) if (userInformationReceivedContext.CheckEventResult(out result))
{ {
return result; return result;
} }
@ -830,12 +830,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
} }
} }
private async Task<MessageReceivedContext> RunMessageReceivedEventAsync(OpenIdConnectMessage message) private async Task<MessageReceivedContext> RunMessageReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties)
{ {
Logger.MessageReceived(message.BuildRedirectUrl()); Logger.MessageReceived(message.BuildRedirectUrl());
var messageReceivedContext = new MessageReceivedContext(Context, Options) var messageReceivedContext = new MessageReceivedContext(Context, Options)
{ {
ProtocolMessage = message ProtocolMessage = message,
Properties = properties,
}; };
await Options.Events.MessageReceived(messageReceivedContext); await Options.Events.MessageReceived(messageReceivedContext);
@ -851,23 +852,29 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return messageReceivedContext; return messageReceivedContext;
} }
private async Task<AuthorizationResponseReceivedContext> RunAuthorizationResponseReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties) private async Task<TokenValidatedContext> RunTokenValidatedEventAsync(OpenIdConnectMessage authorizationResponse, OpenIdConnectMessage tokenEndpointResponse, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt, string nonce)
{ {
Logger.AuthorizationResponseReceived(); var tokenValidatedContext = new TokenValidatedContext(Context, Options)
var authorizationResponseReceivedContext = new AuthorizationResponseReceivedContext(Context, Options, properties)
{ {
ProtocolMessage = message ProtocolMessage = authorizationResponse,
TokenEndpointResponse = tokenEndpointResponse,
Properties = properties,
Ticket = ticket,
SecurityToken = jwt,
Nonce = nonce,
}; };
await Options.Events.AuthorizationResponseReceived(authorizationResponseReceivedContext);
if (authorizationResponseReceivedContext.HandledResponse) await Options.Events.TokenValidated(tokenValidatedContext);
if (tokenValidatedContext.HandledResponse)
{ {
Logger.AuthorizationResponseReceivedHandledResponse(); Logger.TokenValidatedHandledResponse();
} }
else if (authorizationResponseReceivedContext.Skipped) else if (tokenValidatedContext.Skipped)
{ {
Logger.AuthorizationResponseReceivedSkipped(); Logger.TokenValidatedSkipped();
} }
return authorizationResponseReceivedContext;
return tokenValidatedContext;
} }
private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage authorizationResponse, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt) private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage authorizationResponse, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt)
@ -909,46 +916,23 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
private async Task<TokenResponseReceivedContext> RunTokenResponseReceivedEventAsync(OpenIdConnectMessage message, OpenIdConnectMessage tokenEndpointResponse, AuthenticationProperties properties) private async Task<TokenResponseReceivedContext> RunTokenResponseReceivedEventAsync(OpenIdConnectMessage message, OpenIdConnectMessage tokenEndpointResponse, AuthenticationProperties properties)
{ {
Logger.TokenResponseReceived(); Logger.TokenResponseReceived();
var eventContext = new TokenResponseReceivedContext(Context, Options, properties)
var tokenResponseReceivedContext = new TokenResponseReceivedContext(Context, Options, properties)
{ {
ProtocolMessage = message, ProtocolMessage = message,
TokenEndpointResponse = tokenEndpointResponse TokenEndpointResponse = tokenEndpointResponse
}; };
await Options.Events.TokenResponseReceived(tokenResponseReceivedContext); await Options.Events.TokenResponseReceived(eventContext);
if (tokenResponseReceivedContext.HandledResponse) if (eventContext.HandledResponse)
{ {
Logger.AuthorizationCodeRedeemedContextHandledResponse(); Logger.TokenResponseReceivedHandledResponse();
} }
else if (tokenResponseReceivedContext.Skipped) else if (eventContext.Skipped)
{ {
Logger.AuthorizationCodeRedeemedContextSkipped(); Logger.TokenResponseReceivedSkipped();
} }
return tokenResponseReceivedContext; return eventContext;
}
private async Task<AuthenticationValidatedContext> RunAuthenticationValidatedEventAsync(OpenIdConnectMessage message, AuthenticationTicket ticket, AuthenticationProperties properties, OpenIdConnectMessage tokenResponse)
{
var authenticationValidatedContext = new AuthenticationValidatedContext(Context, Options, properties)
{
Ticket = ticket,
ProtocolMessage = message,
TokenEndpointResponse = tokenResponse,
};
await Options.Events.AuthenticationValidated(authenticationValidatedContext);
if (authenticationValidatedContext.HandledResponse)
{
Logger.AuthenticationValidatedHandledResponse();
}
else if (authenticationValidatedContext.Skipped)
{
Logger.AuthenticationValidatedSkipped();
}
return authenticationValidatedContext;
} }
private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(AuthenticationTicket ticket, OpenIdConnectMessage message, JObject user) private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(AuthenticationTicket ticket, OpenIdConnectMessage message, JObject user)

View File

@ -46,5 +46,21 @@ namespace Microsoft.AspNetCore.Authentication
/// Gets or set the <see cref="Ticket"/> to return if this event signals it handled the event. /// Gets or set the <see cref="Ticket"/> to return if this event signals it handled the event.
/// </summary> /// </summary>
public AuthenticationTicket Ticket { get; set; } public AuthenticationTicket Ticket { get; set; }
public bool CheckEventResult(out AuthenticateResult result)
{
if (HandledResponse)
{
result = AuthenticateResult.Success(Ticket);
return true;
}
else if (Skipped)
{
result = AuthenticateResult.Skip();
return true;
}
result = null;
return false;
}
} }
} }

View File

@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
AutomaticAuthenticate = true, AutomaticAuthenticate = true,
Events = new JwtBearerEvents() Events = new JwtBearerEvents()
{ {
OnReceivingToken = context => OnMessageReceived = context =>
{ {
var claims = new[] var claims = new[]
{ {
@ -143,39 +143,6 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Assert.Equal("", response.ResponseText); Assert.Equal("", response.ResponseText);
} }
[Fact]
public async Task CustomTokenReceived()
{
var server = CreateServer(new JwtBearerOptions
{
AutomaticAuthenticate = true,
Events = new JwtBearerEvents()
{
OnReceivedToken = context =>
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
new Claim(ClaimTypes.Email, "bob@contoso.com"),
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
};
context.Ticket = new AuthenticationTicket(
new ClaimsPrincipal(new ClaimsIdentity(claims, context.Options.AuthenticationScheme)),
new AuthenticationProperties(), context.Options.AuthenticationScheme);
context.HandleResponse();
return Task.FromResult<object>(null);
}
}
});
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode);
Assert.Equal("Bob le Magnifique", response.ResponseText);
}
[Fact] [Fact]
public async Task CustomTokenValidated() public async Task CustomTokenValidated()
{ {
@ -184,7 +151,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
AutomaticAuthenticate = true, AutomaticAuthenticate = true,
Events = new JwtBearerEvents() Events = new JwtBearerEvents()
{ {
OnValidatedToken = context => OnTokenValidated = context =>
{ {
// 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.
@ -204,6 +171,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
} }
}; };
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new BlobTokenValidator(options.AuthenticationScheme)); options.SecurityTokenValidators.Add(new BlobTokenValidator(options.AuthenticationScheme));
var server = CreateServer(options); var server = CreateServer(options);
@ -215,67 +183,37 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
[Fact] [Fact]
public async Task RetrievingTokenFromAlternateLocation() public async Task RetrievingTokenFromAlternateLocation()
{ {
var server = CreateServer(new JwtBearerOptions var options = new JwtBearerOptions()
{ {
AutomaticAuthenticate = true, AutomaticAuthenticate = true,
Events = new JwtBearerEvents() Events = new JwtBearerEvents()
{ {
OnReceivingToken = context => OnMessageReceived = context =>
{ {
context.Token = "CustomToken"; context.Token = "CustomToken";
return Task.FromResult<object>(null);
},
OnReceivedToken = context =>
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
new Claim(ClaimTypes.Email, "bob@contoso.com"),
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
};
context.Ticket = new AuthenticationTicket(
new ClaimsPrincipal(new ClaimsIdentity(claims, context.Options.AuthenticationScheme)),
new AuthenticationProperties(), context.Options.AuthenticationScheme);
context.HandleResponse();
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
} }
} }
}); };
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT", token =>
{
Assert.Equal("CustomToken", token);
}));
var server = CreateServer(options);
var response = await SendAsync(server, "http://example.com/oauth", "Bearer Token"); var response = await SendAsync(server, "http://example.com/oauth", "Bearer Token");
Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode);
Assert.Equal("Bob le Magnifique", response.ResponseText); Assert.Equal("Bob le Tout Puissant", response.ResponseText);
} }
[Fact] [Fact]
public async Task BearerTurns401To403IfAuthenticated() public async Task BearerTurns401To403IfAuthenticated()
{ {
var server = CreateServer(new JwtBearerOptions var options = new JwtBearerOptions();
{ options.SecurityTokenValidators.Clear();
Events = new JwtBearerEvents() options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT"));
{ var server = CreateServer(options);
OnReceivedToken = context =>
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
new Claim(ClaimTypes.Email, "bob@contoso.com"),
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
};
context.Ticket = new AuthenticationTicket(
new ClaimsPrincipal(new ClaimsIdentity(claims, context.Options.AuthenticationScheme)),
new AuthenticationProperties(), context.Options.AuthenticationScheme);
context.HandleResponse();
return Task.FromResult<object>(null);
}
}
});
var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token"); var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token");
Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode); Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode);
@ -284,52 +222,26 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
[Fact] [Fact]
public async Task BearerDoesNothingTo401IfNotAuthenticated() public async Task BearerDoesNothingTo401IfNotAuthenticated()
{ {
var server = CreateServer(new JwtBearerOptions var server = CreateServer(new JwtBearerOptions());
{
Events = new JwtBearerEvents()
{
OnReceivedToken = context =>
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
new Claim(ClaimTypes.Email, "bob@contoso.com"),
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
};
context.Ticket = new AuthenticationTicket(
new ClaimsPrincipal(new ClaimsIdentity(claims, context.Options.AuthenticationScheme)),
new AuthenticationProperties(), context.Options.AuthenticationScheme);
context.HandleResponse();
return Task.FromResult<object>(null);
}
}
});
var response = await SendAsync(server, "http://example.com/unauthorized"); var response = await SendAsync(server, "http://example.com/unauthorized");
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
} }
[Fact] [Fact]
public async Task EventOnReceivingTokenSkipped_NoMoreEventsExecuted() public async Task EventOnMessageReceivedSkipped_NoMoreEventsExecuted()
{ {
var server = CreateServer(new JwtBearerOptions var server = CreateServer(new JwtBearerOptions
{ {
AutomaticAuthenticate = true, AutomaticAuthenticate = true,
Events = new JwtBearerEvents() Events = new JwtBearerEvents()
{ {
OnReceivingToken = context => OnMessageReceived = context =>
{ {
context.SkipToNextMiddleware(); context.SkipToNextMiddleware();
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnReceivedToken = context => OnTokenValidated = context =>
{
throw new NotImplementedException();
},
OnValidatedToken = context =>
{ {
throw new NotImplementedException(); throw new NotImplementedException();
}, },
@ -350,47 +262,14 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
} }
[Fact] [Fact]
public async Task EventOnReceivedTokenSkipped_NoMoreEventsExecuted() public async Task EventOnTokenValidatedSkipped_NoMoreEventsExecuted()
{
var server = CreateServer(new JwtBearerOptions
{
AutomaticAuthenticate = true,
Events = new JwtBearerEvents()
{
OnReceivedToken = context =>
{
context.SkipToNextMiddleware();
return Task.FromResult(0);
},
OnValidatedToken = context =>
{
throw new NotImplementedException();
},
OnAuthenticationFailed = context =>
{
throw new NotImplementedException(context.Exception.ToString());
},
OnChallenge = context =>
{
throw new NotImplementedException();
},
}
});
var response = await SendAsync(server, "http://example.com/checkforerrors", "Bearer Token");
Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode);
Assert.Equal(string.Empty, response.ResponseText);
}
[Fact]
public async Task EventOnValidatedTokenSkipped_NoMoreEventsExecuted()
{ {
var options = new JwtBearerOptions var options = new JwtBearerOptions
{ {
AutomaticAuthenticate = true, AutomaticAuthenticate = true,
Events = new JwtBearerEvents() Events = new JwtBearerEvents()
{ {
OnValidatedToken = context => OnTokenValidated = context =>
{ {
context.SkipToNextMiddleware(); context.SkipToNextMiddleware();
return Task.FromResult(0); return Task.FromResult(0);
@ -422,7 +301,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
AutomaticAuthenticate = true, AutomaticAuthenticate = true,
Events = new JwtBearerEvents() Events = new JwtBearerEvents()
{ {
OnValidatedToken = context => OnTokenValidated = context =>
{ {
throw new Exception("Test Exception"); throw new Exception("Test Exception");
}, },
@ -493,9 +372,17 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
class BlobTokenValidator : ISecurityTokenValidator class BlobTokenValidator : ISecurityTokenValidator
{ {
private Action<string> _tokenValidator;
public BlobTokenValidator(string authenticationScheme) public BlobTokenValidator(string authenticationScheme)
{ {
AuthenticationScheme = authenticationScheme; AuthenticationScheme = authenticationScheme;
}
public BlobTokenValidator(string authenticationScheme, Action<string> tokenValidator)
{
AuthenticationScheme = authenticationScheme;
_tokenValidator = tokenValidator;
} }
public string AuthenticationScheme { get; } public string AuthenticationScheme { get; }
@ -519,6 +406,10 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{ {
validatedToken = null; validatedToken = null;
if (_tokenValidator != null)
{
_tokenValidator(securityToken);
}
var claims = new[] var claims = new[]
{ {

View File

@ -150,12 +150,12 @@ namespace Microsoft.AspNetCore.Authentication.Tests.OpenIdConnect
options.AutomaticChallenge = true; options.AutomaticChallenge = true;
options.Events = new OpenIdConnectEvents() options.Events = new OpenIdConnectEvents()
{ {
OnRedirectToAuthenticationEndpoint = (context) => OnRedirectToIdentityProvider = (context) =>
{ {
context.ProtocolMessage = fakeOpenIdRequestMessage; context.ProtocolMessage = fakeOpenIdRequestMessage;
return Task.FromResult(0); return Task.FromResult(0);
}, },
OnRedirectToEndSessionEndpoint = (context) => OnRedirectToIdentityProviderForSignOut = (context) =>
{ {
context.ProtocolMessage = fakeOpenIdRequestMessage; context.ProtocolMessage = fakeOpenIdRequestMessage;
return Task.FromResult(0); return Task.FromResult(0);
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Authentication.Tests.OpenIdConnect
options.AutomaticChallenge = challenge.Equals(ChallengeWithOutContext); options.AutomaticChallenge = challenge.Equals(ChallengeWithOutContext);
options.Events = new OpenIdConnectEvents() options.Events = new OpenIdConnectEvents()
{ {
OnRedirectToAuthenticationEndpoint = context => OnRedirectToIdentityProvider = context =>
{ {
context.ProtocolMessage.State = userState; context.ProtocolMessage.State = userState;
context.ProtocolMessage.RedirectUri = queryValues.RedirectUri; context.ProtocolMessage.RedirectUri = queryValues.RedirectUri;
@ -258,7 +258,7 @@ namespace Microsoft.AspNetCore.Authentication.Tests.OpenIdConnect
var options = GetOptions(DefaultParameters(), queryValues); var options = GetOptions(DefaultParameters(), queryValues);
options.Events = new OpenIdConnectEvents() options.Events = new OpenIdConnectEvents()
{ {
OnRedirectToAuthenticationEndpoint = context => OnRedirectToIdentityProvider = context =>
{ {
context.ProtocolMessage.ClientId = queryValuesSetInEvent.ClientId; context.ProtocolMessage.ClientId = queryValuesSetInEvent.ClientId;
context.ProtocolMessage.RedirectUri = queryValuesSetInEvent.RedirectUri; context.ProtocolMessage.RedirectUri = queryValuesSetInEvent.RedirectUri;