This commit is contained in:
parent
9ce84d39c2
commit
501bd4ff10
|
|
@ -5,8 +5,8 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
{
|
||||
public interface INonceCache
|
||||
{
|
||||
string AddNonce(string nonce);
|
||||
bool TryAddNonce(string nonce);
|
||||
|
||||
bool TryRemoveNonce(string nonce);
|
||||
bool HasNonce(string nonce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// <summary>
|
||||
/// The prefix used to for the a nonce in the cookie
|
||||
/// </summary>
|
||||
internal const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
||||
public const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
||||
|
||||
/// <summary>
|
||||
/// The property for the RedirectUri that was used when asking for a 'authorizationCode'
|
||||
|
|
@ -36,6 +36,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// <summary>
|
||||
/// Constant used to identify state in openIdConnect protocal message
|
||||
/// </summary>
|
||||
internal const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
||||
public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,5 +26,16 @@ namespace Microsoft.AspNet.Builder
|
|||
Name = optionsName
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="OpenIdConnectAuthenticationMiddleware"/> into the ASP.NET runtime.
|
||||
/// </summary>
|
||||
/// <param name="app">The application builder</param>
|
||||
/// <param name="options">Options which control the processing of the OpenIdConnect protocol and token validation.</param>
|
||||
/// <returns>The application builder</returns>
|
||||
public static IApplicationBuilder UseOpenIdConnectAuthentication(this IApplicationBuilder app, IOptions<OpenIdConnectAuthenticationOptions> options)
|
||||
{
|
||||
return app.UseMiddleware<OpenIdConnectAuthenticationMiddleware>(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
|
||||
// Set End_Session_Endpoint in order:
|
||||
// 1. properties.Redirect
|
||||
// 2. Options.Wreply
|
||||
// 2. Options.PostLogoutRedirectUri
|
||||
var properties = new AuthenticationProperties(signout.Properties);
|
||||
if (!string.IsNullOrEmpty(properties.RedirectUri))
|
||||
{
|
||||
|
|
@ -98,7 +98,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
string redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
|
||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||
{
|
||||
_logger.LogWarning("The logout redirect URI is malformed: {0}", (redirectUri ?? "null"));
|
||||
_logger.LogWarning(Resources.OIDCH_0051_RedirectUriLogoutIsNotWellFormed, redirectUri);
|
||||
}
|
||||
|
||||
Response.Redirect(redirectUri);
|
||||
|
|
@ -115,28 +115,37 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// Responds to a 401 Challenge. Sends an OpenIdConnect message to the 'identity authority' to obtain an identity.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>Uses log id's OIDCH-0026 - OIDCH-0050, next num: 37</remarks>
|
||||
protected override async Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0026_ApplyResponseChallengeAsync, this.GetType());
|
||||
}
|
||||
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0027_401_ConvertedTo_403);
|
||||
Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Response.StatusCode != 401)
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0028_StatusCodeNot401, Response.StatusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// When Automatic should redirect on 401 even if there wasn't an explicit challenge.
|
||||
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0029_ChallengeContextEqualsNull);
|
||||
return;
|
||||
}
|
||||
|
||||
// order for redirect_uri
|
||||
// order for local RedirectUri
|
||||
// 1. challenge.Properties.RedirectUri
|
||||
// 2. CurrentUri
|
||||
// 2. CurrentUri if Options.DefaultToCurrentUriOnRedirect is true)
|
||||
AuthenticationProperties properties;
|
||||
if (ChallengeContext == null)
|
||||
{
|
||||
|
|
@ -147,12 +156,22 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
properties = new AuthenticationProperties(ChallengeContext.Properties);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(properties.RedirectUri))
|
||||
if (!string.IsNullOrWhiteSpace(properties.RedirectUri))
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0030_Using_Properties_RedirectUri, properties.RedirectUri);
|
||||
}
|
||||
else if (Options.DefaultToCurrentUriOnRedirect)
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0032_UsingCurrentUriRedirectUri, CurrentUri);
|
||||
properties.RedirectUri = CurrentUri;
|
||||
}
|
||||
|
||||
// this value will be passed to the AuthorizationCodeReceivedNotification
|
||||
if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri);
|
||||
}
|
||||
|
||||
// When redeeming a 'code' for an AccessToken, this value is needed
|
||||
if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
|
||||
{
|
||||
properties.Dictionary.Add(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey, Options.RedirectUri);
|
||||
|
|
@ -163,14 +182,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||
}
|
||||
|
||||
OpenIdConnectMessage openIdConnectMessage = new OpenIdConnectMessage
|
||||
var message = new OpenIdConnectMessage
|
||||
{
|
||||
ClientId = Options.ClientId,
|
||||
IssuerAddress = _configuration == null ? string.Empty : (_configuration.AuthorizationEndpoint ?? string.Empty),
|
||||
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
|
||||
RedirectUri = Options.RedirectUri,
|
||||
// [brentschmaltz] - this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
|
||||
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
|
||||
Resource = Options.Resource,
|
||||
ResponseMode = OpenIdConnectResponseModes.FormPost,
|
||||
ResponseMode = Options.ResponseMode,
|
||||
ResponseType = Options.ResponseType,
|
||||
Scope = Options.Scope,
|
||||
State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(properties))
|
||||
|
|
@ -178,33 +198,45 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
|
||||
if (Options.ProtocolValidator.RequireNonce)
|
||||
{
|
||||
openIdConnectMessage.Nonce = Options.ProtocolValidator.GenerateNonce();
|
||||
message.Nonce = Options.ProtocolValidator.GenerateNonce();
|
||||
if (Options.NonceCache != null)
|
||||
{
|
||||
Options.NonceCache.AddNonce(openIdConnectMessage.Nonce);
|
||||
if (!Options.NonceCache.TryAddNonce(message.Nonce))
|
||||
{
|
||||
_logger.LogError(Resources.OIDCH_0033_TryAddNonceFailed, message.Nonce);
|
||||
throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0033_TryAddNonceFailed, message.Nonce));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RememberNonce(openIdConnectMessage.Nonce);
|
||||
WriteNonceCookie(message.Nonce);
|
||||
}
|
||||
}
|
||||
|
||||
var notification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = openIdConnectMessage
|
||||
ProtocolMessage = message
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(notification);
|
||||
if (!notification.HandledResponse)
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||
{
|
||||
string redirectUri = notification.ProtocolMessage.CreateAuthenticationRequestUrl();
|
||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||
{
|
||||
_logger.LogWarning("Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: {0}", (redirectUri ?? "null"));
|
||||
}
|
||||
|
||||
Response.Redirect(redirectUri);
|
||||
_logger.LogInformation(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||
return;
|
||||
}
|
||||
else if (redirectToIdentityProviderNotification.Skipped)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
||||
return;
|
||||
}
|
||||
|
||||
string redirectUri = redirectToIdentityProviderNotification.ProtocolMessage.CreateAuthenticationRequestUrl();
|
||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||
{
|
||||
_logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
|
||||
}
|
||||
|
||||
Response.Redirect(redirectUri);
|
||||
}
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
|
|
@ -216,15 +248,21 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// Invoked to process incoming OpenIdConnect messages.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
|
||||
/// <remarks>Uses log id's OIDCH-0000 - OIDCH-0025</remarks>
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType());
|
||||
}
|
||||
|
||||
// Allow login to be constrained to a specific path. Need to make this runtime configurable.
|
||||
if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
OpenIdConnectMessage openIdConnectMessage = null;
|
||||
OpenIdConnectMessage message = null;
|
||||
|
||||
// assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
|
||||
if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
|
||||
|
|
@ -235,180 +273,215 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
{
|
||||
IFormCollection form = await Request.ReadFormAsync();
|
||||
Request.Body.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
openIdConnectMessage = new OpenIdConnectMessage(form);
|
||||
message = new OpenIdConnectMessage(form);
|
||||
}
|
||||
|
||||
if (openIdConnectMessage == null)
|
||||
if (message == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var messageReceivedNotification = new MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
ProtocolMessage = openIdConnectMessage
|
||||
};
|
||||
_logger.LogDebug(Resources.OIDCH_0001_MessageReceived, message.BuildRedirectUrl());
|
||||
}
|
||||
|
||||
var messageReceivedNotification =
|
||||
new MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = message
|
||||
};
|
||||
|
||||
await Options.Notifications.MessageReceived(messageReceivedNotification);
|
||||
if (messageReceivedNotification.HandledResponse)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
|
||||
return messageReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (messageReceivedNotification.Skipped)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
// runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
|
||||
// should process.
|
||||
AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
|
||||
// runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we should process.
|
||||
if (string.IsNullOrWhiteSpace(message.State))
|
||||
{
|
||||
_logger.LogError(Resources.OIDCH_0004_MessageStateIsNullOrWhiteSpace);
|
||||
return null;
|
||||
}
|
||||
|
||||
var properties = GetPropertiesFromState(message.State);
|
||||
if (properties == null)
|
||||
{
|
||||
_logger.LogWarning("The state field is missing or invalid.");
|
||||
_logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
|
||||
return null;
|
||||
}
|
||||
|
||||
// devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
|
||||
if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error))
|
||||
if (!string.IsNullOrWhiteSpace(message.Error))
|
||||
{
|
||||
throw new OpenIdConnectProtocolException(
|
||||
string.Format(CultureInfo.InvariantCulture,
|
||||
openIdConnectMessage.Error,
|
||||
Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
|
||||
_logger.LogError(Resources.OIDCH_0006_MessageErrorNotNull, message.Error);
|
||||
throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageErrorNotNull, message.Error));
|
||||
}
|
||||
|
||||
// code is only accepted with id_token, in this version, hence check for code is inside this if
|
||||
// OpenIdConnect protocol allows a Code to be received without the id_token
|
||||
if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken))
|
||||
{
|
||||
_logger.LogWarning("The id_token is missing.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var securityTokenReceivedNotification = new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = openIdConnectMessage
|
||||
};
|
||||
|
||||
await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
|
||||
if (securityTokenReceivedNotification.HandledResponse)
|
||||
{
|
||||
return securityTokenReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenReceivedNotification.Skipped)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
AuthenticationTicket ticket = null;
|
||||
JwtSecurityToken jwt = null;
|
||||
|
||||
if (_configuration == null && Options.ConfigurationManager != null)
|
||||
{
|
||||
_logger.LogDebug(Resources.OIDCH_0007_UpdatingConfiguration);
|
||||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||
}
|
||||
|
||||
// Copy and augment to avoid cross request race conditions for updated configurations.
|
||||
TokenValidationParameters validationParameters = Options.TokenValidationParameters.Clone();
|
||||
if (_configuration != null)
|
||||
// OpenIdConnect protocol allows a Code to be received without the id_token
|
||||
if (!string.IsNullOrWhiteSpace(message.IdToken))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
|
||||
{
|
||||
validationParameters.ValidIssuer = _configuration.Issuer;
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(_configuration.Issuer))
|
||||
{
|
||||
validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? new[] { _configuration.Issuer } : validationParameters.ValidIssuers.Concat(new[] { _configuration.Issuer }));
|
||||
}
|
||||
|
||||
validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
|
||||
}
|
||||
|
||||
AuthenticationTicket ticket;
|
||||
SecurityToken validatedToken = null;
|
||||
ClaimsPrincipal principal = null;
|
||||
JwtSecurityToken jwt = null;
|
||||
|
||||
foreach (var validator in Options.SecurityTokenValidators)
|
||||
{
|
||||
if (validator.CanReadToken(openIdConnectMessage.IdToken))
|
||||
{
|
||||
principal = validator.ValidateToken(openIdConnectMessage.IdToken, validationParameters, out validatedToken);
|
||||
jwt = validatedToken as JwtSecurityToken;
|
||||
if (jwt == null)
|
||||
_logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
|
||||
var securityTokenReceivedNotification =
|
||||
new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
throw new InvalidOperationException("Validated Security Token must be a JwtSecurityToken was: " + (validatedToken == null ? "null" : validatedToken.GetType().ToString()));
|
||||
ProtocolMessage = message
|
||||
};
|
||||
|
||||
await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
|
||||
if (securityTokenReceivedNotification.HandledResponse)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
|
||||
return securityTokenReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenReceivedNotification.Skipped)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Copy and augment to avoid cross request race conditions for updated configurations.
|
||||
TokenValidationParameters validationParameters = Options.TokenValidationParameters.Clone();
|
||||
if (_configuration != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
|
||||
{
|
||||
validationParameters.ValidIssuer = _configuration.Issuer;
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(_configuration.Issuer))
|
||||
{
|
||||
validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(new[] { _configuration.Issuer }) ?? new[] { _configuration.Issuer };
|
||||
}
|
||||
|
||||
validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys) ?? _configuration.SigningKeys;
|
||||
}
|
||||
|
||||
SecurityToken validatedToken = null;
|
||||
ClaimsPrincipal principal = null;
|
||||
foreach (var validator in Options.SecurityTokenValidators)
|
||||
{
|
||||
if (validator.CanReadToken(message.IdToken))
|
||||
{
|
||||
principal = validator.ValidateToken(message.IdToken, validationParameters, out validatedToken);
|
||||
jwt = validatedToken as JwtSecurityToken;
|
||||
if (jwt == null)
|
||||
{
|
||||
_logger.LogError(Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType());
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (validatedToken == null)
|
||||
{
|
||||
throw new InvalidOperationException("No SecurityTokenValidator found for token: " + openIdConnectMessage.IdToken);
|
||||
}
|
||||
|
||||
ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
|
||||
if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState))
|
||||
{
|
||||
ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState;
|
||||
}
|
||||
|
||||
if (_configuration != null && !string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
|
||||
{
|
||||
ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
|
||||
}
|
||||
|
||||
if (Options.UseTokenLifetime)
|
||||
{
|
||||
// Override any session persistence to match the token lifetime.
|
||||
DateTime issued = validatedToken.ValidFrom;
|
||||
if (issued != DateTime.MinValue)
|
||||
if (validatedToken == null)
|
||||
{
|
||||
ticket.Properties.IssuedUtc = issued;
|
||||
_logger.LogError(Resources.OIDCH_0011_UnableToValidateToken, message.IdToken);
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0011_UnableToValidateToken, message.IdToken));
|
||||
}
|
||||
|
||||
DateTime expires = validatedToken.ValidTo;
|
||||
if (expires != DateTime.MinValue)
|
||||
ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
|
||||
if (!string.IsNullOrWhiteSpace(message.SessionState))
|
||||
{
|
||||
ticket.Properties.ExpiresUtc = expires;
|
||||
ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = message.SessionState;
|
||||
}
|
||||
|
||||
ticket.Properties.AllowRefresh = false;
|
||||
if (_configuration != null && !string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
|
||||
{
|
||||
ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
|
||||
}
|
||||
|
||||
// Rename?
|
||||
if (Options.UseTokenLifetime)
|
||||
{
|
||||
DateTime issued = validatedToken.ValidFrom;
|
||||
if (issued != DateTime.MinValue)
|
||||
{
|
||||
ticket.Properties.IssuedUtc = issued;
|
||||
}
|
||||
|
||||
DateTime expires = validatedToken.ValidTo;
|
||||
if (expires != DateTime.MinValue)
|
||||
{
|
||||
ticket.Properties.ExpiresUtc = expires;
|
||||
}
|
||||
}
|
||||
|
||||
var securityTokenValidatedNotification =
|
||||
new SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
AuthenticationTicket = ticket,
|
||||
ProtocolMessage = message
|
||||
};
|
||||
|
||||
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
|
||||
if (securityTokenValidatedNotification.HandledResponse)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
|
||||
return securityTokenValidatedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenValidatedNotification.Skipped)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
string nonce = jwt.Payload.Nonce;
|
||||
if (Options.NonceCache != null)
|
||||
{
|
||||
// if the nonce cannot be removed, it was used
|
||||
if (!Options.NonceCache.TryRemoveNonce(nonce))
|
||||
{
|
||||
nonce = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonce = ReadNonceCookie(nonce);
|
||||
}
|
||||
|
||||
var protocolValidationContext = new OpenIdConnectProtocolValidationContext
|
||||
{
|
||||
AuthorizationCode = message.Code,
|
||||
Nonce = nonce,
|
||||
};
|
||||
|
||||
Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
|
||||
}
|
||||
|
||||
var securityTokenValidatedNotification = new SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
if (message.Code != null)
|
||||
{
|
||||
AuthenticationTicket = ticket,
|
||||
ProtocolMessage = openIdConnectMessage
|
||||
};
|
||||
_logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code);
|
||||
if (ticket == null)
|
||||
{
|
||||
ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
|
||||
if (securityTokenValidatedNotification.HandledResponse)
|
||||
{
|
||||
return securityTokenValidatedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenValidatedNotification.Skipped)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var protocolValidationContext = new OpenIdConnectProtocolValidationContext
|
||||
{
|
||||
AuthorizationCode = openIdConnectMessage.Code,
|
||||
Nonce = RetrieveNonce(jwt.Payload.Nonce),
|
||||
};
|
||||
|
||||
Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
|
||||
if (openIdConnectMessage.Code != null)
|
||||
{
|
||||
var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
|
||||
{
|
||||
AuthenticationTicket = ticket,
|
||||
Code = openIdConnectMessage.Code,
|
||||
Code = message.Code,
|
||||
JwtSecurityToken = jwt,
|
||||
ProtocolMessage = openIdConnectMessage,
|
||||
ProtocolMessage = message,
|
||||
RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
|
||||
ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
|
||||
};
|
||||
|
|
@ -416,11 +489,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
|
||||
if (authorizationCodeReceivedNotification.HandledResponse)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse);
|
||||
return authorizationCodeReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (authorizationCodeReceivedNotification.Skipped)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -429,7 +504,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError("Exception occurred while processing message", exception);
|
||||
_logger.LogError(Resources.OIDCH_0017_ExceptionOccurredWhileProcessingMessage, exception);
|
||||
|
||||
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
|
||||
if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
|
||||
|
|
@ -437,20 +512,23 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
Options.ConfigurationManager.RequestRefresh();
|
||||
}
|
||||
|
||||
var authenticationFailedNotification = new AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = openIdConnectMessage,
|
||||
Exception = exception
|
||||
};
|
||||
var authenticationFailedNotification =
|
||||
new AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = message,
|
||||
Exception = exception
|
||||
};
|
||||
|
||||
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
|
||||
if (authenticationFailedNotification.HandledResponse)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
|
||||
return authenticationFailedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (authenticationFailedNotification.Skipped)
|
||||
{
|
||||
_logger.LogInformation(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +542,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// <param name="nonce">the nonce to remember.</param>
|
||||
/// <remarks><see cref="HttpResponse.Cookies.Append"/>is called to add a cookie with the name: 'OpenIdConnectAuthenticationDefaults.Nonce + <see cref="OpenIdConnectAuthenticationOptions.StringDataFormat.Protect"/>(nonce)'.
|
||||
/// The value of the cookie is: "N".</remarks>
|
||||
private void RememberNonce(string nonce)
|
||||
private void WriteNonceCookie(string nonce)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nonce))
|
||||
{
|
||||
|
|
@ -484,13 +562,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// <summary>
|
||||
/// Searches <see cref="HttpRequest.Cookies"/> for a matching nonce.
|
||||
/// </summary>
|
||||
/// <param name="nonceExpectedValue">the nonce that was found in the jwt token.</param>
|
||||
/// <returns>'nonceExpectedValue' if a cookie is found that matches, null otherwise.</returns>
|
||||
/// <param name="nonce">the nonce that we are looking for.</param>
|
||||
/// <returns>echos 'nonce' if a cookie is found that matches, null otherwise.</returns>
|
||||
/// <remarks>Examine <see cref="HttpRequest.Cookies.Keys"/> that start with the prefix: 'OpenIdConnectAuthenticationDefaults.Nonce'.
|
||||
/// <see cref="OpenIdConnectAuthenticationOptions.StringDataFormat.Unprotect"/> is used to obtain the actual 'nonce'. If the nonce is found, then <see cref="HttpResponse.Cookies.Delete"/> is called.</remarks>
|
||||
private string RetrieveNonce(string nonceExpectedValue)
|
||||
private string ReadNonceCookie(string nonce)
|
||||
{
|
||||
if (nonceExpectedValue == null)
|
||||
if (nonce == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
@ -502,7 +580,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
try
|
||||
{
|
||||
string nonceDecodedValue = Options.StringDataFormat.Unprotect(nonceKey.Substring(OpenIdConnectAuthenticationDefaults.CookieNoncePrefix.Length, nonceKey.Length - OpenIdConnectAuthenticationDefaults.CookieNoncePrefix.Length));
|
||||
if (nonceDecodedValue == nonceExpectedValue)
|
||||
if (nonceDecodedValue == nonce)
|
||||
{
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
|
|
@ -511,7 +589,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
};
|
||||
|
||||
Response.Cookies.Delete(nonceKey, cookieOptions);
|
||||
return nonceExpectedValue;
|
||||
return nonce;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -30,9 +30,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// <summary>
|
||||
/// Initializes a <see cref="OpenIdConnectAuthenticationMiddleware"/>
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the ASP.NET pipeline to invoke</param>
|
||||
/// <param name="app">The ASP.NET application</param>
|
||||
/// <param name="options">Configuration options for the middleware</param>
|
||||
/// <param name="next">The next middleware in the ASP.NET pipeline to invoke.</param>
|
||||
/// <param name="dataProtectionProvider"> provider for creating a data protector.</param>
|
||||
/// <param name="loggerFactory">factory for creating a <see cref="ILogger"/>.</param>
|
||||
/// <param name="options">a <see cref="IOptions{OpenIdConnectAuthenticationOptions}"/> instance that will supply <see cref="OpenIdConnectAuthenticationOptions"/>
|
||||
/// if configureOptions is null.</param>
|
||||
/// <param name="configureOptions">a <see cref="ConfigureOptions{OpenIdConnectAuthenticationOptions}"/> instance that will be passed to an instance of <see cref="OpenIdConnectAuthenticationOptions"/>
|
||||
/// that is retrieved by calling <see cref="IOptions{OpenIdConnectAuthenticationOptions}.GetNamedOptions(string)"/> where string == <see cref="ConfigureOptions{OpenIdConnectAuthenticationOptions}.Name"/> provides runtime configuration.</param>
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
|
||||
public OpenIdConnectAuthenticationMiddleware(
|
||||
[NotNull] RequestDelegate next,
|
||||
|
|
@ -40,21 +44,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
[NotNull] ILoggerFactory loggerFactory,
|
||||
[NotNull] IOptions<ExternalAuthenticationOptions> externalOptions,
|
||||
[NotNull] IOptions<OpenIdConnectAuthenticationOptions> options,
|
||||
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions)
|
||||
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions = null)
|
||||
: base(next, options, configureOptions)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<OpenIdConnectAuthenticationMiddleware>();
|
||||
|
||||
if (string.IsNullOrEmpty(Options.SignInScheme))
|
||||
if (string.IsNullOrEmpty(Options.SignInScheme) && !string.IsNullOrEmpty(externalOptions.Options.SignInScheme))
|
||||
{
|
||||
Options.SignInScheme = externalOptions.Options.SignInScheme;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType))
|
||||
{
|
||||
Options.TokenValidationParameters.AuthenticationType = Options.AuthenticationScheme;
|
||||
}
|
||||
|
||||
if (Options.StateDataFormat == null)
|
||||
{
|
||||
var dataProtector = dataProtectionProvider.CreateProtector(
|
||||
|
|
@ -152,7 +150,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
var webRequestHandler = handler as WebRequestHandler;
|
||||
if (webRequestHandler == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
|
||||
throw new InvalidOperationException(Resources.OIDCH_0102_ExceptionValidatorHandlerMismatch);
|
||||
}
|
||||
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,32 +52,31 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationOptions.set_Caption(System.String)", Justification = "Not a LOC field")]
|
||||
public OpenIdConnectAuthenticationOptions(string authenticationScheme)
|
||||
{
|
||||
// REVIEW: why was this active by default??
|
||||
//AuthenticationMode = AuthenticationMode.Active;
|
||||
AuthenticationScheme = authenticationScheme;
|
||||
BackchannelTimeout = TimeSpan.FromMinutes(1);
|
||||
Caption = OpenIdConnectAuthenticationDefaults.Caption;
|
||||
ProtocolValidator = new OpenIdConnectProtocolValidator();
|
||||
RefreshOnIssuerKeyNotFound = true;
|
||||
ResponseMode = OpenIdConnectResponseModes.FormPost;
|
||||
ResponseType = OpenIdConnectResponseTypes.CodeIdToken;
|
||||
Scope = OpenIdConnectScopes.OpenIdProfile;
|
||||
TokenValidationParameters = new TokenValidationParameters();
|
||||
UseTokenLifetime = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expected audience for any received JWT token.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The expected audience for any received JWT token.
|
||||
/// </value>
|
||||
public string Audience { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Authority to use when making OpenIdConnect calls.
|
||||
/// </summary>
|
||||
public string Authority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional constrained path on which to process the authentication callback.
|
||||
/// If not provided and RedirectUri is available, this value will be generated from RedirectUri.
|
||||
/// </summary>
|
||||
/// <remarks>If you set this value, then the <see cref="OpenIdConnectAuthenticationHandler"/> will only listen for posts at this address.
|
||||
/// If the IdentityProvider does not post to this address, you may end up in a 401 -> IdentityProvider -> Client -> 401 -> ...</remarks>
|
||||
public PathString CallbackPath { get; set; }
|
||||
|
||||
#if DNX451
|
||||
/// <summary>
|
||||
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
|
||||
|
|
@ -112,7 +111,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
{
|
||||
if (value <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("BackchannelTimeout", value, Resources.ArgsException_BackchallelLessThanZero);
|
||||
throw new ArgumentOutOfRangeException("BackchannelTimeout", value, Resources.OIDCH_0101_BackChallnelLessThanZero);
|
||||
}
|
||||
|
||||
_backchannelTimeout = value;
|
||||
|
|
@ -128,6 +127,14 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
set { Description.Caption = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An optional constrained path on which to process the authentication callback.
|
||||
/// If not provided and RedirectUri is available, this value will be generated from RedirectUri.
|
||||
/// </summary>
|
||||
/// <remarks>If you set this value, then the <see cref="OpenIdConnectAuthenticationHandler"/> will only listen for posts at this address.
|
||||
/// If the IdentityProvider does not post to this address, you may end up in a 401 -> IdentityProvider -> Client -> 401 -> ...</remarks>
|
||||
public PathString CallbackPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 'client_id'.
|
||||
/// </summary>
|
||||
|
|
@ -145,11 +152,16 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
public OpenIdConnectConfiguration Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The OpenIdConnect protocol http://openid.net/specs/openid-connect-core-1_0.html
|
||||
/// recommends adding a nonce to a request as a mitigation against replay attacks when requesting id_tokens.
|
||||
/// By default the runtime uses cookies with unique names generated from a hash of the nonce.
|
||||
/// Responsible for retrieving, caching, and refreshing the configuration from metadata.
|
||||
/// If not provided, then one will be created using the MetadataAddress and Backchannel properties.
|
||||
/// </summary>
|
||||
public INonceCache NonceCache { get; set; }
|
||||
public IConfigurationManager<OpenIdConnectConfiguration> ConfigurationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value controlling if the 'CurrentUri' should be used as the 'local redirect' post authentication
|
||||
/// if AuthenticationProperties.RedirectUri is null or empty.
|
||||
/// </summary>
|
||||
public bool DefaultToCurrentUriOnRedirect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discovery endpoint for obtaining metadata
|
||||
|
|
@ -157,24 +169,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
public string MetadataAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expected audience for any received JWT token.
|
||||
/// The OpenIdConnect protocol http://openid.net/specs/openid-connect-core-1_0.html
|
||||
/// recommends adding a nonce to a request as a mitigation against replay attacks when requesting id_tokens.
|
||||
/// By default the runtime uses cookies with unique names generated from a hash of the nonce.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The expected audience for any received JWT token.
|
||||
/// </value>
|
||||
public string Audience { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Responsible for retrieving, caching, and refreshing the configuration from metadata.
|
||||
/// If not provided, then one will be created using the MetadataAddress and Backchannel properties.
|
||||
/// </summary>
|
||||
public IConfigurationManager<OpenIdConnectConfiguration> ConfigurationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if a metadata refresh should be attempted after a SecurityTokenSignatureKeyNotFoundException. This allows for automatic
|
||||
/// recovery in the event of a signature key rollover. This is enabled by default.
|
||||
/// </summary>
|
||||
public bool RefreshOnIssuerKeyNotFound { get; set; }
|
||||
public INonceCache NonceCache { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="OpenIdConnectAuthenticationNotifications"/> to notify when processing OpenIdConnect messages.
|
||||
|
|
@ -217,11 +216,22 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "By Design")]
|
||||
public string RedirectUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if a metadata refresh should be attempted after a SecurityTokenSignatureKeyNotFoundException. This allows for automatic
|
||||
/// recovery in the event of a signature key rollover. This is enabled by default.
|
||||
/// </summary>
|
||||
public bool RefreshOnIssuerKeyNotFound { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 'resource'.
|
||||
/// </summary>
|
||||
public string Resource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 'response_mode'.
|
||||
/// </summary>
|
||||
public string ResponseMode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 'response_type'.
|
||||
/// </summary>
|
||||
|
|
@ -233,10 +243,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
public string Scope { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authentication scheme corresponding to the middleware
|
||||
/// responsible of persisting user's identity after a successful authentication.
|
||||
/// This value typically corresponds to a cookie middleware registered in the Startup class.
|
||||
/// When omitted, <see cref="ExternalAuthenticationOptions.SignInScheme"/> is used as a fallback value.
|
||||
/// Gets or sets the SignInScheme which will be used to set the <see cref="System.Security.Claims.ClaimsIdentity.AuthenticationType"/>.
|
||||
/// </summary>
|
||||
public string SignInScheme { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
{
|
||||
public static IServiceCollection ConfigureOpenIdConnectAuthentication([NotNull] this IServiceCollection services, [NotNull] Action<OpenIdConnectAuthenticationOptions> configure)
|
||||
{
|
||||
return services.ConfigureOpenIdConnectAuthentication(configure, optionsName: "");
|
||||
return ConfigureOpenIdConnectAuthentication(services, configure, null);
|
||||
}
|
||||
|
||||
public static IServiceCollection ConfigureOpenIdConnectAuthentication([NotNull] this IServiceCollection services, [NotNull] Action<OpenIdConnectAuthenticationOptions> configure, string optionsName)
|
||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
|
||||
public static IServiceCollection ConfigureOpenIdConnectAuthentication([NotNull] this IServiceCollection services, [NotNull] IConfiguration config)
|
||||
{
|
||||
return services.ConfigureOpenIdConnectAuthentication(config, optionsName: "");
|
||||
return ConfigureOpenIdConnectAuthentication(services, config, null);
|
||||
}
|
||||
|
||||
public static IServiceCollection ConfigureOpenIdConnectAuthentication([NotNull] this IServiceCollection services, [NotNull] IConfiguration config, string optionsName)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34014
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.OpenIdConnect {
|
||||
using System;
|
||||
// <auto-generated />
|
||||
namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
using System.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
|
|
@ -24,78 +15,337 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect {
|
|||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
internal class Resources
|
||||
{
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Owin.Security.OpenIdConnect.Resources", IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly);
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (object.ReferenceEquals(resourceMan, null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Authentication.OpenIdConnect.Resources", System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to BackchannelTimeout cannot be less or equal to TimeSpan.Zero..
|
||||
/// OIDCH_0101: BackchannelTimeout cannot be less or equal to TimeSpan.Zero.
|
||||
/// </summary>
|
||||
internal static string ArgsException_BackchallelLessThanZero {
|
||||
get {
|
||||
return ResourceManager.GetString("ArgsException_BackchallelLessThanZero", resourceCulture);
|
||||
}
|
||||
internal static string OIDCH_0101_BackChallnelLessThanZero
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0101_BackChallnelLessThanZero"); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to "OpenIdConnectMessage.Error was not null, indicating an error. Error: '{0}'. Error_Description (may be empty): '{1}'. Error_Uri (may be empty): '{2}'.".
|
||||
/// OIDCH0102: An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.
|
||||
/// </summary>
|
||||
internal static string Exception_OpenIdConnectMessageError {
|
||||
get {
|
||||
return ResourceManager.GetString("Exception_OpenIdConnectMessageError", resourceCulture);
|
||||
}
|
||||
internal static string OIDCH_0102_ExceptionValidatorHandlerMismatch
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0102_Exception_ValidatorHandlerMismatch"); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OIDC_20001: The query string for Logout is not a well formed URI. The runtime cannot redirect. Redirect uri: '{0}'..
|
||||
/// OIDCH_0051: The query string for Logout is not a well formed URI. The runtime cannot redirect. Redirect uri: '{0}'.
|
||||
/// </summary>
|
||||
internal static string Exception_RedirectUri_LogoutQueryString_IsNotWellFormed {
|
||||
get {
|
||||
return ResourceManager.GetString("Exception_RedirectUri_LogoutQueryString_IsNotWellFormed", resourceCulture);
|
||||
}
|
||||
internal static string OIDCH_0051_RedirectUriLogoutIsNotWellFormed
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0051_RedirectUriLogoutIsNotWellFormed"); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler..
|
||||
/// OIDCH_0026: Entering: '{0}'
|
||||
/// </summary>
|
||||
internal static string Exception_ValidatorHandlerMismatch {
|
||||
get {
|
||||
return ResourceManager.GetString("Exception_ValidatorHandlerMismatch", resourceCulture);
|
||||
}
|
||||
internal static string OIDCH_0026_ApplyResponseChallengeAsync
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0026_ApplyResponseChallengeAsync"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0027: converted 401 to 403.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0027_401_ConvertedTo_403
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0027_401_ConvertedTo_403"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0028: Response.StatusCode != 401, StatusCode: '{0}'."
|
||||
/// </summary>
|
||||
internal static string OIDCH_0028_StatusCodeNot401
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0028_StatusCodeNot401"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0029: ChallengeContext == null AND !Options.AutomaticAuthentication
|
||||
/// </summary>
|
||||
internal static string OIDCH_0029_ChallengeContextEqualsNull
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0029_ChallengeContextEqualsNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0030: using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0030_Using_Properties_RedirectUri
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0030_Using_Properties_RedirectUri"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0031: using Options.RedirectUri for 'redirect_uri': '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0031_Using_Options_RedirectUri
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0031_Using_Options_RedirectUri"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0032_UsingCurrentUriRedirectUri
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0032_UsingCurrentUriRedirectUri"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0033: ProtocolValidator.RequireNonce == true. Options.NonceCache.TryAddNonce returned false. This usually indicates the nonce is not unique or has been used. The nonce is: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0033_TryAddNonceFailed
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0033_TryAddNonceFailed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0035: redirectToIdentityProviderNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0035_RedirectToIdentityProviderNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0035_RedirectToIdentityProviderNotificationSkipped"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: {0}", (redirectUri ?? "null"))
|
||||
/// </summary>
|
||||
internal static string OIDCH_0036_UriIsNotWellFormed
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0036_UriIsNotWellFormed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0000: Entering: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0000_AuthenticateCoreAsync
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0000_AuthenticateCoreAsync"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0001: MessageReceived: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0001_MessageReceived
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0001_MessageReceived"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0001: MessageReceived: '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatOIDCH_0001_MessageReceived(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, ResourceManager.GetString("OIDCH_0001_MessageReceived"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0002: messageReceivedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0002_MessageReceivedNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0002_MessageReceivedNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0003: messageReceivedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0003_MessageReceivedNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0003_MessageReceivedNotificationSkipped"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or whitespace. State is required to process the message.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0004_MessageStateIsNullOrWhiteSpace
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0004_MessageStateIsNullOrWhiteSpace"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0005: unable to unprotect the message.State
|
||||
/// </summary>
|
||||
internal static string OIDCH_0005_MessageStateIsInvalid
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0005_MessageStateIsInvalid"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0006_MessageErrorNotNull: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0006_MessageErrorNotNull
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0006_MessageErrorNotNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0007: updating configuration
|
||||
/// </summary>
|
||||
internal static string OIDCH_0007_UpdatingConfiguration
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0007_UpdatingConfiguration"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0008: securityTokenReceivedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0009: securityTokenReceivedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0009_SecurityTokenReceivedNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0009_SecurityTokenReceivedNotificationSkipped:"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0010: Validated Security Token must be a JwtSecurityToken was: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0010_ValidatedSecurityTokenNotJwt
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0010_ValidatedSecurityTokenNotJwt"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0011: Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: {0}."
|
||||
/// </summary>
|
||||
internal static string OIDCH_0011_UnableToValidateToken
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0011_UnableToValidateToken"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0012: securityTokenValidatedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0013: securityTokenValidatedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0013_SecurityTokenValidatedNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0013_SecurityTokenValidatedNotificationSkipped"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0014: 'code' received: '{0}'
|
||||
/// </summary>
|
||||
internal static string OIDCH_0014_CodeReceived
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0014_CodeReceived"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0015: codeReceivedNotification.HandledResponse")
|
||||
/// </summary>
|
||||
internal static string OIDCH_0015_CodeReceivedNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0015_CodeReceivedNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0016: codeReceivedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0016_CodeReceivedNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0016_CodeReceivedNotificationSkipped"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0017: Exception occurred while processing message
|
||||
/// </summary>
|
||||
internal static string OIDCH_0017_ExceptionOccurredWhileProcessingMessage
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0017_ExceptionOccurredWhileProcessingMessage"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0018: authenticationFailedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0018_AuthenticationFailedNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0018_AuthenticationFailedNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0019: authenticationFailedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0019_AuthenticationFailedNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0019_AuthenticationFailedNotificationSkipped"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0020: 'id_token' received: '{0}'
|
||||
/// </summary>
|
||||
internal static string OIDCH_0020_IdTokenReceived
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0020_IdTokenReceived"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,16 +117,109 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ArgsException_BackchallelLessThanZero" xml:space="preserve">
|
||||
<value>BackchannelTimeout cannot be less or equal to TimeSpan.Zero.</value>
|
||||
<data name="OIDCH_0101_BackChallnelLessThanZero" xml:space="preserve">
|
||||
<value>OIDCH_0101: BackchannelTimeout cannot be less or equal to TimeSpan.Zero.</value>
|
||||
</data>
|
||||
<data name="Exception_OpenIdConnectMessageError" xml:space="preserve">
|
||||
<value>"OpenIdConnectMessage.Error was not null, indicating an error. Error: '{0}'. Error_Description (may be empty): '{1}'. Error_Uri (may be empty): '{2}'."</value>
|
||||
<data name="OIDCH_0102_Exception_ValidatorHandlerMismatch" xml:space="preserve">
|
||||
<value>OIDCH_0102: An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.</value>
|
||||
</data>
|
||||
<data name="Exception_RedirectUri_LogoutQueryString_IsNotWellFormed" xml:space="preserve">
|
||||
<value>OIDC_20001: The query string for Logout is not a well formed URI. The runtime cannot redirect. Redirect uri: '{0}'.</value>
|
||||
<data name="OIDCH_0051_RedirectUriLogoutIsNotWellFormed" xml:space="preserve">
|
||||
<value>OIDC_0051: The query string for Logout is not a well formed URI. The runtime cannot redirect. Redirect uri: '{0}'.</value>
|
||||
</data>
|
||||
<data name="Exception_ValidatorHandlerMismatch" xml:space="preserve">
|
||||
<value>An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.</value>
|
||||
<data name="OIDCH_0026_ApplyResponseChallengeAsync" xml:space="preserve">
|
||||
<value>OIDCH_0026: Entering: '{0}'</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="OIDCH_0027_401_ConvertedTo_403" xml:space="preserve">
|
||||
<value>OIDCH_0027: converted 401 to 403.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0028_StatusCodeNot401" xml:space="preserve">
|
||||
<value>OIDCH_0028: Response.StatusCode != 401, StatusCode: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0029_ChallengContextEqualsNull" xml:space="preserve">
|
||||
<value>OIDCH_0029: ChallengeContext == null AND !Options.AutomaticAuthentication</value>
|
||||
</data>
|
||||
<data name="OIDCH_0030_Using_Properties_RedirectUri" xml:space="preserve">
|
||||
<value>OIDCH_0030: using properties.RedirectUri for 'local redirect' post authentication: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0031_Using_Options_RedirectUri" xml:space="preserve">
|
||||
<value>OIDCH_0031: using Options.RedirectUri for 'redirect_uri': '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0032_UsingCurrentUriRedirectUri" xml:space="preserve">
|
||||
<value>OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0033_TryAddNonceFailed" xml:space="preserve">
|
||||
<value>OIDCH_0033: ProtocolValidator.RequireNonce == true. Options.NonceCache.TryAddNonce returned false. This usually indicates the nonce is not unique or has been used. The nonce is: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0035_RedirectToIdentityProviderNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0035: redirectToIdentityProviderNotification.Skipped</value>
|
||||
</data>
|
||||
<data name="OIDCH_0036_UriIsNotWellFormed" xml:space="preserve">
|
||||
<value>OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: {0}", (redirectUri ?? "null"))</value>
|
||||
</data>
|
||||
<data name="OIDCH_0000_AuthenticateCoreAsync" xml:space="preserve">
|
||||
<value>OIDCH_0000: Entering: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0001_MessageReceived" xml:space="preserve">
|
||||
<value>OIDCH_0001: MessageReceived: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0002_MessageReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0002: messageReceivedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0003_MessageReceivedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0003: messageReceivedNotification.Skipped</value>
|
||||
</data>
|
||||
<data name="OIDCH_0004_MessageStateIsNullOrWhiteSpace" xml:space="preserve">
|
||||
<value>OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or whitespace. State is required to process the message.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0005_MessageStateIsInValid" xml:space="preserve">
|
||||
<value>OIDCH_0005: unable to unprotect the message.State</value>
|
||||
</data>
|
||||
<data name="OIDCH_0006_MessageErrorNotNull" xml:space="preserve">
|
||||
<value>OIDCH_0006_MessageErrorNotNull: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0007_UpdatingConfiguration" xml:space="preserve">
|
||||
<value>OIDCH_0007: updating configuration</value>
|
||||
</data>
|
||||
<data name="OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0008: securityTokenReceivedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0009_SecurityTokenReceivedNotificationSkipped:" xml:space="preserve">
|
||||
<value>OIDCH_0009: securityTokenReceivedNotification.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_SecurityTokenValidatedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0012: securityTokenValidatedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0013_SecurityTokenValidatedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0013: securityTokenValidatedNotification.Skipped</value>
|
||||
</data>
|
||||
<data name="OIDCH_0014_CodeReceived" xml:space="preserve">
|
||||
<value>OIDCH_0014: 'code' received: '{0}'</value>
|
||||
</data>
|
||||
<data name="OIDCH_0015_CodeReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0015: codeReceivedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0016_CodeReceivedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0016: codeReceivedNotification.Skipped</value>
|
||||
</data>
|
||||
<data name="OIDCH_0017: Exception occurred while processing message" xml:space="preserve">
|
||||
<value>OIDCH_0017: Exception occurred while processing message</value>
|
||||
</data>
|
||||
<data name="OIDCH_0018_AuthenticationFailedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0018: authenticationFailedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0019_AuthenticationNotificationFailedSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0019: authenticationFailedNotification.Skipped</value>
|
||||
</data>
|
||||
<data name="OIDCH_0020_IdTokenReceived" xml:space="preserve">
|
||||
<value>OIDCH_0020: 'id_token' received: '{0}'</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,768 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// this controls if the logs are written to the console.
|
||||
// they can be reviewed for general content.
|
||||
//#define _Verbose
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// These tests are designed to test OpenIdConnectAuthenticationHandler.
|
||||
/// </summary>
|
||||
public class OpenIdConnectHandlerTests
|
||||
{
|
||||
static List<LogEntry> CompleteLogEntries;
|
||||
static Dictionary<string, LogLevel> LogEntries;
|
||||
|
||||
static OpenIdConnectHandlerTests()
|
||||
{
|
||||
LogEntries =
|
||||
new Dictionary<string, LogLevel>()
|
||||
{
|
||||
{ "OIDCH_0000:", LogLevel.Debug },
|
||||
{ "OIDCH_0001:", LogLevel.Debug },
|
||||
{ "OIDCH_0002:", LogLevel.Information },
|
||||
{ "OIDCH_0003:", LogLevel.Information },
|
||||
{ "OIDCH_0004:", LogLevel.Error },
|
||||
{ "OIDCH_0005:", LogLevel.Error },
|
||||
{ "OIDCH_0006:", LogLevel.Error },
|
||||
{ "OIDCH_0007:", LogLevel.Error },
|
||||
{ "OIDCH_0008:", LogLevel.Debug },
|
||||
{ "OIDCH_0009:", LogLevel.Debug },
|
||||
{ "OIDCH_0010:", LogLevel.Error },
|
||||
{ "OIDCH_0011:", LogLevel.Error },
|
||||
{ "OIDCH_0012:", LogLevel.Debug },
|
||||
{ "OIDCH_0013:", LogLevel.Debug },
|
||||
{ "OIDCH_0014:", LogLevel.Debug },
|
||||
{ "OIDCH_0015:", LogLevel.Debug },
|
||||
{ "OIDCH_0016:", LogLevel.Debug },
|
||||
{ "OIDCH_0017:", LogLevel.Error },
|
||||
{ "OIDCH_0018:", LogLevel.Debug },
|
||||
{ "OIDCH_0019:", LogLevel.Debug },
|
||||
{ "OIDCH_0020:", LogLevel.Debug },
|
||||
{ "OIDCH_0026:", LogLevel.Error },
|
||||
};
|
||||
|
||||
BuildLogEntryList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the complete list of log entries that are available in the runtime.
|
||||
/// </summary>
|
||||
private static void BuildLogEntryList()
|
||||
{
|
||||
CompleteLogEntries = new List<LogEntry>();
|
||||
foreach (var entry in LogEntries)
|
||||
{
|
||||
CompleteLogEntries.Add(new LogEntry { State = entry.Key, Level = entry.Value });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanity check that logging is filtering, hi / low water marks are checked
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggingLevel()
|
||||
{
|
||||
var logger = new CustomLogger(LogLevel.Debug);
|
||||
logger.IsEnabled(LogLevel.Critical).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Error).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Information).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Verbose).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Warning).ShouldBe<bool>(true);
|
||||
|
||||
logger = new CustomLogger(LogLevel.Critical);
|
||||
logger.IsEnabled(LogLevel.Critical).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(false);
|
||||
logger.IsEnabled(LogLevel.Error).ShouldBe<bool>(false);
|
||||
logger.IsEnabled(LogLevel.Information).ShouldBe<bool>(false);
|
||||
logger.IsEnabled(LogLevel.Verbose).ShouldBe<bool>(false);
|
||||
logger.IsEnabled(LogLevel.Warning).ShouldBe<bool>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test <see cref="OpenIdConnectAuthenticationHandler.AuthenticateCoreAsync"/> produces expected logs.
|
||||
/// Each call to 'RunVariation' is configured with an <see cref="OpenIdConnectAuthenticationOptions"/> and <see cref="OpenIdConnectMessage"/>.
|
||||
/// The list of expected log entries is checked and any errors reported.
|
||||
/// <see cref="CustomLoggerFactory"/> captures the logs so they can be prepared.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Fact]
|
||||
public async Task AuthenticateCore()
|
||||
{
|
||||
//System.Diagnostics.Debugger.Launch();
|
||||
|
||||
var propertiesFormatter = new AuthenticationPropertiesFormater();
|
||||
var protectedProperties = propertiesFormatter.Protect(new AuthenticationProperties());
|
||||
var state = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + Uri.EscapeDataString(protectedProperties);
|
||||
var code = Guid.NewGuid().ToString();
|
||||
var message =
|
||||
new OpenIdConnectMessage
|
||||
{
|
||||
Code = code,
|
||||
State = state,
|
||||
};
|
||||
|
||||
var errors = new Dictionary<string, List<Tuple<LogEntry, LogEntry>>>();
|
||||
|
||||
var logsEntriesExpected = new int[] { 0, 1, 7, 14, 15 };
|
||||
await RunVariation(LogLevel.Debug, message, CodeReceivedHandledOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[] { 0, 1, 7, 14, 16 };
|
||||
await RunVariation(LogLevel.Debug, message, CodeReceivedSkippedOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[] { 0, 1, 7, 14 };
|
||||
await RunVariation(LogLevel.Debug, message, DefaultOptions, errors, logsEntriesExpected);
|
||||
|
||||
// each message below should return before processing the idtoken
|
||||
message.IdToken = "invalid_token";
|
||||
|
||||
logsEntriesExpected = new int[] { 0, 1, 2 };
|
||||
await RunVariation(LogLevel.Debug, message, MessageReceivedHandledOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[]{ 2 };
|
||||
await RunVariation(LogLevel.Information, message, MessageReceivedHandledOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[] { 0, 1, 3 };
|
||||
await RunVariation(LogLevel.Debug, message, MessageReceivedSkippedOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[] { 3 };
|
||||
await RunVariation(LogLevel.Information, message, MessageReceivedSkippedOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[] {0, 1, 7, 20, 8 };
|
||||
await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedHandledOptions, errors, logsEntriesExpected);
|
||||
|
||||
logsEntriesExpected = new int[] {0, 1, 7, 20, 9 };
|
||||
await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedSkippedOptions, errors, logsEntriesExpected);
|
||||
|
||||
#if _Verbose
|
||||
Console.WriteLine("\n ===== \n");
|
||||
DisplayErrors(errors);
|
||||
#endif
|
||||
errors.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that <see cref="OpenIdConnectAuthenticationHandler"/> processes a messaage as expected.
|
||||
/// The test runs two independant paths: Using <see cref="ConfigureOptions{TOptions}"/> and <see cref="IOptions{TOptions}"/>
|
||||
/// </summary>
|
||||
/// <param name="logLevel"><see cref="LogLevel"/> for this variation</param>
|
||||
/// <param name="message">the <see cref="OpenIdConnectMessage"/> that has arrived</param>
|
||||
/// <param name="action">the <see cref="OpenIdConnectAuthenticationOptions"/> delegate used for setting the options.</param>
|
||||
/// <param name="errors">container for propogation of errors.</param>
|
||||
/// <param name="logsEntriesExpected">the expected log entries</param>
|
||||
/// <returns>a Task</returns>
|
||||
private async Task RunVariation(LogLevel logLevel, OpenIdConnectMessage message, Action<OpenIdConnectAuthenticationOptions> action, Dictionary<string, List<Tuple<LogEntry, LogEntry>>> errors, int[] logsEntriesExpected)
|
||||
{
|
||||
var expectedLogs = PopulateLogEntries(logsEntriesExpected);
|
||||
string variation = action.Method.ToString().Substring(5, action.Method.ToString().IndexOf('(') - 5);
|
||||
#if _Verbose
|
||||
Console.WriteLine(Environment.NewLine + "=====" + Environment.NewLine + "Variation: " + variation + ", LogLevel: " + logLevel.ToString() + Environment.NewLine + Environment.NewLine + "Expected Logs: ");
|
||||
DisplayLogs(expectedLogs);
|
||||
Console.WriteLine(Environment.NewLine + "Logs using ConfigureOptions:");
|
||||
#endif
|
||||
var form = new FormUrlEncodedContent(message.Parameters);
|
||||
var loggerFactory = new CustomLoggerFactory(logLevel);
|
||||
var server = CreateServer(new CustomConfigureOptions(action), loggerFactory);
|
||||
await server.CreateClient().PostAsync("http://localhost", form);
|
||||
CheckLogs(variation + ":ConfigOptions", loggerFactory.Logger.Logs, expectedLogs, errors);
|
||||
|
||||
#if _Verbose
|
||||
Console.WriteLine(Environment.NewLine + "Logs using IOptions:");
|
||||
#endif
|
||||
form = new FormUrlEncodedContent(message.Parameters);
|
||||
loggerFactory = new CustomLoggerFactory(logLevel);
|
||||
server = CreateServer(new Options(action), loggerFactory);
|
||||
await server.CreateClient().PostAsync("http://localhost", form);
|
||||
CheckLogs(variation + ":IOptions", loggerFactory.Logger.Logs, expectedLogs, errors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates a list of expected log entries for a test variation.
|
||||
/// </summary>
|
||||
/// <param name="items">the index for the <see cref="LogEntry"/> in CompleteLogEntries of interest.</param>
|
||||
/// <returns>a <see cref="List{LogEntry}"/> that represents the expected entries for a test variation.</returns>
|
||||
private List<LogEntry> PopulateLogEntries(int[] items)
|
||||
{
|
||||
var entries = new List<LogEntry>();
|
||||
foreach(var item in items)
|
||||
{
|
||||
entries.Add(CompleteLogEntries[item]);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private void DisplayLogs(List<LogEntry> logs)
|
||||
{
|
||||
foreach (var logentry in logs)
|
||||
{
|
||||
Console.WriteLine(logentry.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayErrors(Dictionary<string, List<Tuple<LogEntry, LogEntry>>> errors)
|
||||
{
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Console.WriteLine("Error in Variation: " + error.Key);
|
||||
foreach (var logError in error.Value)
|
||||
{
|
||||
Console.WriteLine("*Captured*, *Expected* : *" + (logError.Item1?.ToString() ?? "null") + "*, *" + (logError.Item2?.ToString() ?? "null") + "*");
|
||||
}
|
||||
Console.WriteLine(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds to errors if a variation if any are found.
|
||||
/// </summary>
|
||||
/// <param name="variation">if this has been seen before, errors will be appended, test results are easier to understand if this is unique.</param>
|
||||
/// <param name="capturedLogs">these are the logs the runtime generated</param>
|
||||
/// <param name="expectedLogs">these are the errors that were expected</param>
|
||||
/// <param name="errors">the dictionary to record any errors</param>
|
||||
private void CheckLogs(string variation, List<LogEntry> capturedLogs, List<LogEntry> expectedLogs, Dictionary<string, List<Tuple<LogEntry, LogEntry>>> errors)
|
||||
{
|
||||
var localErrors = new List<Tuple<LogEntry, LogEntry>>();
|
||||
|
||||
if (capturedLogs.Count >= expectedLogs.Count)
|
||||
{
|
||||
for (int i = 0; i < capturedLogs.Count; i++)
|
||||
{
|
||||
if (i + 1 > expectedLogs.Count)
|
||||
{
|
||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], null));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TestUtilities.AreEqual<LogEntry>(capturedLogs[i], expectedLogs[i]))
|
||||
{
|
||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], expectedLogs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < expectedLogs.Count; i++)
|
||||
{
|
||||
if (i + 1 > capturedLogs.Count)
|
||||
{
|
||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(null, expectedLogs[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TestUtilities.AreEqual<LogEntry>(expectedLogs[i], capturedLogs[i]))
|
||||
{
|
||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], expectedLogs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localErrors.Count != 0)
|
||||
{
|
||||
if (errors.ContainsKey(variation))
|
||||
{
|
||||
foreach (var error in localErrors)
|
||||
{
|
||||
errors[variation].Add(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errors[variation] = localErrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Configure Options
|
||||
|
||||
private static void CodeReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
AuthorizationCodeReceived = (notification) =>
|
||||
{
|
||||
notification.HandleResponse();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void CodeReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
AuthorizationCodeReceived = (notification) =>
|
||||
{
|
||||
notification.SkipToNextMiddleware();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void DefaultOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
options.AuthenticationScheme = "OpenIdConnectHandlerTest";
|
||||
options.ConfigurationManager = ConfigurationManager.DefaultStaticConfigurationManager;
|
||||
options.StateDataFormat = new AuthenticationPropertiesFormater();
|
||||
}
|
||||
|
||||
private static void MessageReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
MessageReceived = (notification) =>
|
||||
{
|
||||
notification.HandleResponse();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void MessageReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
MessageReceived = (notification) =>
|
||||
{
|
||||
notification.SkipToNextMiddleware();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void SecurityTokenReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
SecurityTokenReceived = (notification) =>
|
||||
{
|
||||
notification.HandleResponse();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void SecurityTokenReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
SecurityTokenReceived = (notification) =>
|
||||
{
|
||||
notification.SkipToNextMiddleware();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void SecurityTokenValidatedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
SecurityTokenValidated = (notification) =>
|
||||
{
|
||||
notification.HandleResponse();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void SecurityTokenValidatedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
SecurityTokenValidated = (notification) =>
|
||||
{
|
||||
notification.SkipToNextMiddleware();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static TestServer CreateServer(IOptions<OpenIdConnectAuthenticationOptions> options, ILoggerFactory loggerFactory)
|
||||
{
|
||||
return TestServer.Create(
|
||||
app =>
|
||||
{
|
||||
app.UseCustomOpenIdConnectAuthentication(options, loggerFactory);
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
await next();
|
||||
});
|
||||
},
|
||||
services =>
|
||||
{
|
||||
services.AddDataProtection();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(CustomConfigureOptions configureOptions, ILoggerFactory loggerFactory)
|
||||
{
|
||||
return TestServer.Create(
|
||||
app =>
|
||||
{
|
||||
app.UseCustomOpenIdConnectAuthentication(configureOptions, loggerFactory);
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
await next();
|
||||
});
|
||||
},
|
||||
services =>
|
||||
{
|
||||
services.AddDataProtection();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension specifies <see cref="CustomOpenIdConnectAuthenticationMiddleware"/> as the middleware.
|
||||
/// </summary>
|
||||
public static class OpenIdConnectAuthenticationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="OpenIdConnectAuthenticationMiddleware"/> into the ASP.NET runtime.
|
||||
/// </summary>
|
||||
/// <param name="app">The application builder</param>
|
||||
/// <param name="customConfigureOption">Options which control the processing of the OpenIdConnect protocol and token validation.</param>
|
||||
/// <param name="loggerFactory">custom loggerFactory</param>
|
||||
/// <returns>The application builder</returns>
|
||||
public static IApplicationBuilder UseCustomOpenIdConnectAuthentication(this IApplicationBuilder app, CustomConfigureOptions customConfigureOption, ILoggerFactory loggerFactory)
|
||||
{
|
||||
return app.UseMiddleware<CustomOpenIdConnectAuthenticationMiddleware>(customConfigureOption, loggerFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="OpenIdConnectAuthenticationMiddleware"/> into the ASP.NET runtime.
|
||||
/// </summary>
|
||||
/// <param name="app">The application builder</param>
|
||||
/// <param name="options">Options which control the processing of the OpenIdConnect protocol and token validation.</param>
|
||||
/// <param name="loggerFactory">custom loggerFactory</param>
|
||||
/// <returns>The application builder</returns>
|
||||
public static IApplicationBuilder UseCustomOpenIdConnectAuthentication(this IApplicationBuilder app, IOptions<OpenIdConnectAuthenticationOptions> options, ILoggerFactory loggerFactory)
|
||||
{
|
||||
return app.UseMiddleware<CustomOpenIdConnectAuthenticationMiddleware>(options, loggerFactory);
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenIdConnectAuthenticationContext : IAuthenticateContext
|
||||
{
|
||||
public OpenIdConnectAuthenticationContext(string scheme = null)
|
||||
{
|
||||
AuthenticationScheme = scheme ?? OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
|
||||
}
|
||||
|
||||
public string AuthenticationScheme
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void Authenticated(ClaimsPrincipal principal, IDictionary<string, string> properties, IDictionary<string, object> description)
|
||||
{
|
||||
}
|
||||
|
||||
public void NotAuthenticated()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a Facade over IOptions
|
||||
/// </summary>
|
||||
public class Options : IOptions<OpenIdConnectAuthenticationOptions>
|
||||
{
|
||||
OpenIdConnectAuthenticationOptions _options;
|
||||
|
||||
public Options(Action<OpenIdConnectAuthenticationOptions> action)
|
||||
{
|
||||
_options = new OpenIdConnectAuthenticationOptions();
|
||||
action(_options);
|
||||
}
|
||||
|
||||
OpenIdConnectAuthenticationOptions IOptions<OpenIdConnectAuthenticationOptions>.Options
|
||||
{
|
||||
get
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For now returns _options
|
||||
/// </summary>
|
||||
/// <param name="name">configuration to return</param>
|
||||
/// <returns></returns>
|
||||
public OpenIdConnectAuthenticationOptions GetNamedOptions(string name)
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomConfigureOptions : ConfigureOptions<OpenIdConnectAuthenticationOptions>
|
||||
{
|
||||
public CustomConfigureOptions(Action<OpenIdConnectAuthenticationOptions> action)
|
||||
: base(action)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Configure(OpenIdConnectAuthenticationOptions options, string name = "")
|
||||
{
|
||||
base.Configure(options, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to control which methods are handled
|
||||
/// </summary>
|
||||
public class CustomOpenIdConnectAuthenticationHandler : OpenIdConnectAuthenticationHandler
|
||||
{
|
||||
public CustomOpenIdConnectAuthenticationHandler(ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task BaseInitializeAsyncPublic(AuthenticationOptions options, HttpContext context)
|
||||
{
|
||||
await base.BaseInitializeAsync(options, context);
|
||||
}
|
||||
|
||||
public override bool ShouldHandleScheme(string authenticationScheme)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Challenge(IChallengeContext context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to set <see cref="CustomOpenIdConnectAuthenticationHandler"/> as the AuthenticationHandler
|
||||
/// which can be configured to handle certain messages.
|
||||
/// </summary>
|
||||
public class CustomOpenIdConnectAuthenticationMiddleware : OpenIdConnectAuthenticationMiddleware
|
||||
{
|
||||
public CustomOpenIdConnectAuthenticationMiddleware(
|
||||
RequestDelegate next,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<ExternalAuthenticationOptions> externalOptions,
|
||||
IOptions<OpenIdConnectAuthenticationOptions> options,
|
||||
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions = null
|
||||
)
|
||||
: base(next, dataProtectionProvider, loggerFactory, externalOptions, options, configureOptions)
|
||||
{
|
||||
Logger = (loggerFactory as CustomLoggerFactory).Logger;
|
||||
}
|
||||
|
||||
protected override AuthenticationHandler<OpenIdConnectAuthenticationOptions> CreateHandler()
|
||||
{
|
||||
return new CustomOpenIdConnectAuthenticationHandler(Logger);
|
||||
}
|
||||
|
||||
public ILogger Logger
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
public class LogEntry
|
||||
{
|
||||
public LogEntry() { }
|
||||
|
||||
public int EventId { get; set; }
|
||||
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
public Func<object, Exception, string> Formatter { get; set; }
|
||||
|
||||
public LogLevel Level { get; set; }
|
||||
|
||||
public object State { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Formatter != null)
|
||||
{
|
||||
return Formatter(this.State, this.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
string message = (Formatter != null ? Formatter(State, Exception) : (State?.ToString() ?? "null"));
|
||||
message += ", LogLevel: " + Level.ToString();
|
||||
message += ", EventId: " + EventId.ToString();
|
||||
message += ", Exception: " + (Exception == null ? "null" : Exception.Message);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomLogger : ILogger, IDisposable
|
||||
{
|
||||
LogLevel _logLevel = 0;
|
||||
|
||||
public CustomLogger(LogLevel logLevel = LogLevel.Debug)
|
||||
{
|
||||
_logLevel = logLevel;
|
||||
}
|
||||
|
||||
List<LogEntry> logEntries = new List<LogEntry>();
|
||||
|
||||
public IDisposable BeginScopeImpl(object state)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return (logLevel >= _logLevel);
|
||||
}
|
||||
|
||||
public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
|
||||
{
|
||||
if (IsEnabled(logLevel))
|
||||
{
|
||||
logEntries.Add(
|
||||
new LogEntry
|
||||
{
|
||||
EventId = eventId,
|
||||
Exception = exception,
|
||||
Formatter = formatter,
|
||||
Level = logLevel,
|
||||
State = state,
|
||||
});
|
||||
|
||||
#if _Verbose
|
||||
Console.WriteLine(state?.ToString() ?? "state null");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public List<LogEntry> Logs { get { return logEntries; } }
|
||||
}
|
||||
|
||||
public class CustomLoggerFactory : ILoggerFactory
|
||||
{
|
||||
CustomLogger _logger;
|
||||
LogLevel _logLevel = LogLevel.Debug;
|
||||
|
||||
public CustomLoggerFactory(LogLevel logLevel)
|
||||
{
|
||||
_logLevel = logLevel;
|
||||
_logger = new CustomLogger(_logLevel);
|
||||
}
|
||||
|
||||
public LogLevel MinimumLevel
|
||||
{
|
||||
get { return _logLevel; }
|
||||
set {_logLevel = value; }
|
||||
}
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return _logger;
|
||||
}
|
||||
|
||||
public CustomLogger Logger { get { return _logger; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processing a <see cref="OpenIdConnectMessage"/> requires 'unprotecting' the state.
|
||||
/// This class side-steps that process.
|
||||
/// </summary>
|
||||
public class AuthenticationPropertiesFormater : ISecureDataFormat<AuthenticationProperties>
|
||||
{
|
||||
public string Protect(AuthenticationProperties data)
|
||||
{
|
||||
return "protectedData";
|
||||
}
|
||||
|
||||
AuthenticationProperties ISecureDataFormat<AuthenticationProperties>.Unprotect(string protectedText)
|
||||
{
|
||||
return new AuthenticationProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to set up different configurations of metadata for different tests
|
||||
/// </summary>
|
||||
public class ConfigurationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple static empty manager.
|
||||
/// </summary>
|
||||
static public IConfigurationManager<OpenIdConnectConfiguration> DefaultStaticConfigurationManager
|
||||
{
|
||||
get { return new StaticConfigurationManager<OpenIdConnectConfiguration>(new OpenIdConnectConfiguration()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// These utilities are designed to test openidconnect related flows
|
||||
/// </summary>
|
||||
public class TestUtilities
|
||||
{
|
||||
public static bool AreEqual<T>(object obj1, object obj2, Func<object, object, bool> comparer = null) where T : class
|
||||
{
|
||||
if (obj1 == null && obj2 == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj1 == null || obj2 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj1.GetType() != obj2.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj1.GetType() != typeof(T))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (comparer != null)
|
||||
{
|
||||
return comparer(obj1, obj2);
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(LogEntry))
|
||||
{
|
||||
return AreEqual(obj1 as LogEntry, obj2 as LogEntry);
|
||||
}
|
||||
else if (typeof(T) == typeof(Exception))
|
||||
{
|
||||
return AreEqual(obj1 as Exception, obj2 as Exception);
|
||||
}
|
||||
|
||||
throw new ArithmeticException("Unknown type, no comparer. Type: " + typeof(T).ToString());
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never call this method directly, call AreObjectsEqual, as it deals with nulls and types"/>
|
||||
/// </summary>
|
||||
/// <param name="logEntry1"></param>
|
||||
/// <param name="logEntry2"></param>
|
||||
/// <returns></returns>
|
||||
private static bool AreEqual(LogEntry logEntry1, LogEntry logEntry2)
|
||||
{
|
||||
if (logEntry1.EventId != logEntry2.EventId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AreEqual<Exception>(logEntry1.Exception, logEntry2.Exception))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logEntry1.State == null && logEntry2.State == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (logEntry1.State == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logEntry2.State == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string logValue1 = logEntry1.Formatter == null ? logEntry1.State.ToString() : logEntry1.Formatter(logEntry1.State, logEntry1.Exception);
|
||||
string logValue2 = logEntry2.Formatter == null ? logEntry2.State.ToString() : logEntry2.Formatter(logEntry2.State, logEntry2.Exception);
|
||||
|
||||
return (logValue1.StartsWith(logValue2) || (logValue2.StartsWith(logValue1)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never call this method directly, call AreObjectsEqual, as it deals with nulls and types"/>
|
||||
/// </summary>
|
||||
/// <param name="exception1"></param>
|
||||
/// <param name="exception2"></param>
|
||||
/// <returns></returns>
|
||||
private static bool AreEqual(Exception exception1, Exception exception2)
|
||||
{
|
||||
if (!string.Equals(exception1.Message, exception2.Message))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AreEqual<Exception>(exception1.InnerException, exception2.InnerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue