#214 Refactor OIDC state parameters.
This commit is contained in:
parent
039cc18e8b
commit
57031946d0
|
|
@ -9,14 +9,14 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
public static class OpenIdConnectAuthenticationDefaults
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value used for OpenIdConnectAuthenticationOptions.AuthenticationScheme
|
||||
/// Constant used to identify state in openIdConnect protocol message.
|
||||
/// </summary>
|
||||
public const string AuthenticationScheme = "OpenIdConnect";
|
||||
public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
||||
|
||||
/// <summary>
|
||||
/// The prefix used to provide a default OpenIdConnectAuthenticationOptions.CookieName
|
||||
/// The default value used for OpenIdConnectAuthenticationOptions.AuthenticationScheme.
|
||||
/// </summary>
|
||||
public const string CookiePrefix = ".AspNet.OpenIdConnect.";
|
||||
public const string AuthenticationScheme = "OpenIdConnect";
|
||||
|
||||
/// <summary>
|
||||
/// The default value for OpenIdConnectAuthenticationOptions.Caption.
|
||||
|
|
@ -24,18 +24,23 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
public const string Caption = "OpenIdConnect";
|
||||
|
||||
/// <summary>
|
||||
/// The prefix used to for the a nonce in the cookie
|
||||
/// The prefix used to provide a default OpenIdConnectAuthenticationOptions.CookieName.
|
||||
/// </summary>
|
||||
public const string CookiePrefix = ".AspNet.OpenIdConnect.";
|
||||
|
||||
/// <summary>
|
||||
/// The prefix used to for the a nonce in the cookie.
|
||||
/// </summary>
|
||||
public const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
||||
|
||||
/// <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'.
|
||||
/// </summary>
|
||||
public const string RedirectUriUsedForCodeKey = "OpenIdConnect.Code.RedirectUri";
|
||||
public const string RedirectUriForCodePropertiesKey = "OpenIdConnect.Code.RedirectUri";
|
||||
|
||||
/// <summary>
|
||||
/// Constant used to identify state in openIdConnect protocal message
|
||||
/// Constant used to identify userstate inside AuthenticationProperties that have been serialized in the 'state' parameter.
|
||||
/// </summary>
|
||||
public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
||||
public const string UserstatePropertiesKey = "OpenIdConnect.Userstate";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,19 +28,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
private const string UriSchemeDelimiter = "://";
|
||||
private OpenIdConnectConfiguration _configuration;
|
||||
|
||||
private string CurrentUri
|
||||
{
|
||||
get
|
||||
{
|
||||
return Request.Scheme +
|
||||
UriSchemeDelimiter +
|
||||
Request.Host +
|
||||
Request.PathBase +
|
||||
Request.Path +
|
||||
Request.QueryString;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Signout
|
||||
/// </summary>
|
||||
|
|
@ -54,7 +41,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||
}
|
||||
|
||||
var openIdConnectMessage = new OpenIdConnectMessage()
|
||||
var message = new OpenIdConnectMessage()
|
||||
{
|
||||
IssuerAddress = _configuration == null ? string.Empty : (_configuration.EndSessionEndpoint ?? string.Empty),
|
||||
RequestType = OpenIdConnectRequestType.LogoutRequest,
|
||||
|
|
@ -66,30 +53,42 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
var properties = new AuthenticationProperties(signout.Properties);
|
||||
if (!string.IsNullOrEmpty(properties.RedirectUri))
|
||||
{
|
||||
openIdConnectMessage.PostLogoutRedirectUri = properties.RedirectUri;
|
||||
message.PostLogoutRedirectUri = properties.RedirectUri;
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(Options.PostLogoutRedirectUri))
|
||||
else if (!string.IsNullOrEmpty(Options.PostLogoutRedirectUri))
|
||||
{
|
||||
openIdConnectMessage.PostLogoutRedirectUri = Options.PostLogoutRedirectUri;
|
||||
message.PostLogoutRedirectUri = Options.PostLogoutRedirectUri;
|
||||
}
|
||||
|
||||
var notification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
if (Options.Notifications.RedirectToIdentityProvider != null)
|
||||
{
|
||||
ProtocolMessage = openIdConnectMessage
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(notification);
|
||||
|
||||
if (!notification.HandledResponse)
|
||||
{
|
||||
var redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
|
||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
Logger.LogWarning(Resources.OIDCH_0051_RedirectUriLogoutIsNotWellFormed, redirectUri);
|
||||
ProtocolMessage = message
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||
return;
|
||||
}
|
||||
else if (redirectToIdentityProviderNotification.Skipped)
|
||||
{
|
||||
Logger.LogVerbose(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
||||
return;
|
||||
}
|
||||
|
||||
Response.Redirect(redirectUri);
|
||||
message = redirectToIdentityProviderNotification.ProtocolMessage;
|
||||
}
|
||||
|
||||
var redirectUri = message.CreateLogoutRequestUrl();
|
||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||
{
|
||||
Logger.LogWarning(Resources.OIDCH_0051_RedirectUriLogoutIsNotWellFormed, redirectUri);
|
||||
}
|
||||
|
||||
Response.Redirect(redirectUri);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +106,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
// 2. CurrentUri if Options.DefaultToCurrentUriOnRedirect is true)
|
||||
AuthenticationProperties properties = new AuthenticationProperties(context.Properties);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(properties.RedirectUri))
|
||||
if (!string.IsNullOrEmpty(properties.RedirectUri))
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0030_Using_Properties_RedirectUri, properties.RedirectUri);
|
||||
}
|
||||
|
|
@ -117,15 +116,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
properties.RedirectUri = CurrentUri;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
|
||||
if (!string.IsNullOrEmpty(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.IsNullOrEmpty(Options.RedirectUri))
|
||||
{
|
||||
properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey, Options.RedirectUri);
|
||||
properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey, Options.RedirectUri);
|
||||
}
|
||||
|
||||
if (_configuration == null && Options.ConfigurationManager != null)
|
||||
|
|
@ -138,13 +137,12 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
ClientId = Options.ClientId,
|
||||
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
|
||||
RedirectUri = Options.RedirectUri,
|
||||
// [brentschmaltz] - this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
|
||||
// [brentschmaltz] - #215 this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
|
||||
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
|
||||
Resource = Options.Resource,
|
||||
ResponseMode = Options.ResponseMode,
|
||||
ResponseType = Options.ResponseType,
|
||||
Scope = Options.Scope,
|
||||
State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + UrlEncoder.UrlEncode(Options.StateDataFormat.Protect(properties))
|
||||
Scope = Options.Scope
|
||||
};
|
||||
|
||||
if (Options.ProtocolValidator.RequireNonce)
|
||||
|
|
@ -169,24 +167,37 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
}
|
||||
|
||||
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
if (Options.Notifications.RedirectToIdentityProvider != null)
|
||||
{
|
||||
ProtocolMessage = message
|
||||
};
|
||||
var redirectToIdentityProviderNotification =
|
||||
new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = message
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||
return true; // REVIEW: Make sure this should stop all other handlers
|
||||
}
|
||||
else if (redirectToIdentityProviderNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
||||
return false; // REVIEW: Make sure this should not stop all other handlers
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||
return true;
|
||||
}
|
||||
else if (redirectToIdentityProviderNotification.Skipped)
|
||||
{
|
||||
Logger.LogVerbose(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(redirectToIdentityProviderNotification.ProtocolMessage.State))
|
||||
{
|
||||
properties.Items[OpenIdConnectAuthenticationDefaults.UserstatePropertiesKey] = redirectToIdentityProviderNotification.ProtocolMessage.State;
|
||||
}
|
||||
|
||||
message = redirectToIdentityProviderNotification.ProtocolMessage;
|
||||
}
|
||||
|
||||
var redirectUri = redirectToIdentityProviderNotification.ProtocolMessage.CreateAuthenticationRequestUrl();
|
||||
message.State = Options.StateDataFormat.Protect(properties);
|
||||
|
||||
var redirectUri = message.CreateAuthenticationRequestUrl();
|
||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||
{
|
||||
Logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
|
||||
|
|
@ -246,66 +257,74 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
await Options.Notifications.MessageReceived(messageReceivedNotification);
|
||||
if (messageReceivedNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
|
||||
Logger.LogVerbose(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
|
||||
return messageReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (messageReceivedNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
|
||||
Logger.LogVerbose(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
// runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we should process.
|
||||
if (string.IsNullOrWhiteSpace(message.State))
|
||||
var properties = new AuthenticationProperties();
|
||||
|
||||
// if state is missing, just log it
|
||||
if (string.IsNullOrEmpty(message.State))
|
||||
{
|
||||
Logger.LogError(Resources.OIDCH_0004_MessageStateIsNullOrWhiteSpace);
|
||||
return null;
|
||||
Logger.LogWarning(Resources.OIDCH_0004_MessageStateIsNullOrEmpty);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if state exists and we failed to 'unprotect' this is not a message we should process.
|
||||
properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(message.State));
|
||||
if (properties == null)
|
||||
{
|
||||
Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
|
||||
return null;
|
||||
}
|
||||
|
||||
string userstate = null;
|
||||
properties.Items.TryGetValue(OpenIdConnectAuthenticationDefaults.UserstatePropertiesKey, out userstate);
|
||||
message.State = userstate;
|
||||
}
|
||||
|
||||
var properties = GetPropertiesFromState(message.State);
|
||||
if (properties == null)
|
||||
// if any of the error fields are set, throw error null
|
||||
if (!string.IsNullOrEmpty(message.Error))
|
||||
{
|
||||
Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
|
||||
return null;
|
||||
Logger.LogError(Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null");
|
||||
throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null"));
|
||||
}
|
||||
|
||||
// devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
|
||||
if (!string.IsNullOrWhiteSpace(message.Error))
|
||||
if (_configuration == null && Options.ConfigurationManager != null)
|
||||
{
|
||||
Logger.LogError(Resources.OIDCH_0006_MessageErrorNotNull, message.Error);
|
||||
throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageErrorNotNull, message.Error));
|
||||
Logger.LogVerbose(Resources.OIDCH_0007_UpdatingConfiguration);
|
||||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||
}
|
||||
|
||||
AuthenticationTicket ticket = null;
|
||||
JwtSecurityToken jwt = null;
|
||||
|
||||
if (_configuration == null && Options.ConfigurationManager != null)
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0007_UpdatingConfiguration);
|
||||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||
}
|
||||
|
||||
// OpenIdConnect protocol allows a Code to be received without the id_token
|
||||
if (!string.IsNullOrWhiteSpace(message.IdToken))
|
||||
if (!string.IsNullOrEmpty(message.IdToken))
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
|
||||
var securityTokenReceivedNotification =
|
||||
new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
ProtocolMessage = message
|
||||
ProtocolMessage = message,
|
||||
};
|
||||
|
||||
await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
|
||||
if (securityTokenReceivedNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
|
||||
Logger.LogVerbose(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
|
||||
return securityTokenReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenReceivedNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
|
||||
Logger.LogVerbose(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -313,11 +332,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
var validationParameters = Options.TokenValidationParameters.Clone();
|
||||
if (_configuration != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
|
||||
if (string.IsNullOrEmpty(validationParameters.ValidIssuer))
|
||||
{
|
||||
validationParameters.ValidIssuer = _configuration.Issuer;
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(_configuration.Issuer))
|
||||
else if (!string.IsNullOrEmpty(_configuration.Issuer))
|
||||
{
|
||||
validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(new[] { _configuration.Issuer }) ?? new[] { _configuration.Issuer };
|
||||
}
|
||||
|
|
@ -336,7 +355,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
if (jwt == null)
|
||||
{
|
||||
Logger.LogError(Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType());
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
|
||||
throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -344,16 +363,16 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
if (validatedToken == null)
|
||||
{
|
||||
Logger.LogError(Resources.OIDCH_0011_UnableToValidateToken, message.IdToken);
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0011_UnableToValidateToken, message.IdToken));
|
||||
throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0011_UnableToValidateToken, message.IdToken));
|
||||
}
|
||||
|
||||
ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
|
||||
if (!string.IsNullOrWhiteSpace(message.SessionState))
|
||||
if (!string.IsNullOrEmpty(message.SessionState))
|
||||
{
|
||||
ticket.Properties.Items[OpenIdConnectSessionProperties.SessionState] = message.SessionState;
|
||||
}
|
||||
|
||||
if (_configuration != null && !string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
|
||||
if (_configuration != null && !string.IsNullOrEmpty(_configuration.CheckSessionIframe))
|
||||
{
|
||||
ticket.Properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
|
||||
}
|
||||
|
|
@ -384,13 +403,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
|
||||
if (securityTokenValidatedNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
|
||||
Logger.LogVerbose(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
|
||||
return securityTokenValidatedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenValidatedNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
|
||||
Logger.LogVerbose(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -416,7 +435,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
var protocolValidationContext = new OpenIdConnectProtocolValidationContext
|
||||
{
|
||||
AuthorizationCode = message.Code,
|
||||
Nonce = nonce,
|
||||
Nonce = nonce,
|
||||
};
|
||||
|
||||
Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
|
||||
|
|
@ -424,7 +443,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
|
||||
if (message.Code != null)
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code);
|
||||
Logger.LogDebug(Resources.OIDCH_0014_AuthorizationCodeReceived, message.Code);
|
||||
if (ticket == null)
|
||||
{
|
||||
ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
|
|
@ -436,20 +455,20 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
Code = message.Code,
|
||||
JwtSecurityToken = jwt,
|
||||
ProtocolMessage = message,
|
||||
RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
|
||||
ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
|
||||
RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey) ?
|
||||
ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey] : string.Empty,
|
||||
};
|
||||
|
||||
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
|
||||
if (authorizationCodeReceivedNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse);
|
||||
Logger.LogVerbose(Resources.OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse);
|
||||
return authorizationCodeReceivedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (authorizationCodeReceivedNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped);
|
||||
Logger.LogVerbose(Resources.OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -463,7 +482,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
// 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)))
|
||||
{
|
||||
Options.ConfigurationManager.RequestRefresh();
|
||||
if (Options.ConfigurationManager != null)
|
||||
{
|
||||
Logger.LogVerbose(Resources.OIDCH_0021_AutomaticConfigurationRefresh);
|
||||
Options.ConfigurationManager.RequestRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
var authenticationFailedNotification =
|
||||
|
|
@ -476,13 +499,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
|
||||
if (authenticationFailedNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
|
||||
Logger.LogVerbose(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
|
||||
return authenticationFailedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (authenticationFailedNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
|
||||
Logger.LogVerbose(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0027: converted 401 to 403.
|
||||
/// OIDCH_0027: Converted 401 to 403.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0027_401_ConvertedTo_403
|
||||
{
|
||||
|
|
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0030: using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
|
||||
/// OIDCH_0030: Using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0030_Using_Properties_RedirectUri
|
||||
{
|
||||
|
|
@ -125,7 +125,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0031: using Options.RedirectUri for 'redirect_uri': '{0}'.
|
||||
/// OIDCH_0031: Using Options.RedirectUri for 'redirect_uri': '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0031_Using_Options_RedirectUri
|
||||
{
|
||||
|
|
@ -133,7 +133,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.
|
||||
/// OIDCH_0032: Using the CurrentUri for 'local redirect' post authentication: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0032_UsingCurrentUriRedirectUri
|
||||
{
|
||||
|
|
@ -145,11 +145,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// </summary>
|
||||
internal static string OIDCH_0033_NonceAlreadyExists
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); }
|
||||
get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse
|
||||
/// OIDCH_0034: RedirectToIdentityProviderNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse
|
||||
{
|
||||
|
|
@ -157,7 +157,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0035: redirectToIdentityProviderNotification.Skipped
|
||||
/// OIDCH_0035: RedirectToIdentityProviderNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0035_RedirectToIdentityProviderNotificationSkipped
|
||||
{
|
||||
|
|
@ -165,13 +165,21 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: {0}", (redirectUri ?? "null"))
|
||||
/// OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: {0}'.)
|
||||
/// </summary>
|
||||
internal static string OIDCH_0036_UriIsNotWellFormed
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0036_UriIsNotWellFormed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0036: RedirectUri is: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0037_RedirectUri
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0037_RedirectUri"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0000: Entering: '{0}'.
|
||||
/// </summary>
|
||||
|
|
@ -197,7 +205,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0002: messageReceivedNotification.HandledResponse
|
||||
/// OIDCH_0002: MessageReceivedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0002_MessageReceivedNotificationHandledResponse
|
||||
{
|
||||
|
|
@ -205,7 +213,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0003: messageReceivedNotification.Skipped
|
||||
/// OIDCH_0003: MessageReceivedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0003_MessageReceivedNotificationSkipped
|
||||
{
|
||||
|
|
@ -213,15 +221,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or whitespace. State is required to process the message.
|
||||
/// OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or empty.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0004_MessageStateIsNullOrWhiteSpace
|
||||
internal static string OIDCH_0004_MessageStateIsNullOrEmpty
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0004_MessageStateIsNullOrWhiteSpace"); }
|
||||
get { return ResourceManager.GetString("OIDCH_0004_MessageStateIsNullOrEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0005: unable to unprotect the message.State
|
||||
/// OIDCH_0005: Unable to unprotect the message.State
|
||||
/// </summary>
|
||||
internal static string OIDCH_0005_MessageStateIsInvalid
|
||||
{
|
||||
|
|
@ -229,15 +237,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0006_MessageErrorNotNull: '{0}'.
|
||||
/// OIDCH_0006: Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0006_MessageErrorNotNull
|
||||
internal static string OIDCH_0006_MessageContainsError
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0006_MessageErrorNotNull"); }
|
||||
get { return ResourceManager.GetString("OIDCH_0006_MessageContainsError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0007: updating configuration
|
||||
/// OIDCH_0007: Updating configuration
|
||||
/// </summary>
|
||||
internal static string OIDCH_0007_UpdatingConfiguration
|
||||
{
|
||||
|
|
@ -245,7 +253,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0008: securityTokenReceivedNotification.HandledResponse
|
||||
/// OIDCH_0008: SecurityTokenReceivedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse
|
||||
{
|
||||
|
|
@ -253,7 +261,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0009: securityTokenReceivedNotification.Skipped
|
||||
/// OIDCH_0009: SecurityTokenReceivedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0009_SecurityTokenReceivedNotificationSkipped
|
||||
{
|
||||
|
|
@ -269,7 +277,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0011: Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: {0}."
|
||||
/// OIDCH_0011: Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0011_UnableToValidateToken
|
||||
{
|
||||
|
|
@ -277,7 +285,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0012: securityTokenValidatedNotification.HandledResponse
|
||||
/// OIDCH_0012: SecurityTokenValidatedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse
|
||||
{
|
||||
|
|
@ -285,7 +293,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0013: securityTokenValidatedNotification.Skipped
|
||||
/// OIDCH_0013: SecurityTokenValidatedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0013_SecurityTokenValidatedNotificationSkipped
|
||||
{
|
||||
|
|
@ -293,31 +301,31 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0014: 'code' received: '{0}'
|
||||
/// OIDCH_0014: AuthorizationCode received: '{0}'
|
||||
/// </summary>
|
||||
internal static string OIDCH_0014_CodeReceived
|
||||
internal static string OIDCH_0014_AuthorizationCodeReceived
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0014_CodeReceived"); }
|
||||
get { return ResourceManager.GetString("OIDCH_0014_AuthorizationCodeReceived"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0015: codeReceivedNotification.HandledResponse")
|
||||
/// OIDCH_0015: AuthorizationCodeReceivedNotification.HandledResponse
|
||||
/// </summary>
|
||||
internal static string OIDCH_0015_CodeReceivedNotificationHandledResponse
|
||||
internal static string OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0015_CodeReceivedNotificationHandledResponse"); }
|
||||
get { return ResourceManager.GetString("OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0016: codeReceivedNotification.Skipped
|
||||
/// </summary>
|
||||
internal static string OIDCH_0016_CodeReceivedNotificationSkipped
|
||||
internal static string OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0016_CodeReceivedNotificationSkipped"); }
|
||||
get { return ResourceManager.GetString("OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0017: Exception occurred while processing message
|
||||
/// OIDCH_0017: Exception occurred while processing message.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0017_ExceptionOccurredWhileProcessingMessage
|
||||
{
|
||||
|
|
@ -341,11 +349,20 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0020: 'id_token' received: '{0}'
|
||||
/// OIDCH_0020: 'id_token' received: '{0}'.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0020_IdTokenReceived
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0020_IdTokenReceived"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OIDCH_0021: exception of type 'SecurityTokenSignatureKeyNotFoundException' thrown, Options.ConfigurationManager.RequestRefresh() called.
|
||||
/// </summary>
|
||||
internal static string OIDCH_0021_AutomaticConfigurationRefresh
|
||||
{
|
||||
get { return ResourceManager.GetString("OIDCH_0021_AutomaticConfigurationRefresh"); }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
<value>OIDCH_0026: Entering: '{0}'</value>
|
||||
</data>
|
||||
<data name="OIDCH_0027_401_ConvertedTo_403" xml:space="preserve">
|
||||
<value>OIDCH_0027: converted 401 to 403.</value>
|
||||
<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>
|
||||
|
|
@ -139,10 +139,10 @@
|
|||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -151,13 +151,16 @@
|
|||
<value>OIDCH_0033: ProtocolValidator.RequireNonce == true. The generated nonce already exists: this usually indicates the nonce is not unique or has been used. The nonce is: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse</value>
|
||||
<value>OIDCH_0034: RedirectToIdentityProviderNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0035_RedirectToIdentityProviderNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0035: redirectToIdentityProviderNotification.Skipped</value>
|
||||
<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>
|
||||
<value>OIDCH_0036: Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute) returned 'false', redirectUri is: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0037_RedirectUri" xml:space="preserve">
|
||||
<value>OIDCH_0037: RedirectUri is: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0000_AuthenticateCoreAsync" xml:space="preserve">
|
||||
<value>OIDCH_0000: Entering: '{0}'.</value>
|
||||
|
|
@ -166,60 +169,63 @@
|
|||
<value>OIDCH_0001: MessageReceived: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0002_MessageReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0002: messageReceivedNotification.HandledResponse</value>
|
||||
<value>OIDCH_0002: MessageReceivedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0003_MessageReceivedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0003: messageReceivedNotification.Skipped</value>
|
||||
<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 name="OIDCH_0004_MessageStateIsNullOrEmpty" xml:space="preserve">
|
||||
<value>OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or empty.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0005_MessageStateIsInValid" xml:space="preserve">
|
||||
<value>OIDCH_0005: unable to unprotect the message.State</value>
|
||||
<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 name="OIDCH_0006_MessageContainsError" xml:space="preserve">
|
||||
<value>OIDCH_0006: Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0007_UpdatingConfiguration" xml:space="preserve">
|
||||
<value>OIDCH_0007: updating configuration</value>
|
||||
<value>OIDCH_0007: Updating configuration</value>
|
||||
</data>
|
||||
<data name="OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0008: securityTokenReceivedNotification.HandledResponse</value>
|
||||
<value>OIDCH_0008: SecurityTokenReceivedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0009_SecurityTokenReceivedNotificationSkipped:" xml:space="preserve">
|
||||
<value>OIDCH_0009: securityTokenReceivedNotification.Skipped</value>
|
||||
<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>
|
||||
<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>
|
||||
<value>OIDCH_0012: SecurityTokenValidatedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0013_SecurityTokenValidatedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0013: securityTokenValidatedNotification.Skipped</value>
|
||||
<value>OIDCH_0013: SecurityTokenValidatedNotification.Skipped</value>
|
||||
</data>
|
||||
<data name="OIDCH_0014_CodeReceived" xml:space="preserve">
|
||||
<value>OIDCH_0014: 'code' received: '{0}'</value>
|
||||
<data name="OIDCH_0014_AuthorizationCodeReceived" xml:space="preserve">
|
||||
<value>OIDCH_0014: AuthorizationCode received: '{0}'.</value>
|
||||
</data>
|
||||
<data name="OIDCH_0015_CodeReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0015: codeReceivedNotification.HandledResponse</value>
|
||||
<data name="OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse" xml:space="preserve">
|
||||
<value>OIDCH_0015: AuthorizationCodeReceivedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0016_CodeReceivedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0016: codeReceivedNotification.Skipped</value>
|
||||
<data name="OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0016: AuthorizationCodeReceivedNotification.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 name="OIDCH_0017_ExceptionOccurredWhileProcessingMessage" 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>
|
||||
<value>OIDCH_0018: AuthenticationFailedNotification.HandledResponse</value>
|
||||
</data>
|
||||
<data name="OIDCH_0019_AuthenticationNotificationFailedSkipped" xml:space="preserve">
|
||||
<value>OIDCH_0019: authenticationFailedNotification.Skipped</value>
|
||||
<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>
|
||||
<data name="OIDCH_0021_AutomaticConfigurationRefresh" xml:space="preserve">
|
||||
<value>OIDCH_0021: exception of type 'SecurityTokenSignatureKeyNotFoundException' thrown, Options.ConfigurationManager.RequestRefresh() called.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,28 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// When a user configures the <see cref="AuthenticationMiddleware{TOptions}"/> to be notified prior to redirecting to an IdentityProvider
|
||||
/// an instance of <see cref="RedirectFromIdentityProviderNotification{TMessage, TOptions, TMessage}"/> is passed to the 'RedirectToIdentityProviderNotification".
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">protocol specific message.</typeparam>
|
||||
/// <typeparam name="TOptions">protocol specific options.</typeparam>
|
||||
public class RedirectToIdentityProviderNotification<TMessage, TOptions> : BaseNotification<TOptions>
|
||||
{
|
||||
public RedirectToIdentityProviderNotification(HttpContext context, TOptions options) : base(context, options)
|
||||
public RedirectToIdentityProviderNotification([NotNull] HttpContext context, [NotNull] TOptions options) : base(context, options)
|
||||
{
|
||||
}
|
||||
|
||||
public TMessage ProtocolMessage { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="{TMessage}"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">if 'value' is null.</exception>
|
||||
public TMessage ProtocolMessage { get; [param: NotNull] set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 System;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// This formatter creates an easy to read string of the format: "'key1' 'value1' ..."
|
||||
/// </summary>
|
||||
public class AuthenticationPropertiesFormaterKeyValue : ISecureDataFormat<AuthenticationProperties>
|
||||
{
|
||||
string _protectedString = Guid.NewGuid().ToString();
|
||||
|
||||
public string Protect(AuthenticationProperties data)
|
||||
{
|
||||
if (data == null || data.Items.Count == 0)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
var encoder = UrlEncoder.Default;
|
||||
var sb = new StringBuilder();
|
||||
foreach(var item in data.Items)
|
||||
{
|
||||
sb.Append(encoder.UrlEncode(item.Key) + " " + encoder.UrlEncode(item.Value) + " ");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
AuthenticationProperties ISecureDataFormat<AuthenticationProperties>.Unprotect(string protectedText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(protectedText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (protectedText == "null")
|
||||
{
|
||||
return new AuthenticationProperties();
|
||||
}
|
||||
|
||||
string[] items = protectedText.Split(' ');
|
||||
if (items.Length % 2 != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var propeties = new AuthenticationProperties();
|
||||
for (int i = 0; i < items.Length - 1; i+=2)
|
||||
{
|
||||
propeties.Items.Add(items[i], items[i + 1]);
|
||||
}
|
||||
|
||||
return propeties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// This helper class is used to check that query string parameters are as expected.
|
||||
/// </summary>
|
||||
public class ExpectedQueryValues
|
||||
{
|
||||
public ExpectedQueryValues(string authority, OpenIdConnectConfiguration configuration = null)
|
||||
{
|
||||
Authority = authority;
|
||||
Configuration = configuration ?? TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||
}
|
||||
|
||||
public static ExpectedQueryValues Defaults(string authority)
|
||||
{
|
||||
var result = new ExpectedQueryValues(authority);
|
||||
result.Scope = OpenIdConnectScopes.OpenIdProfile;
|
||||
result.ResponseType = OpenIdConnectResponseTypes.CodeIdToken;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void CheckValues(string query, IEnumerable<string> parameters)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
if (!query.StartsWith(ExpectedAuthority))
|
||||
{
|
||||
errors.Add("ExpectedAuthority: " + ExpectedAuthority);
|
||||
}
|
||||
|
||||
foreach(var str in parameters)
|
||||
{
|
||||
if (str == OpenIdConnectParameterNames.ClientId)
|
||||
{
|
||||
if (!query.Contains(ExpectedClientId))
|
||||
errors.Add("ExpectedClientId: " + ExpectedClientId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str == OpenIdConnectParameterNames.RedirectUri)
|
||||
{
|
||||
if(!query.Contains(ExpectedRedirectUri))
|
||||
errors.Add("ExpectedRedirectUri: " + ExpectedRedirectUri);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str == OpenIdConnectParameterNames.Resource)
|
||||
{
|
||||
if(!query.Contains(ExpectedResource))
|
||||
errors.Add("ExpectedResource: " + ExpectedResource);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str == OpenIdConnectParameterNames.ResponseMode)
|
||||
{
|
||||
if(!query.Contains(ExpectedResponseMode))
|
||||
errors.Add("ExpectedResponseMode: " + ExpectedResponseMode);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str == OpenIdConnectParameterNames.Scope)
|
||||
{
|
||||
if (!query.Contains(ExpectedScope))
|
||||
errors.Add("ExpectedScope: " + ExpectedScope);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str == OpenIdConnectParameterNames.State)
|
||||
{
|
||||
if (!query.Contains(ExpectedState))
|
||||
errors.Add("ExpectedState: " + ExpectedState);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("query string not as expected: " + Environment.NewLine + query + Environment.NewLine);
|
||||
foreach (var str in errors)
|
||||
{
|
||||
sb.AppendLine(str);
|
||||
}
|
||||
|
||||
Debug.WriteLine(sb.ToString());
|
||||
Assert.True(false, sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public UrlEncoder Encoder { get; set; } = UrlEncoder.Default;
|
||||
|
||||
public string Authority { get; set; }
|
||||
|
||||
public string ClientId { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public string RedirectUri { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public OpenIdConnectRequestType RequestType { get; set; } = OpenIdConnectRequestType.AuthenticationRequest;
|
||||
|
||||
public string Resource { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public string ResponseMode { get; set; } = OpenIdConnectResponseModes.FormPost;
|
||||
|
||||
public string ResponseType { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public string Scope { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public string State { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public string ExpectedAuthority
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RequestType == OpenIdConnectRequestType.TokenRequest)
|
||||
{
|
||||
return Configuration?.EndSessionEndpoint ?? Authority + @"/oauth2/token";
|
||||
}
|
||||
else if (RequestType == OpenIdConnectRequestType.LogoutRequest)
|
||||
{
|
||||
return Configuration?.TokenEndpoint ?? Authority + @"/oauth2/logout";
|
||||
}
|
||||
|
||||
return Configuration?.AuthorizationEndpoint ?? Authority + (@"/oauth2/authorize");
|
||||
}
|
||||
}
|
||||
|
||||
public OpenIdConnectConfiguration Configuration { get; set; }
|
||||
|
||||
public string ExpectedClientId
|
||||
{
|
||||
get { return OpenIdConnectParameterNames.ClientId + "=" + Encoder.UrlEncode(ClientId); }
|
||||
}
|
||||
|
||||
public string ExpectedRedirectUri
|
||||
{
|
||||
get { return OpenIdConnectParameterNames.RedirectUri + "=" + Encoder.UrlEncode(RedirectUri); }
|
||||
}
|
||||
|
||||
public string ExpectedResource
|
||||
{
|
||||
get { return OpenIdConnectParameterNames.Resource + "=" + Encoder.UrlEncode(Resource); }
|
||||
}
|
||||
|
||||
public string ExpectedResponseMode
|
||||
{
|
||||
get { return OpenIdConnectParameterNames.ResponseMode + "=" + Encoder.UrlEncode(ResponseMode); }
|
||||
}
|
||||
|
||||
public string ExpectedScope
|
||||
{
|
||||
get { return OpenIdConnectParameterNames.Scope + "=" + Encoder.UrlEncode(Scope); }
|
||||
}
|
||||
|
||||
public string ExpectedState
|
||||
{
|
||||
get { return OpenIdConnectParameterNames.State + "=" + Encoder.UrlEncode(State); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
public class InMemoryLogger : ILogger, IDisposable
|
||||
{
|
||||
LogLevel _logLevel = 0;
|
||||
|
||||
public InMemoryLogger(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))
|
||||
{
|
||||
var logEntry =
|
||||
new LogEntry
|
||||
{
|
||||
EventId = eventId,
|
||||
Exception = exception,
|
||||
Formatter = formatter,
|
||||
Level = logLevel,
|
||||
State = state,
|
||||
};
|
||||
|
||||
_logEntries.Add(logEntry);
|
||||
Debug.WriteLine(logEntry.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public List<LogEntry> Logs { get { return _logEntries; } }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// 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.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
public class InMemoryLoggerFactory : ILoggerFactory
|
||||
{
|
||||
InMemoryLogger _logger;
|
||||
LogLevel _logLevel = LogLevel.Debug;
|
||||
|
||||
public InMemoryLoggerFactory(LogLevel logLevel)
|
||||
{
|
||||
_logLevel = logLevel;
|
||||
_logger = new InMemoryLogger(_logLevel);
|
||||
}
|
||||
|
||||
public LogLevel MinimumLevel
|
||||
{
|
||||
get { return _logLevel; }
|
||||
set { _logLevel = value; }
|
||||
}
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return _logger;
|
||||
}
|
||||
|
||||
public InMemoryLogger Logger { get { return _logger; } }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
public class LoggingUtilities
|
||||
{
|
||||
static List<LogEntry> CompleteLogEntries;
|
||||
static Dictionary<string, LogLevel> LogEntries;
|
||||
|
||||
static LoggingUtilities()
|
||||
{
|
||||
LogEntries =
|
||||
new Dictionary<string, LogLevel>()
|
||||
{
|
||||
{ "OIDCH_0000:", LogLevel.Debug },
|
||||
{ "OIDCH_0001:", LogLevel.Debug },
|
||||
{ "OIDCH_0002:", LogLevel.Verbose },
|
||||
{ "OIDCH_0003:", LogLevel.Verbose },
|
||||
{ "OIDCH_0004:", LogLevel.Warning },
|
||||
{ "OIDCH_0005:", LogLevel.Error },
|
||||
{ "OIDCH_0006:", LogLevel.Error },
|
||||
{ "OIDCH_0007:", LogLevel.Verbose },
|
||||
{ "OIDCH_0008:", LogLevel.Verbose },
|
||||
{ "OIDCH_0009:", LogLevel.Verbose },
|
||||
{ "OIDCH_0010:", LogLevel.Error },
|
||||
{ "OIDCH_0011:", LogLevel.Error },
|
||||
{ "OIDCH_0012:", LogLevel.Verbose },
|
||||
{ "OIDCH_0013:", LogLevel.Verbose },
|
||||
{ "OIDCH_0014:", LogLevel.Debug },
|
||||
{ "OIDCH_0015:", LogLevel.Verbose },
|
||||
{ "OIDCH_0016:", LogLevel.Verbose },
|
||||
{ "OIDCH_0017:", LogLevel.Error },
|
||||
{ "OIDCH_0018:", LogLevel.Verbose },
|
||||
{ "OIDCH_0019:", LogLevel.Verbose },
|
||||
{ "OIDCH_0020:", LogLevel.Debug },
|
||||
{ "OIDCH_0021:", LogLevel.Verbose },
|
||||
{ "OIDCH_0026:", LogLevel.Error },
|
||||
};
|
||||
|
||||
BuildLogEntryList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the complete list of OpenIdConnect 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>
|
||||
/// 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>
|
||||
public static void CheckLogs(List<LogEntry> capturedLogs, List<LogEntry> expectedLogs, List<Tuple<LogEntry, LogEntry>> errors)
|
||||
{
|
||||
if (capturedLogs.Count >= expectedLogs.Count)
|
||||
{
|
||||
for (int i = 0; i < capturedLogs.Count; i++)
|
||||
{
|
||||
if (i + 1 > expectedLogs.Count)
|
||||
{
|
||||
errors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], null));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TestUtilities.AreEqual<LogEntry>(capturedLogs[i], expectedLogs[i]))
|
||||
{
|
||||
errors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], expectedLogs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < expectedLogs.Count; i++)
|
||||
{
|
||||
if (i + 1 > capturedLogs.Count)
|
||||
{
|
||||
errors.Add(new Tuple<LogEntry, LogEntry>(null, expectedLogs[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TestUtilities.AreEqual<LogEntry>(expectedLogs[i], capturedLogs[i]))
|
||||
{
|
||||
errors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], expectedLogs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string LoggingErrors(List<Tuple<LogEntry, LogEntry>> errors)
|
||||
{
|
||||
string loggingErrors = null;
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
stringBuilder.AppendLine("");
|
||||
foreach (var error in errors)
|
||||
{
|
||||
stringBuilder.AppendLine("*Captured*, *Expected* : *" + (error.Item1?.ToString() ?? "null") + "*, *" + (error.Item2?.ToString() ?? "null") + "*");
|
||||
}
|
||||
|
||||
loggingErrors = stringBuilder.ToString();
|
||||
}
|
||||
|
||||
return loggingErrors;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static List<LogEntry> PopulateLogEntries(int[] items)
|
||||
{
|
||||
var entries = new List<LogEntry>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
entries.Add(CompleteLogEntries[item]);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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 System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows for custom processing of ApplyResponseChallenge, ApplyResponseGrant and AuthenticateCore
|
||||
/// </summary>
|
||||
public class OpenIdConnectAuthenticationHandlerForTestingAuthenticate : OpenIdConnectAuthenticationHandler
|
||||
{
|
||||
public OpenIdConnectAuthenticationHandlerForTestingAuthenticate()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
|
||||
{
|
||||
return await base.HandleUnauthorizedAsync(context);
|
||||
}
|
||||
|
||||
protected override Task HandleSignInAsync(SignInContext context)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
protected override Task HandleSignOutAsync(SignOutContext context)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
//public override bool ShouldHandleScheme(string authenticationScheme)
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// 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 System;
|
||||
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// pass a <see cref="OpenIdConnectAuthenticationHandler"/> as the AuthenticationHandler
|
||||
/// configured to handle certain messages.
|
||||
/// </summary>
|
||||
public class OpenIdConnectAuthenticationMiddlewareForTestingAuthenticate : OpenIdConnectAuthenticationMiddleware
|
||||
{
|
||||
OpenIdConnectAuthenticationHandler _handler;
|
||||
|
||||
public OpenIdConnectAuthenticationMiddlewareForTestingAuthenticate(
|
||||
RequestDelegate next,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IUrlEncoder encoder,
|
||||
IServiceProvider services,
|
||||
IOptions<ExternalAuthenticationOptions> externalOptions,
|
||||
IOptions<OpenIdConnectAuthenticationOptions> options,
|
||||
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions = null,
|
||||
OpenIdConnectAuthenticationHandler handler = null
|
||||
)
|
||||
: base(next, dataProtectionProvider, loggerFactory, encoder, services, externalOptions, options, configureOptions)
|
||||
{
|
||||
_handler = handler;
|
||||
var customFactory = loggerFactory as InMemoryLoggerFactory;
|
||||
if (customFactory != null)
|
||||
Logger = customFactory.Logger;
|
||||
}
|
||||
|
||||
protected override AuthenticationHandler<OpenIdConnectAuthenticationOptions> CreateHandler()
|
||||
{
|
||||
return _handler ?? base.CreateHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// 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.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IdentityModel.Tokens;
|
||||
using System.Linq;
|
||||
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.Http.Features.Authentication;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Moq;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -32,52 +30,9 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
/// </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 });
|
||||
}
|
||||
}
|
||||
private const string nonceForJwt = "abc";
|
||||
private static SecurityToken specCompliantJwt = new JwtSecurityToken("issuer", "audience", new List<Claim> { new Claim("iat", EpochTime.GetIntDate(DateTime.UtcNow).ToString()), new Claim("nonce", nonceForJwt) }, DateTime.UtcNow, DateTime.UtcNow + TimeSpan.FromDays(1));
|
||||
private const string ExpectedStateParameter = "expectedState";
|
||||
|
||||
/// <summary>
|
||||
/// Sanity check that logging is filtering, hi / low water marks are checked
|
||||
|
|
@ -85,7 +40,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
[Fact]
|
||||
public void LoggingLevel()
|
||||
{
|
||||
var logger = new CustomLogger(LogLevel.Debug);
|
||||
var logger = new InMemoryLogger(LogLevel.Debug);
|
||||
logger.IsEnabled(LogLevel.Critical).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Error).ShouldBe<bool>(true);
|
||||
|
|
@ -93,7 +48,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
logger.IsEnabled(LogLevel.Verbose).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Warning).ShouldBe<bool>(true);
|
||||
|
||||
logger = new CustomLogger(LogLevel.Critical);
|
||||
logger = new InMemoryLogger(LogLevel.Critical);
|
||||
logger.IsEnabled(LogLevel.Critical).ShouldBe<bool>(true);
|
||||
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(false);
|
||||
logger.IsEnabled(LogLevel.Error).ShouldBe<bool>(false);
|
||||
|
|
@ -102,208 +57,190 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
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()
|
||||
[Theory, MemberData("AuthenticateCoreStateDataSet")]
|
||||
public async Task AuthenticateCoreState(Action<OpenIdConnectAuthenticationOptions> action, OpenIdConnectMessage message)
|
||||
{
|
||||
//System.Diagnostics.Debugger.Launch();
|
||||
|
||||
var propertiesFormatter = new AuthenticationPropertiesFormater();
|
||||
var protectedProperties = propertiesFormatter.Protect(new AuthenticationProperties());
|
||||
var state = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + UrlEncoder.Default.UrlEncode(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);
|
||||
var handler = new OpenIdConnectAuthenticationHandlerForTestingAuthenticate();
|
||||
var server = CreateServer(new ConfigureOptions<OpenIdConnectAuthenticationOptions>(action), UrlEncoder.Default, handler);
|
||||
await server.CreateClient().PostAsync("http://localhost", new FormUrlEncodedContent(message.Parameters.Where(pair => pair.Value != null)));
|
||||
}
|
||||
|
||||
/// <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)
|
||||
public static TheoryData<Action<OpenIdConnectAuthenticationOptions>, OpenIdConnectMessage> AuthenticateCoreStateDataSet
|
||||
{
|
||||
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)
|
||||
get
|
||||
{
|
||||
entries.Add(CompleteLogEntries[item]);
|
||||
}
|
||||
var formater = new AuthenticationPropertiesFormaterKeyValue();
|
||||
var properties = new AuthenticationProperties();
|
||||
var dataset = new TheoryData<Action<OpenIdConnectAuthenticationOptions>, OpenIdConnectMessage>();
|
||||
|
||||
return entries;
|
||||
}
|
||||
// expected user state is added to the message.Parameters.Items[ExpectedStateParameter]
|
||||
// Userstate == null
|
||||
var message = new OpenIdConnectMessage();
|
||||
message.State = UrlEncoder.Default.UrlEncode(formater.Protect(properties));
|
||||
message.Code = Guid.NewGuid().ToString();
|
||||
message.Parameters.Add(ExpectedStateParameter, null);
|
||||
dataset.Add(SetStateOptions, message);
|
||||
|
||||
private void DisplayLogs(List<LogEntry> logs)
|
||||
{
|
||||
foreach (var logentry in logs)
|
||||
{
|
||||
Console.WriteLine(logentry.ToString());
|
||||
// Userstate != null
|
||||
message = new OpenIdConnectMessage();
|
||||
properties.Items.Clear();
|
||||
var userstate = Guid.NewGuid().ToString();
|
||||
message.Code = Guid.NewGuid().ToString();
|
||||
properties.Items.Add(OpenIdConnectAuthenticationDefaults.UserstatePropertiesKey, userstate);
|
||||
message.State = UrlEncoder.Default.UrlEncode(formater.Protect(properties));
|
||||
message.Parameters.Add(ExpectedStateParameter, userstate);
|
||||
dataset.Add(SetStateOptions, message);
|
||||
return dataset;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayErrors(Dictionary<string, List<Tuple<LogEntry, LogEntry>>> errors)
|
||||
// Setup a notification to check for expected state.
|
||||
// The state gets set by the runtime after the 'MessageReceivedNotification'
|
||||
private static void SetStateOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
if (errors.Count > 0)
|
||||
options.AuthenticationScheme = "OpenIdConnectHandlerTest";
|
||||
options.ConfigurationManager = TestUtilities.DefaultOpenIdConnectConfigurationManager;
|
||||
options.ClientId = Guid.NewGuid().ToString();
|
||||
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
|
||||
options.Notifications = new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
foreach (var error in errors)
|
||||
AuthorizationCodeReceived = notification =>
|
||||
{
|
||||
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);
|
||||
if (notification.ProtocolMessage.State == null && !notification.ProtocolMessage.Parameters.ContainsKey(ExpectedStateParameter))
|
||||
return Task.FromResult<object>(null);
|
||||
|
||||
if (notification.ProtocolMessage.State == null || !notification.ProtocolMessage.Parameters.ContainsKey(ExpectedStateParameter))
|
||||
Assert.True(false, "(notification.ProtocolMessage.State=!= null || !notification.ProtocolMessage.Parameters.ContainsKey(expectedState)");
|
||||
|
||||
Assert.Equal(notification.ProtocolMessage.State, notification.ProtocolMessage.Parameters[ExpectedStateParameter]);
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <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)
|
||||
[Theory, MemberData("AuthenticateCoreDataSet")]
|
||||
public async Task AuthenticateCore(LogLevel logLevel, int[] expectedLogIndexes, Action<OpenIdConnectAuthenticationOptions> action, OpenIdConnectMessage message)
|
||||
{
|
||||
var localErrors = new List<Tuple<LogEntry, LogEntry>>();
|
||||
var errors = new List<Tuple<LogEntry, LogEntry>>();
|
||||
var expectedLogs = LoggingUtilities.PopulateLogEntries(expectedLogIndexes);
|
||||
var handler = new OpenIdConnectAuthenticationHandlerForTestingAuthenticate();
|
||||
var loggerFactory = new InMemoryLoggerFactory(logLevel);
|
||||
var server = CreateServer(new ConfigureOptions<OpenIdConnectAuthenticationOptions>(action), UrlEncoder.Default, loggerFactory, handler);
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await server.CreateClient().PostAsync("http://localhost", new FormUrlEncodedContent(message.Parameters));
|
||||
LoggingUtilities.CheckLogs(loggerFactory.Logger.Logs, expectedLogs, errors);
|
||||
Debug.WriteLine(LoggingUtilities.LoggingErrors(errors));
|
||||
Assert.True(errors.Count == 0, LoggingUtilities.LoggingErrors(errors));
|
||||
}
|
||||
|
||||
if (localErrors.Count != 0)
|
||||
public static TheoryData<LogLevel, int[], Action<OpenIdConnectAuthenticationOptions>, OpenIdConnectMessage> AuthenticateCoreDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
if (errors.ContainsKey(variation))
|
||||
{
|
||||
foreach (var error in localErrors)
|
||||
{
|
||||
errors[variation].Add(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errors[variation] = localErrors;
|
||||
}
|
||||
var formater = new AuthenticationPropertiesFormaterKeyValue();
|
||||
var dataset = new TheoryData<LogLevel, int[], Action< OpenIdConnectAuthenticationOptions >, OpenIdConnectMessage>();
|
||||
var properties = new AuthenticationProperties();
|
||||
var message = new OpenIdConnectMessage();
|
||||
var validState = UrlEncoder.Default.UrlEncode(formater.Protect(properties));
|
||||
message.State = validState;
|
||||
|
||||
// MessageReceived - Handled / Skipped
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 2 }, MessageReceivedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 2 }, MessageReceivedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, MessageReceivedHandledOptions, message);
|
||||
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 3 }, MessageReceivedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 3 }, MessageReceivedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, MessageReceivedSkippedOptions, message);
|
||||
|
||||
// State - null, empty string, invalid
|
||||
message = new OpenIdConnectMessage();
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 4, 7 }, StateNullOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 4, 7 }, StateNullOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, StateNullOptions, message);
|
||||
|
||||
message = new OpenIdConnectMessage();
|
||||
message.State = string.Empty;
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 4, 7 }, StateEmptyOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 4, 7 }, StateEmptyOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, StateEmptyOptions, message);
|
||||
|
||||
message = new OpenIdConnectMessage();
|
||||
message.State = Guid.NewGuid().ToString();
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 5 }, StateInvalidOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 5 }, StateInvalidOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { 5 }, StateInvalidOptions, message);
|
||||
|
||||
// OpenIdConnectMessage.Error != null
|
||||
message = new OpenIdConnectMessage();
|
||||
message.Error = "Error";
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 4, 6, 17, 18 }, MessageWithErrorOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 4, 6, 17, 18 }, MessageWithErrorOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { 6, 17 }, MessageWithErrorOptions, message);
|
||||
|
||||
// SecurityTokenReceived - Handled / Skipped
|
||||
message = new OpenIdConnectMessage();
|
||||
message.IdToken = "invalid";
|
||||
message.State = validState;
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20, 8 }, SecurityTokenReceivedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 8 }, SecurityTokenReceivedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, SecurityTokenReceivedHandledOptions, message);
|
||||
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20, 9 }, SecurityTokenReceivedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 9 }, SecurityTokenReceivedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, SecurityTokenReceivedSkippedOptions, message);
|
||||
|
||||
// SecurityTokenValidation - ReturnsNull, Throws, Validates
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20, 11, 17, 18 }, SecurityTokenValidatorCannotReadToken, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 11, 17, 18 }, SecurityTokenValidatorCannotReadToken, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { 11, 17 }, SecurityTokenValidatorCannotReadToken, message);
|
||||
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20, 17, 21, 18 }, SecurityTokenValidatorThrows, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 17, 21, 18 }, SecurityTokenValidatorThrows, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { 17 }, SecurityTokenValidatorThrows, message);
|
||||
|
||||
message.Nonce = nonceForJwt;
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20 }, SecurityTokenValidatorValidatesAllTokens, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7 }, SecurityTokenValidatorValidatesAllTokens, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, SecurityTokenValidatorValidatesAllTokens, message);
|
||||
|
||||
// SecurityTokenValidation - Handled / Skipped
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20, 12 }, SecurityTokenValidatedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 12 }, SecurityTokenValidatedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, SecurityTokenValidatedHandledOptions, message);
|
||||
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 20, 13 }, SecurityTokenValidatedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 13 }, SecurityTokenValidatedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, SecurityTokenValidatedSkippedOptions, message);
|
||||
|
||||
// AuthenticationCodeReceived - Handled / Skipped
|
||||
message = new OpenIdConnectMessage();
|
||||
message.Code = Guid.NewGuid().ToString();
|
||||
message.State = validState;
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 14, 15 }, AuthorizationCodeReceivedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 15 }, AuthorizationCodeReceivedHandledOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, AuthorizationCodeReceivedHandledOptions, message);
|
||||
|
||||
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 7, 14, 16 }, AuthorizationCodeReceivedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Verbose, new int[] { 7, 16 }, AuthorizationCodeReceivedSkippedOptions, message);
|
||||
dataset.Add(LogLevel.Error, new int[] { }, AuthorizationCodeReceivedSkippedOptions, message);
|
||||
|
||||
return dataset;
|
||||
}
|
||||
}
|
||||
|
||||
#region Configure Options
|
||||
#region Configure Options for AuthenticateCore variations
|
||||
|
||||
private static void CodeReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
private static void DefaultOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
options.AuthenticationScheme = "OpenIdConnectHandlerTest";
|
||||
options.SignInScheme = "OpenIdConnectHandlerTest";
|
||||
options.ConfigurationManager = TestUtilities.DefaultOpenIdConnectConfigurationManager;
|
||||
options.ClientId = Guid.NewGuid().ToString();
|
||||
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
|
||||
}
|
||||
|
||||
private static void AuthorizationCodeReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
|
|
@ -317,7 +254,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
};
|
||||
}
|
||||
|
||||
private static void CodeReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
private static void AuthorizationCodeReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
|
|
@ -331,11 +268,32 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
};
|
||||
}
|
||||
|
||||
private static void DefaultOptions(OpenIdConnectAuthenticationOptions options)
|
||||
private static void AuthenticationErrorHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
options.AuthenticationScheme = "OpenIdConnectHandlerTest";
|
||||
options.ConfigurationManager = ConfigurationManager.DefaultStaticConfigurationManager;
|
||||
options.StateDataFormat = new AuthenticationPropertiesFormater();
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
AuthenticationFailed = (notification) =>
|
||||
{
|
||||
notification.HandleResponse();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void AuthenticationErrorSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
AuthenticationFailed = (notification) =>
|
||||
{
|
||||
notification.SkipToNextMiddleware();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void MessageReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
|
|
@ -366,6 +324,11 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
};
|
||||
}
|
||||
|
||||
private static void MessageWithErrorOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
AuthenticationErrorHandledOptions(options);
|
||||
}
|
||||
|
||||
private static void SecurityTokenReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
|
|
@ -394,9 +357,40 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
};
|
||||
}
|
||||
|
||||
private static void SecurityTokenValidatedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
private static void SecurityTokenValidatorCannotReadToken(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
AuthenticationErrorHandledOptions(options);
|
||||
var mockValidator = new Mock<ISecurityTokenValidator>();
|
||||
SecurityToken jwt = null;
|
||||
mockValidator.Setup(v => v.ValidateToken(It.IsAny<string>(), It.IsAny<TokenValidationParameters>(), out jwt)).Returns(new ClaimsPrincipal());
|
||||
mockValidator.Setup(v => v.CanReadToken(It.IsAny<string>())).Returns(false);
|
||||
options.SecurityTokenValidators = new Collection<ISecurityTokenValidator> { mockValidator.Object };
|
||||
}
|
||||
|
||||
private static void SecurityTokenValidatorThrows(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
AuthenticationErrorHandledOptions(options);
|
||||
var mockValidator = new Mock<ISecurityTokenValidator>();
|
||||
SecurityToken jwt = null;
|
||||
mockValidator.Setup(v => v.ValidateToken(It.IsAny<string>(), It.IsAny<TokenValidationParameters>(), out jwt)).Throws<SecurityTokenSignatureKeyNotFoundException>();
|
||||
mockValidator.Setup(v => v.CanReadToken(It.IsAny<string>())).Returns(true);
|
||||
options.SecurityTokenValidators = new Collection<ISecurityTokenValidator> { mockValidator.Object };
|
||||
}
|
||||
|
||||
private static void SecurityTokenValidatorValidatesAllTokens(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
var mockValidator = new Mock<ISecurityTokenValidator>();
|
||||
mockValidator.Setup(v => v.ValidateToken(It.IsAny<string>(), It.IsAny<TokenValidationParameters>(), out specCompliantJwt)).Returns(new ClaimsPrincipal());
|
||||
mockValidator.Setup(v => v.CanReadToken(It.IsAny<string>())).Returns(true);
|
||||
options.SecurityTokenValidators = new Collection<ISecurityTokenValidator> { mockValidator.Object };
|
||||
options.ProtocolValidator.RequireTimeStampInNonce = false;
|
||||
options.ProtocolValidator.RequireNonce = false;
|
||||
}
|
||||
|
||||
private static void SecurityTokenValidatedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
SecurityTokenValidatorValidatesAllTokens(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
|
|
@ -410,7 +404,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
|
||||
private static void SecurityTokenValidatedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
SecurityTokenValidatorValidatesAllTokens(options);
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
|
|
@ -422,14 +416,31 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
private static void StateNullOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(IOptions<OpenIdConnectAuthenticationOptions> options, ILoggerFactory loggerFactory)
|
||||
private static void StateEmptyOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
}
|
||||
|
||||
private static void StateInvalidOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
DefaultOptions(options);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static Task EmptyTask() { return Task.FromResult(0); }
|
||||
|
||||
private static TestServer CreateServer(ConfigureOptions<OpenIdConnectAuthenticationOptions> options, IUrlEncoder encoder, OpenIdConnectAuthenticationHandler handler = null)
|
||||
{
|
||||
return TestServer.Create(
|
||||
app =>
|
||||
{
|
||||
app.UseCustomOpenIdConnectAuthentication(options, loggerFactory);
|
||||
app.UseMiddleware<OpenIdConnectAuthenticationMiddlewareForTestingAuthenticate>(options, encoder, handler);
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
await next();
|
||||
|
|
@ -437,19 +448,18 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddAuthentication();
|
||||
services.AddWebEncoders();
|
||||
services.AddDataProtection();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(CustomConfigureOptions configureOptions, ILoggerFactory loggerFactory)
|
||||
private static TestServer CreateServer(ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions, IUrlEncoder encoder, ILoggerFactory loggerFactory, OpenIdConnectAuthenticationHandler handler = null)
|
||||
{
|
||||
return TestServer.Create(
|
||||
app =>
|
||||
{
|
||||
app.UseCustomOpenIdConnectAuthentication(configureOptions, loggerFactory);
|
||||
app.UseMiddleware<OpenIdConnectAuthenticationMiddlewareForTestingAuthenticate>(configureOptions, encoder, loggerFactory, handler);
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
await next();
|
||||
|
|
@ -457,274 +467,10 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
},
|
||||
services =>
|
||||
{
|
||||
services.AddAuthentication();
|
||||
services.AddWebEncoders();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 async Task BaseInitializeAsyncPublic(AuthenticationOptions options, HttpContext context, ILogger logger, IUrlEncoder encoder)
|
||||
{
|
||||
await base.BaseInitializeAsync(options, context, logger, encoder);
|
||||
}
|
||||
|
||||
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
|
||||
{
|
||||
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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,
|
||||
IUrlEncoder encoder,
|
||||
IServiceProvider services,
|
||||
IOptions<ExternalAuthenticationOptions> externalOptions,
|
||||
IOptions<OpenIdConnectAuthenticationOptions> options,
|
||||
ConfigureOptions<OpenIdConnectAuthenticationOptions> configureOptions = null
|
||||
)
|
||||
: base(next, dataProtectionProvider, loggerFactory, encoder, services, externalOptions, options, configureOptions)
|
||||
{
|
||||
Logger = (loggerFactory as CustomLoggerFactory).Logger;
|
||||
}
|
||||
|
||||
protected override AuthenticationHandler<OpenIdConnectAuthenticationOptions> CreateHandler()
|
||||
{
|
||||
return new CustomOpenIdConnectAuthenticationHandler();
|
||||
}
|
||||
}
|
||||
|
||||
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()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,26 +4,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNet.Authentication.Cookies;
|
||||
using Microsoft.AspNet.Authentication.DataHandler;
|
||||
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.WebEncoders;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Moq;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -33,25 +29,31 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
{
|
||||
static string noncePrefix = "OpenIdConnect." + "Nonce.";
|
||||
static string nonceDelimiter = ".";
|
||||
const string Challenge = "/challenge";
|
||||
const string ChallengeWithOutContext = "/challengeWithOutContext";
|
||||
const string ChallengeWithProperties = "/challengeWithProperties";
|
||||
const string DefaultHost = @"https://example.com";
|
||||
const string DefaultAuthority = @"https://example.com/common";
|
||||
const string ExpectedAuthorizeRequest = @"https://example.com/common/oauth2/signin";
|
||||
const string ExpectedLogoutRequest = @"https://example.com/common/oauth2/logout";
|
||||
const string Logout = "/logout";
|
||||
const string Signin = "/signin";
|
||||
const string Signout = "/signout";
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillTriggerRedirect()
|
||||
public async Task ChallengeWillSetDefaults()
|
||||
{
|
||||
var stateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
|
||||
var queryValues = ExpectedQueryValues.Defaults(DefaultAuthority);
|
||||
queryValues.State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + stateDataFormat.Protect(new AuthenticationProperties());
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.ClientId = "Test Id";
|
||||
options.SignInScheme = OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
|
||||
SetOptions(options, DefaultParameters(), queryValues);
|
||||
});
|
||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
||||
|
||||
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
var location = transaction.Response.Headers.Location.ToString();
|
||||
location.ShouldContain("https://login.windows.net/common/oauth2/authorize?");
|
||||
location.ShouldContain("client_id=");
|
||||
location.ShouldContain("&response_type=");
|
||||
location.ShouldContain("&scope=");
|
||||
location.ShouldContain("&state=");
|
||||
location.ShouldContain("&response_mode=");
|
||||
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, DefaultParameters());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -59,95 +61,230 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.Authority = DefaultAuthority;
|
||||
options.ClientId = "Test Id";
|
||||
options.Configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||
});
|
||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
||||
transaction.SetCookie.Single().ShouldContain("OpenIdConnect.nonce.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillSetDefaultScope()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.ClientId = "Test Id";
|
||||
});
|
||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
transaction.Response.Headers.Location.Query.ShouldContain("&scope=" + UrlEncoder.Default.UrlEncode("openid profile"));
|
||||
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||
transaction.SetCookie.Single().ShouldContain(OpenIdConnectAuthenticationDefaults.CookieNoncePrefix);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillUseOptionsProperties()
|
||||
{
|
||||
var queryValues = new ExpectedQueryValues(DefaultAuthority);
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.ClientId = "Test Id";
|
||||
options.SignInScheme = OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
|
||||
options.Scope = "https://www.googleapis.com/auth/plus.login";
|
||||
options.ResponseType = "id_token";
|
||||
SetOptions(options, DefaultParameters(), queryValues);
|
||||
});
|
||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
||||
|
||||
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
var query = transaction.Response.Headers.Location.Query;
|
||||
query.ShouldContain("scope=" + UrlEncoder.Default.UrlEncode("https://www.googleapis.com/auth/plus.login"));
|
||||
query.ShouldContain("response_type=" + UrlEncoder.Default.UrlEncode("id_token"));
|
||||
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, DefaultParameters());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests RedirectToIdentityProviderNotification replaces the OpenIdConnectMesssage correctly.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Theory]
|
||||
[InlineData(Challenge, OpenIdConnectRequestType.AuthenticationRequest)]
|
||||
[InlineData(Signout, OpenIdConnectRequestType.LogoutRequest)]
|
||||
public async Task ChallengeSettingMessage(string challenge, OpenIdConnectRequestType requestType)
|
||||
{
|
||||
var configuration = new OpenIdConnectConfiguration
|
||||
{
|
||||
AuthorizationEndpoint = ExpectedAuthorizeRequest,
|
||||
EndSessionEndpoint = ExpectedLogoutRequest
|
||||
};
|
||||
|
||||
var queryValues = new ExpectedQueryValues(DefaultAuthority, configuration)
|
||||
{
|
||||
RequestType = requestType
|
||||
};
|
||||
var server = CreateServer(SetProtocolMessageOptions);
|
||||
var transaction = await SendAsync(server, DefaultHost + challenge);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, new string[] {});
|
||||
}
|
||||
|
||||
private static void SetProtocolMessageOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
var mockOpenIdConnectMessage = new Mock<OpenIdConnectMessage>();
|
||||
mockOpenIdConnectMessage.Setup(m => m.CreateAuthenticationRequestUrl()).Returns(ExpectedAuthorizeRequest);
|
||||
mockOpenIdConnectMessage.Setup(m => m.CreateLogoutRequestUrl()).Returns(ExpectedLogoutRequest);
|
||||
options.AutomaticAuthentication = true;
|
||||
options.Notifications =
|
||||
new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
RedirectToIdentityProvider = (notification) =>
|
||||
{
|
||||
notification.ProtocolMessage = mockOpenIdConnectMessage.Object;
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for users who want to add 'state'. There are two ways to do it.
|
||||
/// 1. Users set 'state' (OpenIdConnectMessage.State) in the notification. The runtime appends to that state.
|
||||
/// 2. Users add to the AuthenticationProperties (notification.AuthenticationProperties), values will be serialized.
|
||||
/// </summary>
|
||||
/// <param name="userSetsState"></param>
|
||||
/// <returns></returns>
|
||||
[Theory, MemberData("StateDataSet")]
|
||||
public async Task ChallengeSettingState(string userState, string challenge)
|
||||
{
|
||||
var queryValues = new ExpectedQueryValues(DefaultAuthority);
|
||||
var stateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
|
||||
var properties = new AuthenticationProperties();
|
||||
if (challenge == ChallengeWithProperties)
|
||||
{
|
||||
properties.Items.Add("item1", Guid.NewGuid().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey, queryValues.RedirectUri);
|
||||
}
|
||||
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
SetOptions(options, DefaultParameters(new string[] { OpenIdConnectParameterNames.State }), queryValues, stateDataFormat);
|
||||
options.AutomaticAuthentication = challenge.Equals(ChallengeWithOutContext);
|
||||
options.Notifications = new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
RedirectToIdentityProvider = notification =>
|
||||
{
|
||||
notification.ProtocolMessage.State = userState;
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
};
|
||||
}, null, properties);
|
||||
|
||||
var transaction = await SendAsync(server, DefaultHost + challenge);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
queryValues.State = stateDataFormat.Protect(properties);
|
||||
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, DefaultParameters(new string[] { OpenIdConnectParameterNames.State }));
|
||||
}
|
||||
|
||||
public static TheoryData<string, string> StateDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
var dataset = new TheoryData<string, string>();
|
||||
dataset.Add(Guid.NewGuid().ToString(), Challenge);
|
||||
dataset.Add(null, Challenge);
|
||||
dataset.Add(Guid.NewGuid().ToString(), ChallengeWithOutContext);
|
||||
dataset.Add(null, ChallengeWithOutContext);
|
||||
dataset.Add(Guid.NewGuid().ToString(), ChallengeWithProperties);
|
||||
dataset.Add(null, ChallengeWithProperties);
|
||||
|
||||
return dataset;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillUseNotifications()
|
||||
{
|
||||
ISecureDataFormat<AuthenticationProperties> stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider().CreateProtector("GoogleTest"));
|
||||
var queryValues = new ExpectedQueryValues(DefaultAuthority);
|
||||
var queryValuesSetInNotification = new ExpectedQueryValues(DefaultAuthority);
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.ClientId = "Test Id";
|
||||
SetOptions(options, DefaultParameters(), queryValues);
|
||||
options.Notifications = new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
MessageReceived = notification =>
|
||||
{
|
||||
notification.ProtocolMessage.Scope = "test openid profile";
|
||||
notification.HandleResponse();
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
RedirectToIdentityProvider = notification =>
|
||||
{
|
||||
notification.ProtocolMessage.ClientId = queryValuesSetInNotification.ClientId;
|
||||
notification.ProtocolMessage.RedirectUri = queryValuesSetInNotification.RedirectUri;
|
||||
notification.ProtocolMessage.Resource = queryValuesSetInNotification.Resource;
|
||||
notification.ProtocolMessage.Scope = queryValuesSetInNotification.Scope;
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var properties = new AuthenticationProperties();
|
||||
var state = stateFormat.Protect(properties);
|
||||
var transaction = await SendAsync(server,"https://example.com/challenge");
|
||||
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
queryValuesSetInNotification.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, DefaultParameters());
|
||||
}
|
||||
|
||||
private void SetOptions(OpenIdConnectAuthenticationOptions options, List<string> parameters, ExpectedQueryValues queryValues, ISecureDataFormat<AuthenticationProperties> secureDataFormat = null)
|
||||
{
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
if (param.Equals(OpenIdConnectParameterNames.ClientId))
|
||||
options.ClientId = queryValues.ClientId;
|
||||
else if (param.Equals(OpenIdConnectParameterNames.RedirectUri))
|
||||
options.RedirectUri = queryValues.RedirectUri;
|
||||
else if (param.Equals(OpenIdConnectParameterNames.Resource))
|
||||
options.Resource = queryValues.Resource;
|
||||
else if (param.Equals(OpenIdConnectParameterNames.Scope))
|
||||
options.Scope = queryValues.Scope;
|
||||
}
|
||||
|
||||
options.Authority = queryValues.Authority;
|
||||
options.Configuration = queryValues.Configuration;
|
||||
options.StateDataFormat = secureDataFormat ?? new AuthenticationPropertiesFormaterKeyValue();
|
||||
}
|
||||
|
||||
private List<string> DefaultParameters(string[] additionalParams = null)
|
||||
{
|
||||
var parameters =
|
||||
new List<string>
|
||||
{
|
||||
OpenIdConnectParameterNames.ClientId,
|
||||
OpenIdConnectParameterNames.RedirectUri,
|
||||
OpenIdConnectParameterNames.Resource,
|
||||
OpenIdConnectParameterNames.ResponseMode,
|
||||
OpenIdConnectParameterNames.Scope,
|
||||
};
|
||||
|
||||
if (additionalParams != null)
|
||||
parameters.AddRange(additionalParams);
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static void DefaultChallengeOptions(OpenIdConnectAuthenticationOptions options)
|
||||
{
|
||||
options.AuthenticationScheme = "OpenIdConnectHandlerTest";
|
||||
options.AutomaticAuthentication = true;
|
||||
options.ClientId = Guid.NewGuid().ToString();
|
||||
options.ConfigurationManager = TestUtilities.DefaultOpenIdConnectConfigurationManager;
|
||||
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutWithDefaultRedirectUri()
|
||||
{
|
||||
var configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.Authority = DefaultAuthority;
|
||||
options.ClientId = "Test Id";
|
||||
options.Configuration = configuration;
|
||||
});
|
||||
|
||||
var transaction = await SendAsync(server, "https://example.com/signout");
|
||||
var transaction = await SendAsync(server, DefaultHost + Signout);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
transaction.Response.Headers.Location.AbsoluteUri.ShouldBe("https://login.windows.net/common/oauth2/logout");
|
||||
transaction.Response.Headers.Location.AbsoluteUri.ShouldBe(configuration.EndSessionEndpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutWithCustomRedirectUri()
|
||||
{
|
||||
var configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.Authority = DefaultAuthority;
|
||||
options.ClientId = "Test Id";
|
||||
options.Configuration = configuration;
|
||||
options.PostLogoutRedirectUri = "https://example.com/logout";
|
||||
});
|
||||
|
||||
var transaction = await SendAsync(server, "https://example.com/signout");
|
||||
var transaction = await SendAsync(server, DefaultHost + Signout);
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
transaction.Response.Headers.Location.AbsoluteUri.ShouldContain(UrlEncoder.Default.UrlEncode("https://example.com/logout"));
|
||||
}
|
||||
|
|
@ -155,10 +292,12 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
[Fact]
|
||||
public async Task SignOutWith_Specific_RedirectUri_From_Authentication_Properites()
|
||||
{
|
||||
var configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/common";
|
||||
options.Authority = DefaultAuthority;
|
||||
options.ClientId = "Test Id";
|
||||
options.Configuration = configuration;
|
||||
options.PostLogoutRedirectUri = "https://example.com/logout";
|
||||
});
|
||||
|
||||
|
|
@ -167,30 +306,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
transaction.Response.Headers.Location.AbsoluteUri.ShouldContain(UrlEncoder.Default.UrlEncode("http://www.example.com/specific_redirect_uri"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
// Test Cases for calculating the expiration time of cookie from cookie name
|
||||
public void NonceCookieExpirationTime()
|
||||
{
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + DateTime.MaxValue.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MaxValue);
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + DateTime.MinValue.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue + TimeSpan.FromHours(1));
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(utcNow + TimeSpan.FromHours(1));
|
||||
|
||||
GetNonceExpirationTime(noncePrefix, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
|
||||
GetNonceExpirationTime("", TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + noncePrefix, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(utcNow + TimeSpan.FromHours(1));
|
||||
|
||||
GetNonceExpirationTime(utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(Action<OpenIdConnectAuthenticationOptions> configureOptions, Func<HttpContext, Task> handler = null)
|
||||
private static TestServer CreateServer(Action<OpenIdConnectAuthenticationOptions> configureOptions, Func<HttpContext, Task> handler = null, AuthenticationProperties properties = null)
|
||||
{
|
||||
return TestServer.Create(app =>
|
||||
{
|
||||
|
|
@ -203,15 +319,25 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
{
|
||||
var req = context.Request;
|
||||
var res = context.Response;
|
||||
if (req.Path == new PathString("/challenge"))
|
||||
|
||||
if (req.Path == new PathString(Challenge))
|
||||
{
|
||||
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
else if (req.Path == new PathString("/signin"))
|
||||
else if (req.Path == new PathString(ChallengeWithProperties))
|
||||
{
|
||||
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, properties);
|
||||
}
|
||||
else if (req.Path == new PathString(ChallengeWithOutContext))
|
||||
{
|
||||
res.StatusCode = 401;
|
||||
}
|
||||
else if (req.Path == new PathString(Signin))
|
||||
{
|
||||
// REVIEW: this used to just be res.SignIn()
|
||||
await context.Authentication.SignInAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal());
|
||||
}
|
||||
else if (req.Path == new PathString("/signout"))
|
||||
else if (req.Path == new PathString(Signout))
|
||||
{
|
||||
await context.Authentication.SignOutAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
|
@ -248,34 +374,39 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
{
|
||||
request.Headers.Add("Cookie", cookieHeader);
|
||||
}
|
||||
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Request = request,
|
||||
Response = await server.CreateClient().SendAsync(request),
|
||||
};
|
||||
|
||||
if (transaction.Response.Headers.Contains("Set-Cookie"))
|
||||
{
|
||||
transaction.SetCookie = transaction.Response.Headers.GetValues("Set-Cookie").ToList();
|
||||
}
|
||||
transaction.ResponseText = await transaction.Response.Content.ReadAsStringAsync();
|
||||
|
||||
transaction.ResponseText = await transaction.Response.Content.ReadAsStringAsync();
|
||||
if (transaction.Response.Content != null &&
|
||||
transaction.Response.Content.Headers.ContentType != null &&
|
||||
transaction.Response.Content.Headers.ContentType.MediaType == "text/xml")
|
||||
{
|
||||
transaction.ResponseElement = XElement.Parse(transaction.ResponseText);
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private class Transaction
|
||||
{
|
||||
public HttpRequestMessage Request { get; set; }
|
||||
|
||||
public HttpResponseMessage Response { get; set; }
|
||||
|
||||
public IList<string> SetCookie { get; set; }
|
||||
|
||||
public string ResponseText { get; set; }
|
||||
|
||||
public XElement ResponseElement { get; set; }
|
||||
|
||||
public string AuthenticationCookieValue
|
||||
|
|
@ -294,57 +425,29 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string FindClaimValue(string claimType)
|
||||
{
|
||||
XElement claim = ResponseElement.Elements("claim").SingleOrDefault(elt => elt.Attribute("type").Value == claimType);
|
||||
if (claim == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return claim.Attribute("value").Value;
|
||||
}
|
||||
}
|
||||
private static void Describe(HttpResponse res, ClaimsIdentity identity)
|
||||
{
|
||||
res.StatusCode = 200;
|
||||
res.ContentType = "text/xml";
|
||||
var xml = new XElement("xml");
|
||||
if (identity != null)
|
||||
{
|
||||
xml.Add(identity.Claims.Select(claim => new XElement("claim", new XAttribute("type", claim.Type), new XAttribute("value", claim.Value))));
|
||||
}
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
using (var writer = new XmlTextWriter(memory, Encoding.UTF8))
|
||||
{
|
||||
xml.WriteTo(writer);
|
||||
}
|
||||
res.Body.Write(memory.ToArray(), 0, memory.ToArray().Length);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestHttpMessageHandler : HttpMessageHandler
|
||||
[Fact]
|
||||
// Test Cases for calculating the expiration time of cookie from cookie name
|
||||
public void NonceCookieExpirationTime()
|
||||
{
|
||||
public Func<HttpRequestMessage, HttpResponseMessage> Sender { get; set; }
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
if (Sender != null)
|
||||
{
|
||||
return Task.FromResult(Sender(request));
|
||||
}
|
||||
GetNonceExpirationTime(noncePrefix + DateTime.MaxValue.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MaxValue);
|
||||
|
||||
return Task.FromResult<HttpResponseMessage>(null);
|
||||
}
|
||||
}
|
||||
GetNonceExpirationTime(noncePrefix + DateTime.MinValue.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue + TimeSpan.FromHours(1));
|
||||
|
||||
private static HttpResponseMessage ReturnJsonResponse(object content)
|
||||
{
|
||||
var res = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
var text = JsonConvert.SerializeObject(content);
|
||||
res.Content = new StringContent(text, Encoding.UTF8, "application/json");
|
||||
return res;
|
||||
GetNonceExpirationTime(noncePrefix + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(utcNow + TimeSpan.FromHours(1));
|
||||
|
||||
GetNonceExpirationTime(noncePrefix, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
|
||||
GetNonceExpirationTime("", TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + noncePrefix, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
|
||||
GetNonceExpirationTime(noncePrefix + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(utcNow + TimeSpan.FromHours(1));
|
||||
|
||||
GetNonceExpirationTime(utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||
}
|
||||
|
||||
private static DateTime GetNonceExpirationTime(string keyname, TimeSpan nonceLifetime)
|
||||
|
|
@ -373,8 +476,8 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nonceTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||
{
|
||||
|
|
@ -10,6 +11,8 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
/// </summary>
|
||||
public class TestUtilities
|
||||
{
|
||||
public const string DefaultHost = @"http://localhost";
|
||||
|
||||
public static bool AreEqual<T>(object obj1, object obj2, Func<object, object, bool> comparer = null) where T : class
|
||||
{
|
||||
if (obj1 == null && obj2 == null)
|
||||
|
|
@ -63,11 +66,6 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!AreEqual<Exception>(logEntry1.Exception, logEntry2.Exception))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logEntry1.State == null && logEntry2.State == null)
|
||||
{
|
||||
return true;
|
||||
|
|
@ -104,5 +102,26 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
|
||||
return AreEqual<Exception>(exception1.InnerException, exception2.InnerException);
|
||||
}
|
||||
|
||||
static public IConfigurationManager<OpenIdConnectConfiguration> DefaultOpenIdConnectConfigurationManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return new StaticConfigurationManager<OpenIdConnectConfiguration>(DefaultOpenIdConnectConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
static public OpenIdConnectConfiguration DefaultOpenIdConnectConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
return new OpenIdConnectConfiguration()
|
||||
{
|
||||
AuthorizationEndpoint = @"https://login.windows.net/common/oauth2/authorize",
|
||||
EndSessionEndpoint = @"https://login.windows.net/common/oauth2/endsessionendpoint",
|
||||
TokenEndpoint = @"https://login.windows.net/common/oauth2/token",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue