This commit is contained in:
parent
9ce84d39c2
commit
501bd4ff10
|
|
@ -5,8 +5,8 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
{
|
{
|
||||||
public interface INonceCache
|
public interface INonceCache
|
||||||
{
|
{
|
||||||
string AddNonce(string nonce);
|
bool TryAddNonce(string nonce);
|
||||||
|
|
||||||
bool TryRemoveNonce(string nonce);
|
bool TryRemoveNonce(string nonce);
|
||||||
bool HasNonce(string nonce);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The prefix used to for the a nonce in the cookie
|
/// The prefix used to for the a nonce in the cookie
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
public const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The property for the RedirectUri that was used when asking for a 'authorizationCode'
|
/// The property for the RedirectUri that was used when asking for a 'authorizationCode'
|
||||||
|
|
@ -36,6 +36,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constant used to identify state in openIdConnect protocal message
|
/// Constant used to identify state in openIdConnect protocal message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,16 @@ namespace Microsoft.AspNet.Builder
|
||||||
Name = optionsName
|
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:
|
// Set End_Session_Endpoint in order:
|
||||||
// 1. properties.Redirect
|
// 1. properties.Redirect
|
||||||
// 2. Options.Wreply
|
// 2. Options.PostLogoutRedirectUri
|
||||||
var properties = new AuthenticationProperties(signout.Properties);
|
var properties = new AuthenticationProperties(signout.Properties);
|
||||||
if (!string.IsNullOrEmpty(properties.RedirectUri))
|
if (!string.IsNullOrEmpty(properties.RedirectUri))
|
||||||
{
|
{
|
||||||
|
|
@ -98,7 +98,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
string redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
|
string redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
|
||||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
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);
|
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.
|
/// Responds to a 401 Challenge. Sends an OpenIdConnect message to the 'identity authority' to obtain an identity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
/// <remarks>Uses log id's OIDCH-0026 - OIDCH-0050, next num: 37</remarks>
|
||||||
protected override async Task ApplyResponseChallengeAsync()
|
protected override async Task ApplyResponseChallengeAsync()
|
||||||
{
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
_logger.LogDebug(Resources.OIDCH_0026_ApplyResponseChallengeAsync, this.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
if (ShouldConvertChallengeToForbidden())
|
if (ShouldConvertChallengeToForbidden())
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug(Resources.OIDCH_0027_401_ConvertedTo_403);
|
||||||
Response.StatusCode = 403;
|
Response.StatusCode = 403;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Response.StatusCode != 401)
|
if (Response.StatusCode != 401)
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug(Resources.OIDCH_0028_StatusCodeNot401, Response.StatusCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When Automatic should redirect on 401 even if there wasn't an explicit challenge.
|
// When Automatic should redirect on 401 even if there wasn't an explicit challenge.
|
||||||
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug(Resources.OIDCH_0029_ChallengeContextEqualsNull);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// order for redirect_uri
|
// order for local RedirectUri
|
||||||
// 1. challenge.Properties.RedirectUri
|
// 1. challenge.Properties.RedirectUri
|
||||||
// 2. CurrentUri
|
// 2. CurrentUri if Options.DefaultToCurrentUriOnRedirect is true)
|
||||||
AuthenticationProperties properties;
|
AuthenticationProperties properties;
|
||||||
if (ChallengeContext == null)
|
if (ChallengeContext == null)
|
||||||
{
|
{
|
||||||
|
|
@ -147,12 +156,22 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
properties = new AuthenticationProperties(ChallengeContext.Properties);
|
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;
|
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))
|
if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
|
||||||
{
|
{
|
||||||
properties.Dictionary.Add(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey, 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);
|
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenIdConnectMessage openIdConnectMessage = new OpenIdConnectMessage
|
var message = new OpenIdConnectMessage
|
||||||
{
|
{
|
||||||
ClientId = Options.ClientId,
|
ClientId = Options.ClientId,
|
||||||
IssuerAddress = _configuration == null ? string.Empty : (_configuration.AuthorizationEndpoint ?? string.Empty),
|
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
|
||||||
RedirectUri = Options.RedirectUri,
|
RedirectUri = Options.RedirectUri,
|
||||||
|
// [brentschmaltz] - this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
|
||||||
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
|
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
|
||||||
Resource = Options.Resource,
|
Resource = Options.Resource,
|
||||||
ResponseMode = OpenIdConnectResponseModes.FormPost,
|
ResponseMode = Options.ResponseMode,
|
||||||
ResponseType = Options.ResponseType,
|
ResponseType = Options.ResponseType,
|
||||||
Scope = Options.Scope,
|
Scope = Options.Scope,
|
||||||
State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(properties))
|
State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(properties))
|
||||||
|
|
@ -178,33 +198,45 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
|
|
||||||
if (Options.ProtocolValidator.RequireNonce)
|
if (Options.ProtocolValidator.RequireNonce)
|
||||||
{
|
{
|
||||||
openIdConnectMessage.Nonce = Options.ProtocolValidator.GenerateNonce();
|
message.Nonce = Options.ProtocolValidator.GenerateNonce();
|
||||||
if (Options.NonceCache != null)
|
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
|
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);
|
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||||
if (!notification.HandledResponse)
|
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
string redirectUri = notification.ProtocolMessage.CreateAuthenticationRequestUrl();
|
_logger.LogInformation(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
return;
|
||||||
{
|
|
||||||
_logger.LogWarning("Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: {0}", (redirectUri ?? "null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Response.Redirect(redirectUri);
|
|
||||||
}
|
}
|
||||||
|
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()
|
protected override AuthenticationTicket AuthenticateCore()
|
||||||
|
|
@ -216,15 +248,21 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// Invoked to process incoming OpenIdConnect messages.
|
/// Invoked to process incoming OpenIdConnect messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
|
/// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
|
||||||
|
/// <remarks>Uses log id's OIDCH-0000 - OIDCH-0025</remarks>
|
||||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
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.
|
// 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))
|
if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path))
|
||||||
{
|
{
|
||||||
return null;
|
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.
|
// 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)
|
if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|
@ -235,180 +273,215 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
{
|
{
|
||||||
IFormCollection form = await Request.ReadFormAsync();
|
IFormCollection form = await Request.ReadFormAsync();
|
||||||
Request.Body.Seek(0, SeekOrigin.Begin);
|
Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
message = new OpenIdConnectMessage(form);
|
||||||
openIdConnectMessage = new OpenIdConnectMessage(form);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openIdConnectMessage == null)
|
if (message == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
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);
|
await Options.Notifications.MessageReceived(messageReceivedNotification);
|
||||||
if (messageReceivedNotification.HandledResponse)
|
if (messageReceivedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
|
||||||
return messageReceivedNotification.AuthenticationTicket;
|
return messageReceivedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageReceivedNotification.Skipped)
|
if (messageReceivedNotification.Skipped)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we
|
// runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we should process.
|
||||||
// should process.
|
if (string.IsNullOrWhiteSpace(message.State))
|
||||||
AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State);
|
{
|
||||||
|
_logger.LogError(Resources.OIDCH_0004_MessageStateIsNullOrWhiteSpace);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = GetPropertiesFromState(message.State);
|
||||||
if (properties == null)
|
if (properties == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("The state field is missing or invalid.");
|
_logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
|
// 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(
|
_logger.LogError(Resources.OIDCH_0006_MessageErrorNotNull, message.Error);
|
||||||
string.Format(CultureInfo.InvariantCulture,
|
throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageErrorNotNull, message.Error));
|
||||||
openIdConnectMessage.Error,
|
|
||||||
Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// code is only accepted with id_token, in this version, hence check for code is inside this if
|
AuthenticationTicket ticket = null;
|
||||||
// OpenIdConnect protocol allows a Code to be received without the id_token
|
JwtSecurityToken jwt = null;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_configuration == null && Options.ConfigurationManager != null)
|
if (_configuration == null && Options.ConfigurationManager != null)
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug(Resources.OIDCH_0007_UpdatingConfiguration);
|
||||||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy and augment to avoid cross request race conditions for updated configurations.
|
// OpenIdConnect protocol allows a Code to be received without the id_token
|
||||||
TokenValidationParameters validationParameters = Options.TokenValidationParameters.Clone();
|
if (!string.IsNullOrWhiteSpace(message.IdToken))
|
||||||
if (_configuration != null)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
|
_logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
|
||||||
{
|
var securityTokenReceivedNotification =
|
||||||
validationParameters.ValidIssuer = _configuration.Issuer;
|
new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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)
|
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)
|
|
||||||
{
|
{
|
||||||
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;
|
ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
|
||||||
if (expires != DateTime.MinValue)
|
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,
|
_logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code);
|
||||||
ProtocolMessage = openIdConnectMessage
|
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)
|
var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
|
||||||
{
|
{
|
||||||
AuthenticationTicket = ticket,
|
AuthenticationTicket = ticket,
|
||||||
Code = openIdConnectMessage.Code,
|
Code = message.Code,
|
||||||
JwtSecurityToken = jwt,
|
JwtSecurityToken = jwt,
|
||||||
ProtocolMessage = openIdConnectMessage,
|
ProtocolMessage = message,
|
||||||
RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
|
RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
|
||||||
ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
|
ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
|
||||||
};
|
};
|
||||||
|
|
@ -416,11 +489,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
|
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
|
||||||
if (authorizationCodeReceivedNotification.HandledResponse)
|
if (authorizationCodeReceivedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse);
|
||||||
return authorizationCodeReceivedNotification.AuthenticationTicket;
|
return authorizationCodeReceivedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizationCodeReceivedNotification.Skipped)
|
if (authorizationCodeReceivedNotification.Skipped)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -429,7 +504,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
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.
|
// 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)))
|
if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
|
||||||
|
|
@ -437,20 +512,23 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
Options.ConfigurationManager.RequestRefresh();
|
Options.ConfigurationManager.RequestRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
var authenticationFailedNotification = new AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
var authenticationFailedNotification =
|
||||||
{
|
new AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||||
ProtocolMessage = openIdConnectMessage,
|
{
|
||||||
Exception = exception
|
ProtocolMessage = message,
|
||||||
};
|
Exception = exception
|
||||||
|
};
|
||||||
|
|
||||||
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
|
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
|
||||||
if (authenticationFailedNotification.HandledResponse)
|
if (authenticationFailedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
|
||||||
return authenticationFailedNotification.AuthenticationTicket;
|
return authenticationFailedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authenticationFailedNotification.Skipped)
|
if (authenticationFailedNotification.Skipped)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,7 +542,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// <param name="nonce">the nonce to remember.</param>
|
/// <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)'.
|
/// <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>
|
/// The value of the cookie is: "N".</remarks>
|
||||||
private void RememberNonce(string nonce)
|
private void WriteNonceCookie(string nonce)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(nonce))
|
if (string.IsNullOrWhiteSpace(nonce))
|
||||||
{
|
{
|
||||||
|
|
@ -484,13 +562,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches <see cref="HttpRequest.Cookies"/> for a matching nonce.
|
/// Searches <see cref="HttpRequest.Cookies"/> for a matching nonce.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nonceExpectedValue">the nonce that was found in the jwt token.</param>
|
/// <param name="nonce">the nonce that we are looking for.</param>
|
||||||
/// <returns>'nonceExpectedValue' if a cookie is found that matches, null otherwise.</returns>
|
/// <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'.
|
/// <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>
|
/// <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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -502,7 +580,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string nonceDecodedValue = Options.StringDataFormat.Unprotect(nonceKey.Substring(OpenIdConnectAuthenticationDefaults.CookieNoncePrefix.Length, nonceKey.Length - OpenIdConnectAuthenticationDefaults.CookieNoncePrefix.Length));
|
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
|
var cookieOptions = new CookieOptions
|
||||||
{
|
{
|
||||||
|
|
@ -511,7 +589,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
};
|
};
|
||||||
|
|
||||||
Response.Cookies.Delete(nonceKey, cookieOptions);
|
Response.Cookies.Delete(nonceKey, cookieOptions);
|
||||||
return nonceExpectedValue;
|
return nonce;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a <see cref="OpenIdConnectAuthenticationMiddleware"/>
|
/// Initializes a <see cref="OpenIdConnectAuthenticationMiddleware"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="next">The next middleware in the ASP.NET pipeline to invoke</param>
|
/// <param name="next">The next middleware in the ASP.NET pipeline to invoke.</param>
|
||||||
/// <param name="app">The ASP.NET application</param>
|
/// <param name="dataProtectionProvider"> provider for creating a data protector.</param>
|
||||||
/// <param name="options">Configuration options for the middleware</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")]
|
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
|
||||||
public OpenIdConnectAuthenticationMiddleware(
|
public OpenIdConnectAuthenticationMiddleware(
|
||||||
[NotNull] RequestDelegate next,
|
[NotNull] RequestDelegate next,
|
||||||
|
|
@ -40,21 +44,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
[NotNull] ILoggerFactory loggerFactory,
|
[NotNull] ILoggerFactory loggerFactory,
|
||||||
[NotNull] IOptions<ExternalAuthenticationOptions> externalOptions,
|
[NotNull] IOptions<ExternalAuthenticationOptions> externalOptions,
|
||||||
[NotNull] IOptions<OpenIdConnectAuthenticationOptions> options,
|
[NotNull] IOptions<OpenIdConnectAuthenticationOptions> options,
|
||||||
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions)
|
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions = null)
|
||||||
: base(next, options, configureOptions)
|
: base(next, options, configureOptions)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger<OpenIdConnectAuthenticationMiddleware>();
|
_logger = loggerFactory.CreateLogger<OpenIdConnectAuthenticationMiddleware>();
|
||||||
|
if (string.IsNullOrEmpty(Options.SignInScheme) && !string.IsNullOrEmpty(externalOptions.Options.SignInScheme))
|
||||||
if (string.IsNullOrEmpty(Options.SignInScheme))
|
|
||||||
{
|
{
|
||||||
Options.SignInScheme = externalOptions.Options.SignInScheme;
|
Options.SignInScheme = externalOptions.Options.SignInScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType))
|
|
||||||
{
|
|
||||||
Options.TokenValidationParameters.AuthenticationType = Options.AuthenticationScheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Options.StateDataFormat == null)
|
if (Options.StateDataFormat == null)
|
||||||
{
|
{
|
||||||
var dataProtector = dataProtectionProvider.CreateProtector(
|
var dataProtector = dataProtectionProvider.CreateProtector(
|
||||||
|
|
@ -152,7 +150,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
var webRequestHandler = handler as WebRequestHandler;
|
var webRequestHandler = handler as WebRequestHandler;
|
||||||
if (webRequestHandler == null)
|
if (webRequestHandler == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
|
throw new InvalidOperationException(Resources.OIDCH_0102_ExceptionValidatorHandlerMismatch);
|
||||||
}
|
}
|
||||||
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
|
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")]
|
[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)
|
public OpenIdConnectAuthenticationOptions(string authenticationScheme)
|
||||||
{
|
{
|
||||||
// REVIEW: why was this active by default??
|
|
||||||
//AuthenticationMode = AuthenticationMode.Active;
|
|
||||||
AuthenticationScheme = authenticationScheme;
|
AuthenticationScheme = authenticationScheme;
|
||||||
BackchannelTimeout = TimeSpan.FromMinutes(1);
|
BackchannelTimeout = TimeSpan.FromMinutes(1);
|
||||||
Caption = OpenIdConnectAuthenticationDefaults.Caption;
|
Caption = OpenIdConnectAuthenticationDefaults.Caption;
|
||||||
ProtocolValidator = new OpenIdConnectProtocolValidator();
|
ProtocolValidator = new OpenIdConnectProtocolValidator();
|
||||||
RefreshOnIssuerKeyNotFound = true;
|
RefreshOnIssuerKeyNotFound = true;
|
||||||
|
ResponseMode = OpenIdConnectResponseModes.FormPost;
|
||||||
ResponseType = OpenIdConnectResponseTypes.CodeIdToken;
|
ResponseType = OpenIdConnectResponseTypes.CodeIdToken;
|
||||||
Scope = OpenIdConnectScopes.OpenIdProfile;
|
Scope = OpenIdConnectScopes.OpenIdProfile;
|
||||||
TokenValidationParameters = new TokenValidationParameters();
|
TokenValidationParameters = new TokenValidationParameters();
|
||||||
UseTokenLifetime = true;
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the Authority to use when making OpenIdConnect calls.
|
/// Gets or sets the Authority to use when making OpenIdConnect calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Authority { get; set; }
|
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
|
#if DNX451
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
|
/// 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)
|
if (value <= TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException("BackchannelTimeout", value, Resources.ArgsException_BackchallelLessThanZero);
|
throw new ArgumentOutOfRangeException("BackchannelTimeout", value, Resources.OIDCH_0101_BackChallnelLessThanZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
_backchannelTimeout = value;
|
_backchannelTimeout = value;
|
||||||
|
|
@ -128,6 +127,14 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
set { Description.Caption = value; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the 'client_id'.
|
/// Gets or sets the 'client_id'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -145,11 +152,16 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
public OpenIdConnectConfiguration Configuration { get; set; }
|
public OpenIdConnectConfiguration Configuration { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The OpenIdConnect protocol http://openid.net/specs/openid-connect-core-1_0.html
|
/// Responsible for retrieving, caching, and refreshing the configuration from metadata.
|
||||||
/// recommends adding a nonce to a request as a mitigation against replay attacks when requesting id_tokens.
|
/// If not provided, then one will be created using the MetadataAddress and Backchannel properties.
|
||||||
/// By default the runtime uses cookies with unique names generated from a hash of the nonce.
|
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets or sets the discovery endpoint for obtaining metadata
|
/// Gets or sets the discovery endpoint for obtaining metadata
|
||||||
|
|
@ -157,24 +169,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
public string MetadataAddress { get; set; }
|
public string MetadataAddress { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <value>
|
public INonceCache NonceCache { get; set; }
|
||||||
/// 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; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="OpenIdConnectAuthenticationNotifications"/> to notify when processing OpenIdConnect messages.
|
/// 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")]
|
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "By Design")]
|
||||||
public string RedirectUri { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the 'resource'.
|
/// Gets or sets the 'resource'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Resource { get; set; }
|
public string Resource { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the 'response_mode'.
|
||||||
|
/// </summary>
|
||||||
|
public string ResponseMode { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the 'response_type'.
|
/// Gets or sets the 'response_type'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -233,10 +243,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
public string Scope { get; set; }
|
public string Scope { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the authentication scheme corresponding to the middleware
|
/// Gets or sets the SignInScheme which will be used to set the <see cref="System.Security.Claims.ClaimsIdentity.AuthenticationType"/>.
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SignInScheme { get; set; }
|
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)
|
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)
|
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)
|
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)
|
public static IServiceCollection ConfigureOpenIdConnectAuthentication([NotNull] this IServiceCollection services, [NotNull] IConfiguration config, string optionsName)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,9 @@
|
||||||
//------------------------------------------------------------------------------
|
// <auto-generated />
|
||||||
// <auto-generated>
|
namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
// This code was generated by a tool.
|
{
|
||||||
// Runtime Version:4.0.30319.34014
|
using System.Globalization;
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
/// 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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
internal class Resources {
|
internal class Resources
|
||||||
|
{
|
||||||
private static global::System.Resources.ResourceManager resourceMan;
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
internal Resources() {
|
internal Resources()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the cached ResourceManager instance used by this class.
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
get {
|
{
|
||||||
if (object.ReferenceEquals(resourceMan, null)) {
|
get
|
||||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Owin.Security.OpenIdConnect.Resources", IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly);
|
{
|
||||||
|
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;
|
resourceMan = temp;
|
||||||
}
|
}
|
||||||
return resourceMan;
|
return resourceMan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overrides the current thread's CurrentUICulture property for all
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
/// resource lookups using this strongly typed resource class.
|
/// resource lookups using this strongly typed resource class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Globalization.CultureInfo Culture {
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
get {
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
return resourceCulture;
|
return resourceCulture;
|
||||||
}
|
}
|
||||||
set {
|
set
|
||||||
|
{
|
||||||
resourceCulture = value;
|
resourceCulture = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
internal static string ArgsException_BackchallelLessThanZero {
|
internal static string OIDCH_0101_BackChallnelLessThanZero
|
||||||
get {
|
{
|
||||||
return ResourceManager.GetString("ArgsException_BackchallelLessThanZero", resourceCulture);
|
get { return ResourceManager.GetString("OIDCH_0101_BackChallnelLessThanZero"); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
internal static string Exception_OpenIdConnectMessageError {
|
internal static string OIDCH_0102_ExceptionValidatorHandlerMismatch
|
||||||
get {
|
{
|
||||||
return ResourceManager.GetString("Exception_OpenIdConnectMessageError", resourceCulture);
|
get { return ResourceManager.GetString("OIDCH_0102_Exception_ValidatorHandlerMismatch"); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
internal static string Exception_RedirectUri_LogoutQueryString_IsNotWellFormed {
|
internal static string OIDCH_0051_RedirectUriLogoutIsNotWellFormed
|
||||||
get {
|
{
|
||||||
return ResourceManager.GetString("Exception_RedirectUri_LogoutQueryString_IsNotWellFormed", resourceCulture);
|
get { return ResourceManager.GetString("OIDCH_0051_RedirectUriLogoutIsNotWellFormed"); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
internal static string Exception_ValidatorHandlerMismatch {
|
internal static string OIDCH_0026_ApplyResponseChallengeAsync
|
||||||
get {
|
{
|
||||||
return ResourceManager.GetString("Exception_ValidatorHandlerMismatch", resourceCulture);
|
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">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ArgsException_BackchallelLessThanZero" xml:space="preserve">
|
<data name="OIDCH_0101_BackChallnelLessThanZero" xml:space="preserve">
|
||||||
<value>BackchannelTimeout cannot be less or equal to TimeSpan.Zero.</value>
|
<value>OIDCH_0101: BackchannelTimeout cannot be less or equal to TimeSpan.Zero.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Exception_OpenIdConnectMessageError" xml:space="preserve">
|
<data name="OIDCH_0102_Exception_ValidatorHandlerMismatch" 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>
|
<value>OIDCH_0102: An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Exception_RedirectUri_LogoutQueryString_IsNotWellFormed" xml:space="preserve">
|
<data name="OIDCH_0051_RedirectUriLogoutIsNotWellFormed" 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>
|
<value>OIDC_0051: The query string for Logout is not a well formed URI. The runtime cannot redirect. Redirect uri: '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Exception_ValidatorHandlerMismatch" xml:space="preserve">
|
<data name="OIDCH_0026_ApplyResponseChallengeAsync" xml:space="preserve">
|
||||||
<value>An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.</value>
|
<value>OIDCH_0026: Entering: '{0}'</value>
|
||||||
</data>
|
</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