#372 Flow mutable event state.

#358 Add a UserInformationReceived event.
#327 Add AuthenticationCompleted event.
#340 Split the Redirect event for Authentication and SignOut.
Rename OnAuthorizationCodeRedeemed to OnTokenResponseReceived.
Move IdTokenReceived to AuthorizationResponseReceived.
Rename IdTokenValidated to AuthenticationValidated.
This commit is contained in:
Chris R 2015-09-16 14:31:36 -07:00
parent 92d5e4ce77
commit 1c0768fb71
17 changed files with 337 additions and 627 deletions

View File

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
public class AuthenticationCompletedContext : BaseControlContext<OpenIdConnectOptions>
{
public AuthenticationCompletedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
}
}
}

View File

@ -6,15 +6,15 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
public class SecurityTokenReceivedContext : BaseControlContext<OpenIdConnectOptions>
public class AuthenticationValidatedContext : BaseControlContext<OpenIdConnectOptions>
{
public SecurityTokenReceivedContext(HttpContext context, OpenIdConnectOptions options)
public AuthenticationValidatedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
}
public string SecurityToken { get; set; }
public OpenIdConnectMessage ProtocolMessage { get; set; }
public OpenIdConnectTokenEndpointResponse TokenEndpointResponse { get; set; }
}
}

View File

@ -2,17 +2,20 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
public class SecurityTokenValidatedContext : BaseControlContext<OpenIdConnectOptions>
public class AuthorizationResponseReceivedContext : BaseControlContext<OpenIdConnectOptions>
{
public SecurityTokenValidatedContext(HttpContext context, OpenIdConnectOptions options)
public AuthorizationResponseReceivedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
}
public OpenIdConnectMessage ProtocolMessage { get; set; }
public AuthenticationProperties Properties { get; set; }
}
}

View File

@ -10,20 +10,30 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// </summary>
public interface IOpenIdConnectEvents
{
/// <summary>
/// Invoked when the authentication process completes.
/// </summary>
Task AuthenticationCompleted(AuthenticationCompletedContext context);
/// <summary>
/// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed.
/// </summary>
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>
/// Invoked after security token validation if an authorization code is present in the protocol message.
/// </summary>
Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context);
/// <summary>
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// Invoked when an authorization response is received.
/// </summary>
Task AuthorizationCodeRedeemed(AuthorizationCodeRedeemedContext context);
Task AuthorizationResponseReceived(AuthorizationResponseReceivedContext context);
/// <summary>
/// Invoked when a protocol message is first received.
@ -31,18 +41,23 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
Task MessageReceived(MessageReceivedContext context);
/// <summary>
/// Invoked to manipulate redirects to the identity provider for SignIn, SignOut, or Challenge.
/// Invoked before redirecting to the identity provider to authenticate.
/// </summary>
Task RedirectToIdentityProvider(RedirectToIdentityProviderContext context);
Task RedirectToAuthenticationEndpoint(RedirectContext context);
/// <summary>
/// Invoked with the security token that has been extracted from the protocol message.
/// Invoked before redirecting to the identity provider to sign out.
/// </summary>
Task SecurityTokenReceived(SecurityTokenReceivedContext context);
Task RedirectToEndSessionEndpoint(RedirectContext context);
/// <summary>
/// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// </summary>
Task SecurityTokenValidated(SecurityTokenValidatedContext context);
Task TokenResponseReceived(TokenResponseReceivedContext context);
/// <summary>
/// Invoked when user information is retrieved from the UserInfoEndpoint.
/// </summary>
Task UserInformationReceived(UserInformationReceivedContext context);
}
}

View File

@ -11,20 +11,30 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// </summary>
public class OpenIdConnectEvents : IOpenIdConnectEvents
{
/// <summary>
/// Invoked when the authentication process completes.
/// </summary>
public Func<AuthenticationCompletedContext, Task> OnAuthenticationCompleted { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed.
/// </summary>
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>
/// Invoked after security token validation if an authorization code is present in the protocol message.
/// </summary>
public Func<AuthorizationCodeReceivedContext, Task> OnAuthorizationCodeReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// Invoked when an authorization response is received.
/// </summary>
public Func<AuthorizationCodeRedeemedContext, Task> OnAuthorizationCodeRedeemed { get; set; } = context => Task.FromResult(0);
public Func<AuthorizationResponseReceivedContext, Task> OnAuthorizationResponseReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked when a protocol message is first received.
@ -32,32 +42,43 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
public Func<MessageReceivedContext, Task> OnMessageReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked to manipulate redirects to the identity provider for SignIn, SignOut, or Challenge.
/// Invoked before redirecting to the identity provider to authenticate.
/// </summary>
public Func<RedirectToIdentityProviderContext, Task> OnRedirectToIdentityProvider { get; set; } = context => Task.FromResult(0);
public Func<RedirectContext, Task> OnRedirectToAuthenticationEndpoint { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked with the security token that has been extracted from the protocol message.
/// Invoked before redirecting to the identity provider to sign out.
/// </summary>
public Func<SecurityTokenReceivedContext, Task> OnSecurityTokenReceived { get; set; } = context => Task.FromResult(0);
public Func<RedirectContext, Task> OnRedirectToEndSessionEndpoint { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
/// </summary>
public Func<SecurityTokenValidatedContext, Task> OnSecurityTokenValidated { get; set; } = context => Task.FromResult(0);
public Func<TokenResponseReceivedContext, Task> OnTokenResponseReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked when user information is retrieved from the UserInfoEndpoint.
/// </summary>
public Func<UserInformationReceivedContext, Task> OnUserInformationReceived { get; set; } = context => Task.FromResult(0);
public virtual Task AuthenticationCompleted(AuthenticationCompletedContext context) => OnAuthenticationCompleted(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 AuthorizationCodeRedeemed(AuthorizationCodeRedeemedContext context) => OnAuthorizationCodeRedeemed(context);
public virtual Task AuthorizationResponseReceived(AuthorizationResponseReceivedContext context) => OnAuthorizationResponseReceived(context);
public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
public virtual Task RedirectToIdentityProvider(RedirectToIdentityProviderContext context) => OnRedirectToIdentityProvider(context);
public virtual Task RedirectToAuthenticationEndpoint(RedirectContext context) => OnRedirectToAuthenticationEndpoint(context);
public virtual Task SecurityTokenReceived(SecurityTokenReceivedContext context) => OnSecurityTokenReceived(context);
public virtual Task RedirectToEndSessionEndpoint(RedirectContext context) => OnRedirectToEndSessionEndpoint(context);
public virtual Task SecurityTokenValidated(SecurityTokenValidatedContext context) => OnSecurityTokenValidated(context);
public virtual Task TokenResponseReceived(TokenResponseReceivedContext context) => OnTokenResponseReceived(context);
public virtual Task UserInformationReceived(UserInformationReceivedContext context) => OnUserInformationReceived(context);
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
/// <summary>
/// When a user configures the <see cref="OpenIdConnectMiddleware"/> to be notified prior to redirecting to an IdentityProvider
/// an instance of <see cref="RedirectContext"/> is passed to the 'RedirectToAuthenticationEndpoint' or 'RedirectToEndSessionEndpoint' events.
/// </summary>
public class RedirectContext : BaseControlContext<OpenIdConnectOptions>
{
public RedirectContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
}
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
public OpenIdConnectMessage ProtocolMessage { get; set; }
}
}

View File

@ -1,30 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
/// <summary>
/// When a user configures the <see cref="AuthenticationMiddleware{TOptions}"/> to be notified prior to redirecting to an IdentityProvider
/// an instance of <see cref="RedirectToIdentityProviderContext"/> is passed to the 'RedirectToIdentityProvider" event.
/// </summary>
/// <typeparam name="TMessage">protocol specific message.</typeparam>
/// <typeparam name="TOptions">protocol specific options.</typeparam>
public class RedirectToIdentityProviderContext : BaseControlContext<OpenIdConnectOptions>
{
public RedirectToIdentityProviderContext([NotNull] HttpContext context, [NotNull] OpenIdConnectOptions options)
: base(context, options)
{
}
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectMessage"/>.
/// </summary>
/// <exception cref="ArgumentNullException">if 'value' is null.</exception>
public OpenIdConnectMessage ProtocolMessage { get; [param: NotNull] set; }
}
}

View File

@ -6,21 +6,16 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// <summary>
/// This Context can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint.
/// </summary>
public class AuthorizationCodeRedeemedContext : BaseControlContext<OpenIdConnectOptions>
public class TokenResponseReceivedContext : BaseControlContext<OpenIdConnectOptions>
{
/// <summary>
/// Creates a <see cref="AuthorizationCodeRedeemedContext"/>
/// Creates a <see cref="TokenResponseReceivedContext"/>
/// </summary>
public AuthorizationCodeRedeemedContext(HttpContext context, OpenIdConnectOptions options)
public TokenResponseReceivedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
}
/// <summary>
/// Gets or sets the 'code'.
/// </summary>
public string Code { get; set; }
/// <summary>
/// Gets or sets the <see cref="OpenIdConnectTokenEndpointResponse"/> that contains the tokens and json response received after redeeming the code at the token endpoint.
/// </summary>

View File

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
public class UserInformationReceivedContext : BaseControlContext<OpenIdConnectOptions>
{
public UserInformationReceivedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
}
public OpenIdConnectMessage ProtocolMessage { get; set; }
public JObject User { get; set; }
}
}

View File

@ -74,7 +74,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
var message = new OpenIdConnectMessage()
{
IssuerAddress = _configuration == null ? string.Empty : (_configuration.EndSessionEndpoint ?? string.Empty),
RequestType = OpenIdConnectRequestType.LogoutRequest,
};
// Set End_Session_Endpoint in order:
@ -90,24 +89,24 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
message.PostLogoutRedirectUri = Options.PostLogoutRedirectUri;
}
var redirectToIdentityProviderContext = new RedirectToIdentityProviderContext(Context, Options)
var redirectContext = new RedirectContext(Context, Options)
{
ProtocolMessage = message
};
await Options.Events.RedirectToIdentityProvider(redirectToIdentityProviderContext);
if (redirectToIdentityProviderContext.HandledResponse)
await Options.Events.RedirectToEndSessionEndpoint(redirectContext);
if (redirectContext.HandledResponse)
{
Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderContextHandledResponse);
Logger.LogVerbose("RedirectToEndSessionEndpoint.HandledResponse");
return;
}
else if (redirectToIdentityProviderContext.Skipped)
else if (redirectContext.Skipped)
{
Logger.LogVerbose(Resources.OIDCH_0035_RedirectToIdentityProviderContextSkipped);
Logger.LogVerbose("RedirectToEndSessionEndpoint.Skipped");
return;
}
message = redirectToIdentityProviderContext.ProtocolMessage;
message = redirectContext.ProtocolMessage;
if (Options.AuthenticationMethod == OpenIdConnectRedirectBehavior.RedirectGet)
{
@ -183,8 +182,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
ClientId = Options.ClientId,
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
RedirectUri = Options.RedirectUri,
// [brentschmaltz] - #215 this should be a property on RedirectToIdentityProviderContext not on the OIDCMessage.
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
Resource = Options.Resource,
ResponseType = Options.ResponseType,
Scope = string.Join(" ", Options.Scope)
@ -207,29 +204,29 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
GenerateCorrelationId(properties);
var redirectToIdentityProviderContext = new RedirectToIdentityProviderContext(Context, Options)
var redirectContext = new RedirectContext(Context, Options)
{
ProtocolMessage = message
};
await Options.Events.RedirectToIdentityProvider(redirectToIdentityProviderContext);
if (redirectToIdentityProviderContext.HandledResponse)
await Options.Events.RedirectToAuthenticationEndpoint(redirectContext);
if (redirectContext.HandledResponse)
{
Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderContextHandledResponse);
Logger.LogVerbose("RedirectToAuthenticationEndpoint.HandledResponse");
return true;
}
else if (redirectToIdentityProviderContext.Skipped)
else if (redirectContext.Skipped)
{
Logger.LogVerbose(Resources.OIDCH_0035_RedirectToIdentityProviderContextSkipped);
Logger.LogVerbose("RedirectToAuthenticationEndpoint.Skipped");
return false;
}
if (!string.IsNullOrEmpty(redirectToIdentityProviderContext.ProtocolMessage.State))
{
properties.Items[OpenIdConnectDefaults.UserstatePropertiesKey] = redirectToIdentityProviderContext.ProtocolMessage.State;
}
message = redirectContext.ProtocolMessage;
message = redirectToIdentityProviderContext.ProtocolMessage;
if (!string.IsNullOrEmpty(message.State))
{
properties.Items[OpenIdConnectDefaults.UserstatePropertiesKey] = message.State;
}
var redirectUriForCode = message.RedirectUri;
if (string.IsNullOrEmpty(redirectUriForCode))
@ -350,36 +347,35 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
return null;
}
message = messageReceivedContext.ProtocolMessage;
var properties = new AuthenticationProperties();
// if state is missing, just log it
// Fail if state is missing, it's required for the correlation id.
if (string.IsNullOrEmpty(message.State))
{
Logger.LogWarning(Resources.OIDCH_0004_MessageStateIsNullOrEmpty);
Logger.LogError(Resources.OIDCH_0004_MessageStateIsNullOrEmpty);
return null;
}
else
{
// if state exists and we failed to 'unprotect' this is not a message we should process.
properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(message.State));
if (properties == null)
{
Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
return null;
}
string userstate = null;
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate);
message.State = userstate;
// if state exists and we failed to 'unprotect' this is not a message we should process.
var properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(message.State));
if (properties == null)
{
Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
return null;
}
// if any of the error fields are set, throw error null
if (!string.IsNullOrEmpty(message.Error))
{
Logger.LogError(Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null");
throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null"));
throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null"));
}
string userstate = null;
properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate);
message.State = userstate;
if (!ValidateCorrelationId(properties))
{
return null;
@ -391,6 +387,26 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
}
Logger.LogDebug("Authorization response received.");
var authorizationResponseReceivedContext = new AuthorizationResponseReceivedContext(Context, Options)
{
ProtocolMessage = message,
Properties = properties
};
await Options.Events.AuthorizationResponseReceived(authorizationResponseReceivedContext);
if (authorizationResponseReceivedContext.HandledResponse)
{
Logger.LogVerbose("AuthorizationResponseReceived.HandledResponse");
return authorizationResponseReceivedContext.AuthenticationTicket;
}
else if (authorizationResponseReceivedContext.Skipped)
{
Logger.LogVerbose("AuthorizationResponseReceived.Skipped");
return null;
}
message = authorizationResponseReceivedContext.ProtocolMessage;
properties = authorizationResponseReceivedContext.Properties;
if (string.IsNullOrEmpty(message.IdToken) && !string.IsNullOrEmpty(message.Code))
{
return await HandleCodeOnlyFlow(message, properties);
@ -433,13 +449,12 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
}
}
// Authorization Code Flow
private async Task<AuthenticationTicket> HandleCodeOnlyFlow(OpenIdConnectMessage message, AuthenticationProperties properties)
{
AuthenticationTicket ticket = null;
JwtSecurityToken jwt = null;
OpenIdConnectTokenEndpointResponse tokenEndpointResponse = null;
string idToken = null;
var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(message, properties, ticket, jwt);
if (authorizationCodeReceivedContext.HandledResponse)
{
@ -449,14 +464,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
return null;
}
message = authorizationCodeReceivedContext.ProtocolMessage;
var code = authorizationCodeReceivedContext.Code;
// Redeeming authorization code for tokens
Logger.LogDebug(Resources.OIDCH_0038_Redeeming_Auth_Code, message.Code);
Logger.LogDebug(Resources.OIDCH_0038_Redeeming_Auth_Code, code);
tokenEndpointResponse = await RedeemAuthorizationCodeAsync(message.Code, authorizationCodeReceivedContext.RedirectUri);
idToken = tokenEndpointResponse.Message.IdToken;
var tokenEndpointResponse = await RedeemAuthorizationCodeAsync(code, authorizationCodeReceivedContext.RedirectUri);
var authorizationCodeRedeemedContext = await RunAuthorizationCodeRedeemedEventAsync(message, tokenEndpointResponse);
var authorizationCodeRedeemedContext = await RunTokenResponseReceivedEventAsync(message, tokenEndpointResponse);
if (authorizationCodeRedeemedContext.HandledResponse)
{
return authorizationCodeRedeemedContext.AuthenticationTicket;
@ -466,63 +482,61 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return null;
}
message = authorizationCodeRedeemedContext.ProtocolMessage;
tokenEndpointResponse = authorizationCodeRedeemedContext.TokenEndpointResponse;
// no need to validate signature when token is received using "code flow" as per spec [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation].
var validationParameters = Options.TokenValidationParameters.Clone();
validationParameters.ValidateSignature = false;
ticket = ValidateToken(idToken, message, properties, validationParameters, out jwt);
ticket = ValidateToken(tokenEndpointResponse.ProtocolMessage.IdToken, message, properties, validationParameters, out jwt);
await ValidateOpenIdConnectProtocolAsync(null, message);
var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(message, ticket, tokenEndpointResponse);
if (authenticationValidatedContext.HandledResponse)
{
return authenticationValidatedContext.AuthenticationTicket;
}
else if (authenticationValidatedContext.Skipped)
{
return null;
}
ticket = authenticationValidatedContext.AuthenticationTicket;
if (Options.GetClaimsFromUserInfoEndpoint)
{
Logger.LogDebug(Resources.OIDCH_0040_Sending_Request_UIEndpoint);
ticket = await GetUserInformationAsync(properties, tokenEndpointResponse.Message, ticket);
}
var securityTokenValidatedContext = await RunSecurityTokenValidatedEventAsync(message, ticket);
if (securityTokenValidatedContext.HandledResponse)
{
return securityTokenValidatedContext.AuthenticationTicket;
}
else if (securityTokenValidatedContext.Skipped)
{
return null;
ticket = await GetUserInformationAsync(tokenEndpointResponse.ProtocolMessage, ticket);
}
return ticket;
}
// Implicit Flow or Hybrid Flow
private async Task<AuthenticationTicket> HandleIdTokenFlows(OpenIdConnectMessage message, AuthenticationProperties properties)
{
AuthenticationTicket ticket = null;
Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
JwtSecurityToken jwt = null;
var securityTokenReceivedContext = await RunSecurityTokenReceivedEventAsync(message);
if (securityTokenReceivedContext.HandledResponse)
{
return securityTokenReceivedContext.AuthenticationTicket;
}
else if (securityTokenReceivedContext.Skipped)
{
return null;
}
var validationParameters = Options.TokenValidationParameters.Clone();
ticket = ValidateToken(message.IdToken, message, properties, validationParameters, out jwt);
var ticket = ValidateToken(message.IdToken, message, properties, validationParameters, out jwt);
await ValidateOpenIdConnectProtocolAsync(jwt, message);
var securityTokenValidatedContext = await RunSecurityTokenValidatedEventAsync(message, ticket);
if (securityTokenValidatedContext.HandledResponse)
var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(message, ticket, tokenEndpointResponse: null);
if (authenticationValidatedContext.HandledResponse)
{
return securityTokenValidatedContext.AuthenticationTicket;
return authenticationValidatedContext.AuthenticationTicket;
}
else if (securityTokenValidatedContext.Skipped)
else if (authenticationValidatedContext.Skipped)
{
return null;
}
message = authenticationValidatedContext.ProtocolMessage;
ticket = authenticationValidatedContext.AuthenticationTicket;
// Hybrid Flow
if (message.Code != null)
{
var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(message, properties, ticket, jwt);
@ -534,6 +548,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
return null;
}
ticket = authorizationCodeReceivedContext.AuthenticationTicket;
}
return ticket;
@ -568,17 +583,12 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// <summary>
/// Goes to UserInfo endpoint to retrieve additional claims and add any unique claims to the given identity.
/// </summary>
/// <param name="properties">Authentication Properties</param>
/// <param name="message">message that is being processed</param>
/// <param name="ticket">authentication ticket with claims principal and identities</param>
/// <returns>Authentication ticket with identity with additional claims, if any.</returns>
protected virtual async Task<AuthenticationTicket> GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket)
protected virtual async Task<AuthenticationTicket> GetUserInformationAsync(OpenIdConnectMessage message, AuthenticationTicket ticket)
{
string userInfoEndpoint = null;
if (_configuration != null)
{
userInfoEndpoint = _configuration.UserInfoEndpoint;
}
string userInfoEndpoint = _configuration?.UserInfoEndpoint;
if (string.IsNullOrEmpty(userInfoEndpoint))
{
@ -593,6 +603,18 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
var userInfoResponse = await responseMessage.Content.ReadAsStringAsync();
var user = JObject.Parse(userInfoResponse);
var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(ticket, message, user);
if (userInformationReceivedContext.HandledResponse)
{
return userInformationReceivedContext.AuthenticationTicket;
}
else if (userInformationReceivedContext.Skipped)
{
return null;
}
ticket = userInformationReceivedContext.AuthenticationTicket;
user = userInformationReceivedContext.User;
var identity = (ClaimsIdentity)ticket.Principal.Identity;
var subjectClaimType = identity.FindFirst(ClaimTypes.NameIdentifier);
if (subjectClaimType == null)
@ -846,68 +868,71 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return authorizationCodeReceivedContext;
}
private async Task<AuthorizationCodeRedeemedContext> RunAuthorizationCodeRedeemedEventAsync(OpenIdConnectMessage message, OpenIdConnectTokenEndpointResponse tokenEndpointResponse)
private async Task<TokenResponseReceivedContext> RunTokenResponseReceivedEventAsync(OpenIdConnectMessage message, OpenIdConnectTokenEndpointResponse tokenEndpointResponse)
{
Logger.LogDebug(Resources.OIDCH_0042_AuthorizationCodeRedeemed, message.Code);
var authorizationCodeRedeemedContext = new AuthorizationCodeRedeemedContext(Context, Options)
Logger.LogDebug("Token response received.");
var tokenResponseReceivedContext = new TokenResponseReceivedContext(Context, Options)
{
Code = message.Code,
ProtocolMessage = message,
TokenEndpointResponse = tokenEndpointResponse
};
await Options.Events.AuthorizationCodeRedeemed(authorizationCodeRedeemedContext);
if (authorizationCodeRedeemedContext.HandledResponse)
await Options.Events.TokenResponseReceived(tokenResponseReceivedContext);
if (tokenResponseReceivedContext.HandledResponse)
{
Logger.LogVerbose(Resources.OIDCH_0043_AuthorizationCodeRedeemedContextHandledResponse);
}
else if (authorizationCodeRedeemedContext.Skipped)
else if (tokenResponseReceivedContext.Skipped)
{
Logger.LogVerbose(Resources.OIDCH_0044_AuthorizationCodeRedeemedContextSkipped);
}
return authorizationCodeRedeemedContext;
return tokenResponseReceivedContext;
}
private async Task<SecurityTokenReceivedContext> RunSecurityTokenReceivedEventAsync(OpenIdConnectMessage message)
private async Task<AuthenticationValidatedContext> RunAuthenticationValidatedEventAsync(OpenIdConnectMessage message, AuthenticationTicket ticket, OpenIdConnectTokenEndpointResponse tokenEndpointResponse)
{
Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
var securityTokenReceivedContext = new SecurityTokenReceivedContext(Context, Options)
{
ProtocolMessage = message,
};
await Options.Events.SecurityTokenReceived(securityTokenReceivedContext);
if (securityTokenReceivedContext.HandledResponse)
{
Logger.LogVerbose(Resources.OIDCH_0008_SecurityTokenReceivedContextHandledResponse);
}
else if (securityTokenReceivedContext.Skipped)
{
Logger.LogVerbose(Resources.OIDCH_0009_SecurityTokenReceivedContextSkipped);
}
return securityTokenReceivedContext;
}
private async Task<SecurityTokenValidatedContext> RunSecurityTokenValidatedEventAsync(OpenIdConnectMessage message, AuthenticationTicket ticket)
{
var securityTokenValidatedContext = new SecurityTokenValidatedContext(Context, Options)
var authenticationValidatedContext = new AuthenticationValidatedContext(Context, Options)
{
AuthenticationTicket = ticket,
ProtocolMessage = message
ProtocolMessage = message,
TokenEndpointResponse = tokenEndpointResponse,
};
await Options.Events.SecurityTokenValidated(securityTokenValidatedContext);
if (securityTokenValidatedContext.HandledResponse)
await Options.Events.AuthenticationValidated(authenticationValidatedContext);
if (authenticationValidatedContext.HandledResponse)
{
Logger.LogVerbose(Resources.OIDCH_0012_SecurityTokenValidatedContextHandledResponse);
Logger.LogVerbose("AuthenticationValidated.HandledResponse");
}
else if (securityTokenValidatedContext.Skipped)
else if (authenticationValidatedContext.Skipped)
{
Logger.LogVerbose(Resources.OIDCH_0013_SecurityTokenValidatedContextSkipped);
Logger.LogVerbose("AuthenticationValidated.Skipped");
}
return securityTokenValidatedContext;
return authenticationValidatedContext;
}
private async Task<UserInformationReceivedContext> RunUserInformationReceivedEventAsync(AuthenticationTicket ticket, OpenIdConnectMessage message, JObject user)
{
Logger.LogDebug("User information received:" + user.ToString());
var userInformationReceivedContext = new UserInformationReceivedContext(Context, Options)
{
AuthenticationTicket = ticket,
ProtocolMessage = message,
User = user,
};
await Options.Events.UserInformationReceived(userInformationReceivedContext);
if (userInformationReceivedContext.HandledResponse)
{
Logger.LogVerbose("The UserInformationReceived event returned Handled.");
}
else if (userInformationReceivedContext.Skipped)
{
Logger.LogVerbose("The UserInformationReceived event returned Skipped.");
}
return userInformationReceivedContext;
}
private async Task<AuthenticationFailedContext> RunAuthenticationFailedEventAsync(OpenIdConnectMessage message, Exception exception)
@ -980,7 +1005,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
ticket.Properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
}
// Rename?
if (Options.UseTokenLifetime)
{
var issued = validatedToken.ValidFrom;
@ -1024,14 +1048,33 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// <returns>True if the request was handled, false if the next middleware should be invoked.</returns>
public override Task<bool> InvokeAsync()
{
return InvokeReplyPathAsync();
return InvokeReturnPathAsync();
}
private async Task<bool> InvokeReplyPathAsync()
private async Task<bool> InvokeReturnPathAsync()
{
var ticket = await HandleAuthenticateOnceAsync();
if (ticket != null)
{
Logger.LogDebug("Authentication completed.");
var authenticationCompletedContext = new AuthenticationCompletedContext(Context, Options)
{
AuthenticationTicket = ticket,
};
await Options.Events.AuthenticationCompleted(authenticationCompletedContext);
if (authenticationCompletedContext.HandledResponse)
{
Logger.LogVerbose("The AuthenticationCompleted event returned Handled.");
return true;
}
else if (authenticationCompletedContext.Skipped)
{
Logger.LogVerbose("The AuthenticationCompleted event returned Skipped.");
return false;
}
ticket = authenticationCompletedContext.AuthenticationTicket;
if (ticket.Principal != null)
{
await Request.HttpContext.Authentication.SignInAsync(Options.SignInScheme, ticket.Principal, ticket.Properties);

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
// 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.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Linq;
@ -15,7 +14,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
public OpenIdConnectTokenEndpointResponse(JObject jsonResponse)
{
JsonResponse = jsonResponse;
Message = new OpenIdConnectMessage()
ProtocolMessage = new OpenIdConnectMessage()
{
AccessToken = JsonResponse.Value<string>(OpenIdConnectParameterNames.AccessToken),
IdToken = JsonResponse.Value<string>(OpenIdConnectParameterNames.IdToken),
@ -27,7 +26,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// <summary>
/// OpenIdConnect message that contains the id token and access tokens
/// </summary>
public OpenIdConnectMessage Message { get; set; }
public OpenIdConnectMessage ProtocolMessage { get; set; }
/// <summary>
/// Json response returned from the token endpoint

View File

@ -186,38 +186,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return string.Format(CultureInfo.CurrentCulture, GetString("OIDCH_0033_NonceAlreadyExists"), p0);
}
/// <summary>
/// OIDCH_0034: RedirectToIdentityProviderContext.HandledResponse
/// </summary>
internal static string OIDCH_0034_RedirectToIdentityProviderContextHandledResponse
{
get { return GetString("OIDCH_0034_RedirectToIdentityProviderContextHandledResponse"); }
}
/// <summary>
/// OIDCH_0034: RedirectToIdentityProviderContext.HandledResponse
/// </summary>
internal static string FormatOIDCH_0034_RedirectToIdentityProviderContextHandledResponse()
{
return GetString("OIDCH_0034_RedirectToIdentityProviderContextHandledResponse");
}
/// <summary>
/// OIDCH_0035: RedirectToIdentityProviderContext.Skipped
/// </summary>
internal static string OIDCH_0035_RedirectToIdentityProviderContextSkipped
{
get { return GetString("OIDCH_0035_RedirectToIdentityProviderContextSkipped"); }
}
/// <summary>
/// OIDCH_0035: RedirectToIdentityProviderContext.Skipped
/// </summary>
internal static string FormatOIDCH_0035_RedirectToIdentityProviderContextSkipped()
{
return GetString("OIDCH_0035_RedirectToIdentityProviderContextSkipped");
}
/// <summary>
/// OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: '{0}'.
/// </summary>
@ -426,38 +394,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return GetString("OIDCH_0007_UpdatingConfiguration");
}
/// <summary>
/// OIDCH_0008: SecurityTokenReceivedContext.HandledResponse
/// </summary>
internal static string OIDCH_0008_SecurityTokenReceivedContextHandledResponse
{
get { return GetString("OIDCH_0008_SecurityTokenReceivedContextHandledResponse"); }
}
/// <summary>
/// OIDCH_0008: SecurityTokenReceivedContext.HandledResponse
/// </summary>
internal static string FormatOIDCH_0008_SecurityTokenReceivedContextHandledResponse()
{
return GetString("OIDCH_0008_SecurityTokenReceivedContextHandledResponse");
}
/// <summary>
/// OIDCH_0009: SecurityTokenReceivedContext.Skipped
/// </summary>
internal static string OIDCH_0009_SecurityTokenReceivedContextSkipped
{
get { return GetString("OIDCH_0009_SecurityTokenReceivedContextSkipped"); }
}
/// <summary>
/// OIDCH_0009: SecurityTokenReceivedContext.Skipped
/// </summary>
internal static string FormatOIDCH_0009_SecurityTokenReceivedContextSkipped()
{
return GetString("OIDCH_0009_SecurityTokenReceivedContextSkipped");
}
/// <summary>
/// OIDCH_0010: Validated Security Token must be a JwtSecurityToken was: '{0}'.
/// </summary>
@ -490,38 +426,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return string.Format(CultureInfo.CurrentCulture, GetString("OIDCH_0011_UnableToValidateToken"), p0);
}
/// <summary>
/// OIDCH_0012: SecurityTokenValidatedContext.HandledResponse
/// </summary>
internal static string OIDCH_0012_SecurityTokenValidatedContextHandledResponse
{
get { return GetString("OIDCH_0012_SecurityTokenValidatedContextHandledResponse"); }
}
/// <summary>
/// OIDCH_0012: SecurityTokenValidatedContext.HandledResponse
/// </summary>
internal static string FormatOIDCH_0012_SecurityTokenValidatedContextHandledResponse()
{
return GetString("OIDCH_0012_SecurityTokenValidatedContextHandledResponse");
}
/// <summary>
/// OIDCH_0013: SecurityTokenValidatedContext.Skipped
/// </summary>
internal static string OIDCH_0013_SecurityTokenValidatedContextSkipped
{
get { return GetString("OIDCH_0013_SecurityTokenValidatedContextSkipped"); }
}
/// <summary>
/// OIDCH_0013: SecurityTokenValidatedContext.Skipped
/// </summary>
internal static string FormatOIDCH_0013_SecurityTokenValidatedContextSkipped()
{
return GetString("OIDCH_0013_SecurityTokenValidatedContextSkipped");
}
/// <summary>
/// OIDCH_0014: AuthorizationCode received: '{0}'.
/// </summary>
@ -666,22 +570,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
return string.Format(CultureInfo.CurrentCulture, GetString("OIDCH_0041_Subject_Claim_Not_Found"), p0);
}
/// <summary>
/// OIDCH_0042: Authorization Code redeemed: '{0}'
/// </summary>
internal static string OIDCH_0042_AuthorizationCodeRedeemed
{
get { return GetString("OIDCH_0042_AuthorizationCodeRedeemed"); }
}
/// <summary>
/// OIDCH_0042: Authorization Code redeemed: '{0}'
/// </summary>
internal static string FormatOIDCH_0042_AuthorizationCodeRedeemed(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("OIDCH_0042_AuthorizationCodeRedeemed"), p0);
}
/// <summary>
/// OIDCH_0043: AuthorizationCodeRedeemedContext.HandledResponse
/// </summary>

View File

@ -150,12 +150,6 @@
<data name="OIDCH_0033_NonceAlreadyExists" xml:space="preserve">
<value>OIDCH_0033: ProtocolValidator.RequireNonce == true. The generated nonce already exists: this usually indicates the nonce is not unique or has been used. The nonce is: '{0}'.</value>
</data>
<data name="OIDCH_0034_RedirectToIdentityProviderContextHandledResponse" xml:space="preserve">
<value>OIDCH_0034: RedirectToIdentityProviderContext.HandledResponse</value>
</data>
<data name="OIDCH_0035_RedirectToIdentityProviderContextSkipped" xml:space="preserve">
<value>OIDCH_0035: RedirectToIdentityProviderContext.Skipped</value>
</data>
<data name="OIDCH_0036_UriIsNotWellFormed" xml:space="preserve">
<value>OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: '{0}'.</value>
</data>
@ -195,24 +189,12 @@
<data name="OIDCH_0007_UpdatingConfiguration" xml:space="preserve">
<value>OIDCH_0007: Updating configuration</value>
</data>
<data name="OIDCH_0008_SecurityTokenReceivedContextHandledResponse" xml:space="preserve">
<value>OIDCH_0008: SecurityTokenReceivedContext.HandledResponse</value>
</data>
<data name="OIDCH_0009_SecurityTokenReceivedContextSkipped" xml:space="preserve">
<value>OIDCH_0009: SecurityTokenReceivedContext.Skipped</value>
</data>
<data name="OIDCH_0010_ValidatedSecurityTokenNotJwt" xml:space="preserve">
<value>OIDCH_0010: Validated Security Token must be a JwtSecurityToken was: '{0}'.</value>
</data>
<data name="OIDCH_0011_UnableToValidateToken" xml:space="preserve">
<value>OIDCH_0011: Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: '{0}'."</value>
</data>
<data name="OIDCH_0012_SecurityTokenValidatedContextHandledResponse" xml:space="preserve">
<value>OIDCH_0012: SecurityTokenValidatedContext.HandledResponse</value>
</data>
<data name="OIDCH_0013_SecurityTokenValidatedContextSkipped" xml:space="preserve">
<value>OIDCH_0013: SecurityTokenValidatedContext.Skipped</value>
</data>
<data name="OIDCH_0014_AuthorizationCodeReceived" xml:space="preserve">
<value>OIDCH_0014: AuthorizationCode received: '{0}'.</value>
</data>
@ -240,9 +222,6 @@
<data name="OIDCH_0041_Subject_Claim_Not_Found" xml:space="preserve">
<value>OIDCH_0041: Subject claim not found in {0}.</value>
</data>
<data name="OIDCH_0042_AuthorizationCodeRedeemed" xml:space="preserve">
<value>OIDCH_0042: Authorization Code redeemed: '{0}'</value>
</data>
<data name="OIDCH_0043_AuthorizationCodeRedeemedContextHandledResponse" xml:space="preserve">
<value>OIDCH_0043: AuthorizationCodeRedeemedContext.HandledResponse</value>
</data>

View File

@ -5,13 +5,11 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Primitives;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Authentication

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
return Task.FromResult(new OpenIdConnectTokenEndpointResponse(jsonResponse));
}
protected override Task<AuthenticationTicket> GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket)
protected override Task<AuthenticationTicket> GetUserInformationAsync(OpenIdConnectMessage message, AuthenticationTicket ticket)
{
var claimsIdentity = (ClaimsIdentity)ticket.Principal.Identity;
if (claimsIdentity == null)

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
@ -19,7 +18,6 @@ using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
@ -56,7 +54,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
Assert.False(logger.IsEnabled(LogLevel.Warning));
}
[Theory, MemberData("AuthenticateCoreStateDataSet")]
[Theory, MemberData(nameof(AuthenticateCoreStateDataSet))]
public async Task AuthenticateCoreState(Action<OpenIdConnectOptions> action, OpenIdConnectMessage message)
{
var handler = new OpenIdConnectHandlerForTestingAuthenticate();
@ -103,7 +101,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
options.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeRedeemed = context =>
OnTokenResponseReceived = context =>
{
context.HandleResponse();
if (context.ProtocolMessage.State == null && !context.ProtocolMessage.Parameters.ContainsKey(ExpectedStateParameter))
@ -118,271 +116,6 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
};
}
#region Configure Options for AuthenticateCore variations
private static void DefaultOptions(OpenIdConnectOptions options)
{
options.AuthenticationScheme = "OpenIdConnectHandlerTest";
options.SignInScheme = "OpenIdConnectHandlerTest";
options.ConfigurationManager = TestUtilities.DefaultOpenIdConnectConfigurationManager;
options.ClientId = Guid.NewGuid().ToString();
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
}
private static void AuthorizationCodeReceivedHandledOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.SecurityTokenValidator = MockSecurityTokenValidator();
options.ProtocolValidator = MockProtocolValidator();
options.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = (context) =>
{
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void AuthorizationCodeReceivedSkippedOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.SecurityTokenValidator = MockSecurityTokenValidator();
options.ProtocolValidator = MockProtocolValidator();
options.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = (context) =>
{
context.SkipToNextMiddleware();
return Task.FromResult<object>(null);
}
};
}
private static void AuthenticationErrorHandledOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.SecurityTokenValidator = MockSecurityTokenValidator();
options.ProtocolValidator = MockProtocolValidator();
options.Events = new OpenIdConnectEvents()
{
OnAuthenticationFailed = (context) =>
{
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void AuthenticationErrorSkippedOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.SecurityTokenValidator = MockSecurityTokenValidator();
options.ProtocolValidator = MockProtocolValidator();
options.Events = new OpenIdConnectEvents()
{
OnAuthenticationFailed = (context) =>
{
context.SkipToNextMiddleware();
return Task.FromResult<object>(null);
}
};
}
private static void MessageReceivedHandledOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.Events = new OpenIdConnectEvents()
{
OnMessageReceived = (context) =>
{
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void CodeReceivedAndRedeemedHandledOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.ResponseType = OpenIdConnectResponseTypes.Code;
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
options.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeRedeemed = (context) =>
{
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void CodeReceivedAndRedeemedSkippedOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.ResponseType = OpenIdConnectResponseTypes.Code;
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
options.Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeRedeemed = (context) =>
{
context.SkipToNextMiddleware();
return Task.FromResult<object>(null);
}
};
}
private static void GetUserInfoFromUIEndpoint(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.ResponseType = OpenIdConnectResponseTypes.Code;
options.ProtocolValidator.RequireNonce = false;
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
options.GetClaimsFromUserInfoEndpoint = true;
options.SecurityTokenValidator = MockSecurityTokenValidator();
options.Events = new OpenIdConnectEvents()
{
OnSecurityTokenValidated = (context) =>
{
var claimValue = context.AuthenticationTicket.Principal.FindFirst("test claim");
Assert.Equal(claimValue.Value, "test value");
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void MessageReceivedSkippedOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.Events = new OpenIdConnectEvents()
{
OnMessageReceived = (context) =>
{
context.SkipToNextMiddleware();
return Task.FromResult<object>(null);
}
};
}
private static void MessageWithErrorOptions(OpenIdConnectOptions options)
{
AuthenticationErrorHandledOptions(options);
}
private static void SecurityTokenReceivedHandledOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.Events = new OpenIdConnectEvents()
{
OnSecurityTokenReceived = (context) =>
{
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void SecurityTokenReceivedSkippedOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.Events = new OpenIdConnectEvents()
{
OnSecurityTokenReceived = (context) =>
{
context.SkipToNextMiddleware();
return Task.FromResult<object>(null);
}
};
}
private static ISecurityTokenValidator MockSecurityTokenValidator()
{
var mockValidator = new Mock<ISecurityTokenValidator>();
mockValidator.Setup(v => v.ValidateToken(It.IsAny<string>(), It.IsAny<TokenValidationParameters>(), out specCompliantJwt)).Returns(new ClaimsPrincipal());
mockValidator.Setup(v => v.CanReadToken(It.IsAny<string>())).Returns(true);
return mockValidator.Object;
}
private static OpenIdConnectProtocolValidator MockProtocolValidator()
{
var mockProtocolValidator = new Mock<OpenIdConnectProtocolValidator>();
mockProtocolValidator.Setup(v => v.Validate(It.IsAny<JwtSecurityToken>(), It.IsAny<OpenIdConnectProtocolValidationContext>()));
return mockProtocolValidator.Object;
}
private static void SecurityTokenValidatorCannotReadToken(OpenIdConnectOptions options)
{
AuthenticationErrorHandledOptions(options);
var mockValidator = new Mock<ISecurityTokenValidator>();
SecurityToken jwt = null;
mockValidator.Setup(v => v.ValidateToken(It.IsAny<string>(), It.IsAny<TokenValidationParameters>(), out jwt)).Returns(new ClaimsPrincipal());
mockValidator.Setup(v => v.CanReadToken(It.IsAny<string>())).Returns(false);
options.SecurityTokenValidator = mockValidator.Object;
}
private static void SecurityTokenValidatorThrows(OpenIdConnectOptions options)
{
AuthenticationErrorHandledOptions(options);
var mockValidator = new Mock<ISecurityTokenValidator>();
SecurityToken jwt = null;
mockValidator.Setup(v => v.ValidateToken(It.IsAny<string>(), It.IsAny<TokenValidationParameters>(), out jwt)).Throws<SecurityTokenSignatureKeyNotFoundException>();
mockValidator.Setup(v => v.CanReadToken(It.IsAny<string>())).Returns(true);
options.SecurityTokenValidator = mockValidator.Object;
}
private static void SecurityTokenValidatorValidatesAllTokens(OpenIdConnectOptions options)
{
DefaultOptions(options);
options.SecurityTokenValidator = MockSecurityTokenValidator();
options.ProtocolValidator.RequireTimeStampInNonce = false;
options.ProtocolValidator.RequireNonce = false;
}
private static void SecurityTokenValidatedHandledOptions(OpenIdConnectOptions options)
{
SecurityTokenValidatorValidatesAllTokens(options);
options.Events = new OpenIdConnectEvents()
{
OnSecurityTokenValidated = (context) =>
{
context.HandleResponse();
return Task.FromResult<object>(null);
}
};
}
private static void SecurityTokenValidatedSkippedOptions(OpenIdConnectOptions options)
{
SecurityTokenValidatorValidatesAllTokens(options);
options.Events = new OpenIdConnectEvents()
{
OnSecurityTokenValidated = (context) =>
{
context.SkipToNextMiddleware();
return Task.FromResult<object>(null);
}
};
}
private static void StateNullOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
}
private static void StateEmptyOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
}
private static void StateInvalidOptions(OpenIdConnectOptions options)
{
DefaultOptions(options);
}
#endregion
private static Task EmptyTask() { return Task.FromResult(0); }
private static TestServer CreateServer(ConfigureOptions<OpenIdConnectOptions> options, IUrlEncoder encoder, OpenIdConnectHandler handler = null)
{
return TestServer.Create(
@ -401,24 +134,5 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
}
);
}
private static TestServer CreateServer(ConfigureOptions<OpenIdConnectOptions> configureOptions, IUrlEncoder encoder, ILoggerFactory loggerFactory, OpenIdConnectHandler handler = null)
{
return TestServer.Create(
app =>
{
app.UseMiddleware<OpenIdConnectMiddlewareForTestingAuthenticate>(configureOptions, encoder, loggerFactory, handler);
app.Use(async (context, next) =>
{
await next();
});
},
services =>
{
services.AddWebEncoders();
services.AddDataProtection();
}
);
}
}
}

View File

@ -106,28 +106,47 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
}
/// <summary>
/// Tests RedirectToIdentityProviderContext replaces the OpenIdConnectMesssage correctly.
/// Tests RedirectForAuthenticationContext replaces the OpenIdConnectMesssage correctly.
/// </summary>
/// <returns>Task</returns>
[Theory]
[InlineData(Challenge, OpenIdConnectRequestType.AuthenticationRequest)]
[InlineData(Signout, OpenIdConnectRequestType.LogoutRequest)]
public async Task ChallengeSettingMessage(string challenge, OpenIdConnectRequestType requestType)
[Fact]
public async Task ChallengeSettingMessage()
{
var configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = ExpectedAuthorizeRequest,
};
var queryValues = new ExpectedQueryValues(DefaultAuthority, configuration)
{
RequestType = OpenIdConnectRequestType.AuthenticationRequest
};
var server = CreateServer(SetProtocolMessageOptions);
var transaction = await SendAsync(server, DefaultHost + Challenge);
Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode);
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, new string[] {});
}
/// <summary>
/// Tests RedirectForSignOutContext replaces the OpenIdConnectMesssage correctly.
/// </summary>
/// <returns>Task</returns>
[Fact]
public async Task SignOutSettingMessage()
{
var configuration = new OpenIdConnectConfiguration
{
EndSessionEndpoint = ExpectedLogoutRequest
};
var queryValues = new ExpectedQueryValues(DefaultAuthority, configuration)
{
RequestType = requestType
RequestType = OpenIdConnectRequestType.LogoutRequest
};
var server = CreateServer(SetProtocolMessageOptions);
var transaction = await SendAsync(server, DefaultHost + challenge);
var transaction = await SendAsync(server, DefaultHost + Signout);
Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode);
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, new string[] {});
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, new string[] { });
}
private static void SetProtocolMessageOptions(OpenIdConnectOptions options)
@ -138,7 +157,12 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
options.AutomaticAuthentication = true;
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = (context) =>
OnRedirectToAuthenticationEndpoint = (context) =>
{
context.ProtocolMessage = mockOpenIdConnectMessage.Object;
return Task.FromResult<object>(null);
},
OnRedirectToEndSessionEndpoint = (context) =>
{
context.ProtocolMessage = mockOpenIdConnectMessage.Object;
return Task.FromResult<object>(null);
@ -170,7 +194,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
options.AutomaticAuthentication = challenge.Equals(ChallengeWithOutContext);
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
OnRedirectToAuthenticationEndpoint = context =>
{
context.ProtocolMessage.State = userState;
return Task.FromResult<object>(null);
@ -221,7 +245,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
SetOptions(options, DefaultParameters(), queryValues);
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
OnRedirectToAuthenticationEndpoint = context =>
{
context.ProtocolMessage.ClientId = queryValuesSetInEvent.ClientId;
context.ProtocolMessage.RedirectUri = queryValuesSetInEvent.RedirectUri;
@ -344,7 +368,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
{
app.UseCookieAuthentication(options =>
{
options.AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
app.UseOpenIdConnectAuthentication(configureOptions);
app.Use(async (context, next) =>