#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
|
public static class OpenIdConnectAuthenticationDefaults
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default value used for OpenIdConnectAuthenticationOptions.AuthenticationScheme
|
/// Constant used to identify state in openIdConnect protocol message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string AuthenticationScheme = "OpenIdConnect";
|
public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The prefix used to provide a default OpenIdConnectAuthenticationOptions.CookieName
|
/// The default value used for OpenIdConnectAuthenticationOptions.AuthenticationScheme.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string CookiePrefix = ".AspNet.OpenIdConnect.";
|
public const string AuthenticationScheme = "OpenIdConnect";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default value for OpenIdConnectAuthenticationOptions.Caption.
|
/// The default value for OpenIdConnectAuthenticationOptions.Caption.
|
||||||
|
|
@ -24,18 +24,23 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
public const string Caption = "OpenIdConnect";
|
public const string Caption = "OpenIdConnect";
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
public const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The property for the RedirectUri that was used when asking for a 'authorizationCode'
|
/// The property for the RedirectUri that was used when asking for a 'authorizationCode'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string RedirectUriUsedForCodeKey = "OpenIdConnect.Code.RedirectUri";
|
public const string RedirectUriForCodePropertiesKey = "OpenIdConnect.Code.RedirectUri";
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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 const string UriSchemeDelimiter = "://";
|
||||||
private OpenIdConnectConfiguration _configuration;
|
private OpenIdConnectConfiguration _configuration;
|
||||||
|
|
||||||
private string CurrentUri
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Request.Scheme +
|
|
||||||
UriSchemeDelimiter +
|
|
||||||
Request.Host +
|
|
||||||
Request.PathBase +
|
|
||||||
Request.Path +
|
|
||||||
Request.QueryString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles Signout
|
/// Handles Signout
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -54,7 +41,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
var openIdConnectMessage = new OpenIdConnectMessage()
|
var message = new OpenIdConnectMessage()
|
||||||
{
|
{
|
||||||
IssuerAddress = _configuration == null ? string.Empty : (_configuration.EndSessionEndpoint ?? string.Empty),
|
IssuerAddress = _configuration == null ? string.Empty : (_configuration.EndSessionEndpoint ?? string.Empty),
|
||||||
RequestType = OpenIdConnectRequestType.LogoutRequest,
|
RequestType = OpenIdConnectRequestType.LogoutRequest,
|
||||||
|
|
@ -66,30 +53,42 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
var properties = new AuthenticationProperties(signout.Properties);
|
var properties = new AuthenticationProperties(signout.Properties);
|
||||||
if (!string.IsNullOrEmpty(properties.RedirectUri))
|
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
|
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||||
};
|
|
||||||
|
|
||||||
await Options.Notifications.RedirectToIdentityProvider(notification);
|
|
||||||
|
|
||||||
if (!notification.HandledResponse)
|
|
||||||
{
|
|
||||||
var redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
|
|
||||||
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
|
||||||
{
|
{
|
||||||
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)
|
// 2. CurrentUri if Options.DefaultToCurrentUriOnRedirect is true)
|
||||||
AuthenticationProperties properties = new AuthenticationProperties(context.Properties);
|
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);
|
Logger.LogDebug(Resources.OIDCH_0030_Using_Properties_RedirectUri, properties.RedirectUri);
|
||||||
}
|
}
|
||||||
|
|
@ -117,15 +116,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
properties.RedirectUri = CurrentUri;
|
properties.RedirectUri = CurrentUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
|
if (!string.IsNullOrEmpty(Options.RedirectUri))
|
||||||
{
|
{
|
||||||
Logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri);
|
Logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When redeeming a 'code' for an AccessToken, this value is needed
|
// 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)
|
if (_configuration == null && Options.ConfigurationManager != null)
|
||||||
|
|
@ -138,13 +137,12 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
ClientId = Options.ClientId,
|
ClientId = Options.ClientId,
|
||||||
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
|
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
|
||||||
RedirectUri = Options.RedirectUri,
|
RedirectUri = Options.RedirectUri,
|
||||||
// [brentschmaltz] - this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
|
// [brentschmaltz] - #215 this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
|
||||||
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
|
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
|
||||||
Resource = Options.Resource,
|
Resource = Options.Resource,
|
||||||
ResponseMode = Options.ResponseMode,
|
ResponseMode = Options.ResponseMode,
|
||||||
ResponseType = Options.ResponseType,
|
ResponseType = Options.ResponseType,
|
||||||
Scope = Options.Scope,
|
Scope = Options.Scope
|
||||||
State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + UrlEncoder.UrlEncode(Options.StateDataFormat.Protect(properties))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Options.ProtocolValidator.RequireNonce)
|
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);
|
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||||
if (redirectToIdentityProviderNotification.HandledResponse)
|
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||||
return true; // REVIEW: Make sure this should stop all other handlers
|
return true;
|
||||||
}
|
}
|
||||||
else if (redirectToIdentityProviderNotification.Skipped)
|
else if (redirectToIdentityProviderNotification.Skipped)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
Logger.LogVerbose(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
||||||
return false; // REVIEW: Make sure this should not stop all other handlers
|
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))
|
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
|
||||||
{
|
{
|
||||||
Logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
|
Logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
|
||||||
|
|
@ -246,66 +257,74 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
await Options.Notifications.MessageReceived(messageReceivedNotification);
|
await Options.Notifications.MessageReceived(messageReceivedNotification);
|
||||||
if (messageReceivedNotification.HandledResponse)
|
if (messageReceivedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
|
Logger.LogVerbose(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
|
||||||
return messageReceivedNotification.AuthenticationTicket;
|
return messageReceivedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageReceivedNotification.Skipped)
|
if (messageReceivedNotification.Skipped)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
|
Logger.LogVerbose(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we should process.
|
var properties = new AuthenticationProperties();
|
||||||
if (string.IsNullOrWhiteSpace(message.State))
|
|
||||||
|
// if state is missing, just log it
|
||||||
|
if (string.IsNullOrEmpty(message.State))
|
||||||
{
|
{
|
||||||
Logger.LogError(Resources.OIDCH_0004_MessageStateIsNullOrWhiteSpace);
|
Logger.LogWarning(Resources.OIDCH_0004_MessageStateIsNullOrEmpty);
|
||||||
return null;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if state exists and we failed to 'unprotect' this is not a message we should process.
|
||||||
|
properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(message.State));
|
||||||
|
if (properties == null)
|
||||||
|
{
|
||||||
|
Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string userstate = null;
|
||||||
|
properties.Items.TryGetValue(OpenIdConnectAuthenticationDefaults.UserstatePropertiesKey, out userstate);
|
||||||
|
message.State = userstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
var properties = GetPropertiesFromState(message.State);
|
// if any of the error fields are set, throw error null
|
||||||
if (properties == null)
|
if (!string.IsNullOrEmpty(message.Error))
|
||||||
{
|
{
|
||||||
Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
|
Logger.LogError(Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null");
|
||||||
return 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 (_configuration == null && Options.ConfigurationManager != null)
|
||||||
if (!string.IsNullOrWhiteSpace(message.Error))
|
|
||||||
{
|
{
|
||||||
Logger.LogError(Resources.OIDCH_0006_MessageErrorNotNull, message.Error);
|
Logger.LogVerbose(Resources.OIDCH_0007_UpdatingConfiguration);
|
||||||
throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageErrorNotNull, message.Error));
|
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationTicket ticket = null;
|
AuthenticationTicket ticket = null;
|
||||||
JwtSecurityToken jwt = 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
|
// 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);
|
Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
|
||||||
var securityTokenReceivedNotification =
|
var securityTokenReceivedNotification =
|
||||||
new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
new SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||||
{
|
{
|
||||||
ProtocolMessage = message
|
ProtocolMessage = message,
|
||||||
};
|
};
|
||||||
|
|
||||||
await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
|
await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
|
||||||
if (securityTokenReceivedNotification.HandledResponse)
|
if (securityTokenReceivedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
|
Logger.LogVerbose(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
|
||||||
return securityTokenReceivedNotification.AuthenticationTicket;
|
return securityTokenReceivedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (securityTokenReceivedNotification.Skipped)
|
if (securityTokenReceivedNotification.Skipped)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
|
Logger.LogVerbose(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,11 +332,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
var validationParameters = Options.TokenValidationParameters.Clone();
|
var validationParameters = Options.TokenValidationParameters.Clone();
|
||||||
if (_configuration != null)
|
if (_configuration != null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
|
if (string.IsNullOrEmpty(validationParameters.ValidIssuer))
|
||||||
{
|
{
|
||||||
validationParameters.ValidIssuer = _configuration.Issuer;
|
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 };
|
validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(new[] { _configuration.Issuer }) ?? new[] { _configuration.Issuer };
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +355,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
if (jwt == null)
|
if (jwt == null)
|
||||||
{
|
{
|
||||||
Logger.LogError(Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType());
|
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)
|
if (validatedToken == null)
|
||||||
{
|
{
|
||||||
Logger.LogError(Resources.OIDCH_0011_UnableToValidateToken, message.IdToken);
|
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);
|
ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
|
||||||
if (!string.IsNullOrWhiteSpace(message.SessionState))
|
if (!string.IsNullOrEmpty(message.SessionState))
|
||||||
{
|
{
|
||||||
ticket.Properties.Items[OpenIdConnectSessionProperties.SessionState] = 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;
|
ticket.Properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
|
||||||
}
|
}
|
||||||
|
|
@ -384,13 +403,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
|
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
|
||||||
if (securityTokenValidatedNotification.HandledResponse)
|
if (securityTokenValidatedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
|
Logger.LogVerbose(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
|
||||||
return securityTokenValidatedNotification.AuthenticationTicket;
|
return securityTokenValidatedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (securityTokenValidatedNotification.Skipped)
|
if (securityTokenValidatedNotification.Skipped)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
|
Logger.LogVerbose(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,7 +435,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
var protocolValidationContext = new OpenIdConnectProtocolValidationContext
|
var protocolValidationContext = new OpenIdConnectProtocolValidationContext
|
||||||
{
|
{
|
||||||
AuthorizationCode = message.Code,
|
AuthorizationCode = message.Code,
|
||||||
Nonce = nonce,
|
Nonce = nonce,
|
||||||
};
|
};
|
||||||
|
|
||||||
Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
|
Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
|
||||||
|
|
@ -424,7 +443,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
|
|
||||||
if (message.Code != null)
|
if (message.Code != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code);
|
Logger.LogDebug(Resources.OIDCH_0014_AuthorizationCodeReceived, message.Code);
|
||||||
if (ticket == null)
|
if (ticket == null)
|
||||||
{
|
{
|
||||||
ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||||
|
|
@ -436,20 +455,20 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
Code = message.Code,
|
Code = message.Code,
|
||||||
JwtSecurityToken = jwt,
|
JwtSecurityToken = jwt,
|
||||||
ProtocolMessage = message,
|
ProtocolMessage = message,
|
||||||
RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
|
RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey) ?
|
||||||
ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
|
ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey] : string.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
|
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
|
||||||
if (authorizationCodeReceivedNotification.HandledResponse)
|
if (authorizationCodeReceivedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse);
|
Logger.LogVerbose(Resources.OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse);
|
||||||
return authorizationCodeReceivedNotification.AuthenticationTicket;
|
return authorizationCodeReceivedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorizationCodeReceivedNotification.Skipped)
|
if (authorizationCodeReceivedNotification.Skipped)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped);
|
Logger.LogVerbose(Resources.OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped);
|
||||||
return null;
|
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.
|
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
|
||||||
if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
|
if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
|
||||||
{
|
{
|
||||||
Options.ConfigurationManager.RequestRefresh();
|
if (Options.ConfigurationManager != null)
|
||||||
|
{
|
||||||
|
Logger.LogVerbose(Resources.OIDCH_0021_AutomaticConfigurationRefresh);
|
||||||
|
Options.ConfigurationManager.RequestRefresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var authenticationFailedNotification =
|
var authenticationFailedNotification =
|
||||||
|
|
@ -476,13 +499,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
|
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
|
||||||
if (authenticationFailedNotification.HandledResponse)
|
if (authenticationFailedNotification.HandledResponse)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
|
Logger.LogVerbose(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
|
||||||
return authenticationFailedNotification.AuthenticationTicket;
|
return authenticationFailedNotification.AuthenticationTicket;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authenticationFailedNotification.Skipped)
|
if (authenticationFailedNotification.Skipped)
|
||||||
{
|
{
|
||||||
Logger.LogInformation(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
|
Logger.LogVerbose(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0027: converted 401 to 403.
|
/// OIDCH_0027: Converted 401 to 403.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0027_401_ConvertedTo_403
|
internal static string OIDCH_0027_401_ConvertedTo_403
|
||||||
{
|
{
|
||||||
|
|
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0030: using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
|
/// OIDCH_0030: Using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0030_Using_Properties_RedirectUri
|
internal static string OIDCH_0030_Using_Properties_RedirectUri
|
||||||
{
|
{
|
||||||
|
|
@ -125,7 +125,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0031: using Options.RedirectUri for 'redirect_uri': '{0}'.
|
/// OIDCH_0031: Using Options.RedirectUri for 'redirect_uri': '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0031_Using_Options_RedirectUri
|
internal static string OIDCH_0031_Using_Options_RedirectUri
|
||||||
{
|
{
|
||||||
|
|
@ -133,7 +133,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.
|
/// OIDCH_0032: Using the CurrentUri for 'local redirect' post authentication: '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0032_UsingCurrentUriRedirectUri
|
internal static string OIDCH_0032_UsingCurrentUriRedirectUri
|
||||||
{
|
{
|
||||||
|
|
@ -145,11 +145,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0033_NonceAlreadyExists
|
internal static string OIDCH_0033_NonceAlreadyExists
|
||||||
{
|
{
|
||||||
get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); }
|
get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse
|
/// OIDCH_0034: RedirectToIdentityProviderNotification.HandledResponse
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse
|
internal static string OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse
|
||||||
{
|
{
|
||||||
|
|
@ -157,7 +157,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0035: redirectToIdentityProviderNotification.Skipped
|
/// OIDCH_0035: RedirectToIdentityProviderNotification.Skipped
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0035_RedirectToIdentityProviderNotificationSkipped
|
internal static string OIDCH_0035_RedirectToIdentityProviderNotificationSkipped
|
||||||
{
|
{
|
||||||
|
|
@ -165,13 +165,21 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
internal static string OIDCH_0036_UriIsNotWellFormed
|
internal static string OIDCH_0036_UriIsNotWellFormed
|
||||||
{
|
{
|
||||||
get { return ResourceManager.GetString("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>
|
/// <summary>
|
||||||
/// OIDCH_0000: Entering: '{0}'.
|
/// OIDCH_0000: Entering: '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -197,7 +205,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0002: messageReceivedNotification.HandledResponse
|
/// OIDCH_0002: MessageReceivedNotification.HandledResponse
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0002_MessageReceivedNotificationHandledResponse
|
internal static string OIDCH_0002_MessageReceivedNotificationHandledResponse
|
||||||
{
|
{
|
||||||
|
|
@ -205,7 +213,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0003: messageReceivedNotification.Skipped
|
/// OIDCH_0003: MessageReceivedNotification.Skipped
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0003_MessageReceivedNotificationSkipped
|
internal static string OIDCH_0003_MessageReceivedNotificationSkipped
|
||||||
{
|
{
|
||||||
|
|
@ -213,15 +221,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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>
|
/// <summary>
|
||||||
/// OIDCH_0005: unable to unprotect the message.State
|
/// OIDCH_0005: Unable to unprotect the message.State
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0005_MessageStateIsInvalid
|
internal static string OIDCH_0005_MessageStateIsInvalid
|
||||||
{
|
{
|
||||||
|
|
@ -229,15 +237,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0006_MessageErrorNotNull: '{0}'.
|
/// OIDCH_0006: Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// OIDCH_0007: updating configuration
|
/// OIDCH_0007: Updating configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0007_UpdatingConfiguration
|
internal static string OIDCH_0007_UpdatingConfiguration
|
||||||
{
|
{
|
||||||
|
|
@ -245,7 +253,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0008: securityTokenReceivedNotification.HandledResponse
|
/// OIDCH_0008: SecurityTokenReceivedNotification.HandledResponse
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse
|
internal static string OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse
|
||||||
{
|
{
|
||||||
|
|
@ -253,7 +261,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0009: securityTokenReceivedNotification.Skipped
|
/// OIDCH_0009: SecurityTokenReceivedNotification.Skipped
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0009_SecurityTokenReceivedNotificationSkipped
|
internal static string OIDCH_0009_SecurityTokenReceivedNotificationSkipped
|
||||||
{
|
{
|
||||||
|
|
@ -269,7 +277,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
internal static string OIDCH_0011_UnableToValidateToken
|
internal static string OIDCH_0011_UnableToValidateToken
|
||||||
{
|
{
|
||||||
|
|
@ -277,7 +285,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0012: securityTokenValidatedNotification.HandledResponse
|
/// OIDCH_0012: SecurityTokenValidatedNotification.HandledResponse
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse
|
internal static string OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse
|
||||||
{
|
{
|
||||||
|
|
@ -285,7 +293,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0013: securityTokenValidatedNotification.Skipped
|
/// OIDCH_0013: SecurityTokenValidatedNotification.Skipped
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0013_SecurityTokenValidatedNotificationSkipped
|
internal static string OIDCH_0013_SecurityTokenValidatedNotificationSkipped
|
||||||
{
|
{
|
||||||
|
|
@ -293,31 +301,31 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0014: 'code' received: '{0}'
|
/// OIDCH_0014: AuthorizationCode received: '{0}'
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// OIDCH_0015: codeReceivedNotification.HandledResponse")
|
/// OIDCH_0015: AuthorizationCodeReceivedNotification.HandledResponse
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// OIDCH_0016: codeReceivedNotification.Skipped
|
/// OIDCH_0016: codeReceivedNotification.Skipped
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// OIDCH_0017: Exception occurred while processing message
|
/// OIDCH_0017: Exception occurred while processing message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0017_ExceptionOccurredWhileProcessingMessage
|
internal static string OIDCH_0017_ExceptionOccurredWhileProcessingMessage
|
||||||
{
|
{
|
||||||
|
|
@ -341,11 +349,20 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OIDCH_0020: 'id_token' received: '{0}'
|
/// OIDCH_0020: 'id_token' received: '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string OIDCH_0020_IdTokenReceived
|
internal static string OIDCH_0020_IdTokenReceived
|
||||||
{
|
{
|
||||||
get { return ResourceManager.GetString("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>
|
<value>OIDCH_0026: Entering: '{0}'</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0027_401_ConvertedTo_403" xml:space="preserve">
|
<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>
|
||||||
<data name="OIDCH_0028_StatusCodeNot401" xml:space="preserve">
|
<data name="OIDCH_0028_StatusCodeNot401" xml:space="preserve">
|
||||||
<value>OIDCH_0028: Response.StatusCode != 401, StatusCode: '{0}'.</value>
|
<value>OIDCH_0028: Response.StatusCode != 401, StatusCode: '{0}'.</value>
|
||||||
|
|
@ -139,10 +139,10 @@
|
||||||
<value>OIDCH_0029: ChallengeContext == null AND !Options.AutomaticAuthentication</value>
|
<value>OIDCH_0029: ChallengeContext == null AND !Options.AutomaticAuthentication</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0030_Using_Properties_RedirectUri" xml:space="preserve">
|
<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>
|
||||||
<data name="OIDCH_0031_Using_Options_RedirectUri" xml:space="preserve">
|
<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>
|
||||||
<data name="OIDCH_0032_UsingCurrentUriRedirectUri" xml:space="preserve">
|
<data name="OIDCH_0032_UsingCurrentUriRedirectUri" xml:space="preserve">
|
||||||
<value>OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.</value>
|
<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>
|
<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>
|
||||||
<data name="OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse" xml:space="preserve">
|
<data name="OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse" xml:space="preserve">
|
||||||
<value>OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse</value>
|
<value>OIDCH_0034: RedirectToIdentityProviderNotification.HandledResponse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0035_RedirectToIdentityProviderNotificationSkipped" xml:space="preserve">
|
<data name="OIDCH_0035_RedirectToIdentityProviderNotificationSkipped" xml:space="preserve">
|
||||||
<value>OIDCH_0035: redirectToIdentityProviderNotification.Skipped</value>
|
<value>OIDCH_0035: RedirectToIdentityProviderNotification.Skipped</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0036_UriIsNotWellFormed" xml:space="preserve">
|
<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>
|
||||||
<data name="OIDCH_0000_AuthenticateCoreAsync" xml:space="preserve">
|
<data name="OIDCH_0000_AuthenticateCoreAsync" xml:space="preserve">
|
||||||
<value>OIDCH_0000: Entering: '{0}'.</value>
|
<value>OIDCH_0000: Entering: '{0}'.</value>
|
||||||
|
|
@ -166,60 +169,63 @@
|
||||||
<value>OIDCH_0001: MessageReceived: '{0}'.</value>
|
<value>OIDCH_0001: MessageReceived: '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0002_MessageReceivedNotificationHandledResponse" xml:space="preserve">
|
<data name="OIDCH_0002_MessageReceivedNotificationHandledResponse" xml:space="preserve">
|
||||||
<value>OIDCH_0002: messageReceivedNotification.HandledResponse</value>
|
<value>OIDCH_0002: MessageReceivedNotification.HandledResponse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0003_MessageReceivedNotificationSkipped" xml:space="preserve">
|
<data name="OIDCH_0003_MessageReceivedNotificationSkipped" xml:space="preserve">
|
||||||
<value>OIDCH_0003: messageReceivedNotification.Skipped</value>
|
<value>OIDCH_0003: MessageReceivedNotification.Skipped</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0004_MessageStateIsNullOrWhiteSpace" xml:space="preserve">
|
<data name="OIDCH_0004_MessageStateIsNullOrEmpty" xml:space="preserve">
|
||||||
<value>OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or whitespace. State is required to process the message.</value>
|
<value>OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or empty.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0005_MessageStateIsInValid" xml:space="preserve">
|
<data name="OIDCH_0005_MessageStateIsInvalid" xml:space="preserve">
|
||||||
<value>OIDCH_0005: unable to unprotect the message.State</value>
|
<value>OIDCH_0005: Unable to unprotect the message.State.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0006_MessageErrorNotNull" xml:space="preserve">
|
<data name="OIDCH_0006_MessageContainsError" xml:space="preserve">
|
||||||
<value>OIDCH_0006_MessageErrorNotNull: '{0}'.</value>
|
<value>OIDCH_0006: Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0007_UpdatingConfiguration" xml:space="preserve">
|
<data name="OIDCH_0007_UpdatingConfiguration" xml:space="preserve">
|
||||||
<value>OIDCH_0007: updating configuration</value>
|
<value>OIDCH_0007: Updating configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse" xml:space="preserve">
|
<data name="OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse" xml:space="preserve">
|
||||||
<value>OIDCH_0008: securityTokenReceivedNotification.HandledResponse</value>
|
<value>OIDCH_0008: SecurityTokenReceivedNotification.HandledResponse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0009_SecurityTokenReceivedNotificationSkipped:" xml:space="preserve">
|
<data name="OIDCH_0009_SecurityTokenReceivedNotificationSkipped:" xml:space="preserve">
|
||||||
<value>OIDCH_0009: securityTokenReceivedNotification.Skipped</value>
|
<value>OIDCH_0009: SecurityTokenReceivedNotification.Skipped</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0010_ValidatedSecurityTokenNotJwt" xml:space="preserve">
|
<data name="OIDCH_0010_ValidatedSecurityTokenNotJwt" xml:space="preserve">
|
||||||
<value>OIDCH_0010: Validated Security Token must be a JwtSecurityToken was: '{0}'.</value>
|
<value>OIDCH_0010: Validated Security Token must be a JwtSecurityToken was: '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0011_UnableToValidateToken" xml:space="preserve">
|
<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>
|
||||||
<data name="OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse" xml:space="preserve">
|
<data name="OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse" xml:space="preserve">
|
||||||
<value>OIDCH_0012: securityTokenValidatedNotification.HandledResponse</value>
|
<value>OIDCH_0012: SecurityTokenValidatedNotification.HandledResponse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0013_SecurityTokenValidatedNotificationSkipped" xml:space="preserve">
|
<data name="OIDCH_0013_SecurityTokenValidatedNotificationSkipped" xml:space="preserve">
|
||||||
<value>OIDCH_0013: securityTokenValidatedNotification.Skipped</value>
|
<value>OIDCH_0013: SecurityTokenValidatedNotification.Skipped</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0014_CodeReceived" xml:space="preserve">
|
<data name="OIDCH_0014_AuthorizationCodeReceived" xml:space="preserve">
|
||||||
<value>OIDCH_0014: 'code' received: '{0}'</value>
|
<value>OIDCH_0014: AuthorizationCode received: '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0015_CodeReceivedNotificationHandledResponse" xml:space="preserve">
|
<data name="OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse" xml:space="preserve">
|
||||||
<value>OIDCH_0015: codeReceivedNotification.HandledResponse</value>
|
<value>OIDCH_0015: AuthorizationCodeReceivedNotification.HandledResponse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0016_CodeReceivedNotificationSkipped" xml:space="preserve">
|
<data name="OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped" xml:space="preserve">
|
||||||
<value>OIDCH_0016: codeReceivedNotification.Skipped</value>
|
<value>OIDCH_0016: AuthorizationCodeReceivedNotification.Skipped</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0017: Exception occurred while processing message" xml:space="preserve">
|
<data name="OIDCH_0017_ExceptionOccurredWhileProcessingMessage" xml:space="preserve">
|
||||||
<value>OIDCH_0017: Exception occurred while processing message</value>
|
<value>OIDCH_0017: Exception occurred while processing message.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0018_AuthenticationFailedNotificationHandledResponse" xml:space="preserve">
|
<data name="OIDCH_0018_AuthenticationFailedNotificationHandledResponse" xml:space="preserve">
|
||||||
<value>OIDCH_0018: authenticationFailedNotification.HandledResponse</value>
|
<value>OIDCH_0018: AuthenticationFailedNotification.HandledResponse</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0019_AuthenticationNotificationFailedSkipped" xml:space="preserve">
|
<data name="OIDCH_0019_AuthenticationNotificationFailedSkipped" xml:space="preserve">
|
||||||
<value>OIDCH_0019: authenticationFailedNotification.Skipped</value>
|
<value>OIDCH_0019: AuthenticationFailedNotification.Skipped</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OIDCH_0020_IdTokenReceived" xml:space="preserve">
|
<data name="OIDCH_0020_IdTokenReceived" xml:space="preserve">
|
||||||
<value>OIDCH_0020: 'id_token' received: '{0}'</value>
|
<value>OIDCH_0020: 'id_token' received: '{0}'</value>
|
||||||
</data>
|
</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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.Notifications
|
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 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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
// this controls if the logs are written to the console.
|
|
||||||
// they can be reviewed for general content.
|
|
||||||
//#define _Verbose
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IdentityModel.Tokens;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Authentication.Notifications;
|
|
||||||
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.DataProtection;
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
using Microsoft.AspNet.Http.Features.Authentication;
|
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Microsoft.Framework.WebEncoders;
|
using Microsoft.Framework.WebEncoders;
|
||||||
using Microsoft.IdentityModel.Protocols;
|
using Microsoft.IdentityModel.Protocols;
|
||||||
|
using Moq;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -32,52 +30,9 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OpenIdConnectHandlerTests
|
public class OpenIdConnectHandlerTests
|
||||||
{
|
{
|
||||||
static List<LogEntry> CompleteLogEntries;
|
private const string nonceForJwt = "abc";
|
||||||
static Dictionary<string, LogLevel> LogEntries;
|
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";
|
||||||
static OpenIdConnectHandlerTests()
|
|
||||||
{
|
|
||||||
LogEntries =
|
|
||||||
new Dictionary<string, LogLevel>()
|
|
||||||
{
|
|
||||||
{ "OIDCH_0000:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0001:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0002:", LogLevel.Information },
|
|
||||||
{ "OIDCH_0003:", LogLevel.Information },
|
|
||||||
{ "OIDCH_0004:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0005:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0006:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0007:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0008:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0009:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0010:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0011:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0012:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0013:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0014:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0015:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0016:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0017:", LogLevel.Error },
|
|
||||||
{ "OIDCH_0018:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0019:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0020:", LogLevel.Debug },
|
|
||||||
{ "OIDCH_0026:", LogLevel.Error },
|
|
||||||
};
|
|
||||||
|
|
||||||
BuildLogEntryList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds the complete list of log entries that are available in the runtime.
|
|
||||||
/// </summary>
|
|
||||||
private static void BuildLogEntryList()
|
|
||||||
{
|
|
||||||
CompleteLogEntries = new List<LogEntry>();
|
|
||||||
foreach (var entry in LogEntries)
|
|
||||||
{
|
|
||||||
CompleteLogEntries.Add(new LogEntry { State = entry.Key, Level = entry.Value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sanity check that logging is filtering, hi / low water marks are checked
|
/// Sanity check that logging is filtering, hi / low water marks are checked
|
||||||
|
|
@ -85,7 +40,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LoggingLevel()
|
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.Critical).ShouldBe<bool>(true);
|
||||||
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(true);
|
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(true);
|
||||||
logger.IsEnabled(LogLevel.Error).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.Verbose).ShouldBe<bool>(true);
|
||||||
logger.IsEnabled(LogLevel.Warning).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.Critical).ShouldBe<bool>(true);
|
||||||
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(false);
|
logger.IsEnabled(LogLevel.Debug).ShouldBe<bool>(false);
|
||||||
logger.IsEnabled(LogLevel.Error).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);
|
logger.IsEnabled(LogLevel.Warning).ShouldBe<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
[Theory, MemberData("AuthenticateCoreStateDataSet")]
|
||||||
/// Test <see cref="OpenIdConnectAuthenticationHandler.AuthenticateCoreAsync"/> produces expected logs.
|
public async Task AuthenticateCoreState(Action<OpenIdConnectAuthenticationOptions> action, OpenIdConnectMessage message)
|
||||||
/// Each call to 'RunVariation' is configured with an <see cref="OpenIdConnectAuthenticationOptions"/> and <see cref="OpenIdConnectMessage"/>.
|
|
||||||
/// The list of expected log entries is checked and any errors reported.
|
|
||||||
/// <see cref="CustomLoggerFactory"/> captures the logs so they can be prepared.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Fact]
|
|
||||||
public async Task AuthenticateCore()
|
|
||||||
{
|
{
|
||||||
//System.Diagnostics.Debugger.Launch();
|
var handler = new OpenIdConnectAuthenticationHandlerForTestingAuthenticate();
|
||||||
|
var server = CreateServer(new ConfigureOptions<OpenIdConnectAuthenticationOptions>(action), UrlEncoder.Default, handler);
|
||||||
var propertiesFormatter = new AuthenticationPropertiesFormater();
|
await server.CreateClient().PostAsync("http://localhost", new FormUrlEncodedContent(message.Parameters.Where(pair => pair.Value != null)));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static TheoryData<Action<OpenIdConnectAuthenticationOptions>, OpenIdConnectMessage> AuthenticateCoreStateDataSet
|
||||||
/// Tests that <see cref="OpenIdConnectAuthenticationHandler"/> processes a messaage as expected.
|
|
||||||
/// The test runs two independant paths: Using <see cref="ConfigureOptions{TOptions}"/> and <see cref="IOptions{TOptions}"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logLevel"><see cref="LogLevel"/> for this variation</param>
|
|
||||||
/// <param name="message">the <see cref="OpenIdConnectMessage"/> that has arrived</param>
|
|
||||||
/// <param name="action">the <see cref="OpenIdConnectAuthenticationOptions"/> delegate used for setting the options.</param>
|
|
||||||
/// <param name="errors">container for propogation of errors.</param>
|
|
||||||
/// <param name="logsEntriesExpected">the expected log entries</param>
|
|
||||||
/// <returns>a Task</returns>
|
|
||||||
private async Task RunVariation(LogLevel logLevel, OpenIdConnectMessage message, Action<OpenIdConnectAuthenticationOptions> action, Dictionary<string, List<Tuple<LogEntry, LogEntry>>> errors, int[] logsEntriesExpected)
|
|
||||||
{
|
{
|
||||||
var expectedLogs = PopulateLogEntries(logsEntriesExpected);
|
get
|
||||||
string variation = action.Method.ToString().Substring(5, action.Method.ToString().IndexOf('(') - 5);
|
|
||||||
#if _Verbose
|
|
||||||
Console.WriteLine(Environment.NewLine + "=====" + Environment.NewLine + "Variation: " + variation + ", LogLevel: " + logLevel.ToString() + Environment.NewLine + Environment.NewLine + "Expected Logs: ");
|
|
||||||
DisplayLogs(expectedLogs);
|
|
||||||
Console.WriteLine(Environment.NewLine + "Logs using ConfigureOptions:");
|
|
||||||
#endif
|
|
||||||
var form = new FormUrlEncodedContent(message.Parameters);
|
|
||||||
var loggerFactory = new CustomLoggerFactory(logLevel);
|
|
||||||
var server = CreateServer(new CustomConfigureOptions(action), loggerFactory);
|
|
||||||
await server.CreateClient().PostAsync("http://localhost", form);
|
|
||||||
CheckLogs(variation + ":ConfigOptions", loggerFactory.Logger.Logs, expectedLogs, errors);
|
|
||||||
|
|
||||||
#if _Verbose
|
|
||||||
Console.WriteLine(Environment.NewLine + "Logs using IOptions:");
|
|
||||||
#endif
|
|
||||||
form = new FormUrlEncodedContent(message.Parameters);
|
|
||||||
loggerFactory = new CustomLoggerFactory(logLevel);
|
|
||||||
server = CreateServer(new Options(action), loggerFactory);
|
|
||||||
await server.CreateClient().PostAsync("http://localhost", form);
|
|
||||||
CheckLogs(variation + ":IOptions", loggerFactory.Logger.Logs, expectedLogs, errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Populates a list of expected log entries for a test variation.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="items">the index for the <see cref="LogEntry"/> in CompleteLogEntries of interest.</param>
|
|
||||||
/// <returns>a <see cref="List{LogEntry}"/> that represents the expected entries for a test variation.</returns>
|
|
||||||
private List<LogEntry> PopulateLogEntries(int[] items)
|
|
||||||
{
|
|
||||||
var entries = new List<LogEntry>();
|
|
||||||
foreach(var item in items)
|
|
||||||
{
|
{
|
||||||
entries.Add(CompleteLogEntries[item]);
|
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)
|
// Userstate != null
|
||||||
{
|
message = new OpenIdConnectMessage();
|
||||||
foreach (var logentry in logs)
|
properties.Items.Clear();
|
||||||
{
|
var userstate = Guid.NewGuid().ToString();
|
||||||
Console.WriteLine(logentry.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);
|
if (notification.ProtocolMessage.State == null && !notification.ProtocolMessage.Parameters.ContainsKey(ExpectedStateParameter))
|
||||||
foreach (var logError in error.Value)
|
return Task.FromResult<object>(null);
|
||||||
{
|
|
||||||
Console.WriteLine("*Captured*, *Expected* : *" + (logError.Item1?.ToString() ?? "null") + "*, *" + (logError.Item2?.ToString() ?? "null") + "*");
|
if (notification.ProtocolMessage.State == null || !notification.ProtocolMessage.Parameters.ContainsKey(ExpectedStateParameter))
|
||||||
}
|
Assert.True(false, "(notification.ProtocolMessage.State=!= null || !notification.ProtocolMessage.Parameters.ContainsKey(expectedState)");
|
||||||
Console.WriteLine(Environment.NewLine);
|
|
||||||
|
Assert.Equal(notification.ProtocolMessage.State, notification.ProtocolMessage.Parameters[ExpectedStateParameter]);
|
||||||
|
return Task.FromResult<object>(null);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
[Theory, MemberData("AuthenticateCoreDataSet")]
|
||||||
/// Adds to errors if a variation if any are found.
|
public async Task AuthenticateCore(LogLevel logLevel, int[] expectedLogIndexes, Action<OpenIdConnectAuthenticationOptions> action, OpenIdConnectMessage message)
|
||||||
/// </summary>
|
|
||||||
/// <param name="variation">if this has been seen before, errors will be appended, test results are easier to understand if this is unique.</param>
|
|
||||||
/// <param name="capturedLogs">these are the logs the runtime generated</param>
|
|
||||||
/// <param name="expectedLogs">these are the errors that were expected</param>
|
|
||||||
/// <param name="errors">the dictionary to record any errors</param>
|
|
||||||
private void CheckLogs(string variation, List<LogEntry> capturedLogs, List<LogEntry> expectedLogs, Dictionary<string, List<Tuple<LogEntry, LogEntry>>> errors)
|
|
||||||
{
|
{
|
||||||
var localErrors = new List<Tuple<LogEntry, LogEntry>>();
|
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)
|
await server.CreateClient().PostAsync("http://localhost", new FormUrlEncodedContent(message.Parameters));
|
||||||
{
|
LoggingUtilities.CheckLogs(loggerFactory.Logger.Logs, expectedLogs, errors);
|
||||||
for (int i = 0; i < capturedLogs.Count; i++)
|
Debug.WriteLine(LoggingUtilities.LoggingErrors(errors));
|
||||||
{
|
Assert.True(errors.Count == 0, LoggingUtilities.LoggingErrors(errors));
|
||||||
if (i + 1 > expectedLogs.Count)
|
}
|
||||||
{
|
|
||||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], null));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!TestUtilities.AreEqual<LogEntry>(capturedLogs[i], expectedLogs[i]))
|
|
||||||
{
|
|
||||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], expectedLogs[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i < expectedLogs.Count; i++)
|
|
||||||
{
|
|
||||||
if (i + 1 > capturedLogs.Count)
|
|
||||||
{
|
|
||||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(null, expectedLogs[i]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!TestUtilities.AreEqual<LogEntry>(expectedLogs[i], capturedLogs[i]))
|
|
||||||
{
|
|
||||||
localErrors.Add(new Tuple<LogEntry, LogEntry>(capturedLogs[i], expectedLogs[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localErrors.Count != 0)
|
public static TheoryData<LogLevel, int[], Action<OpenIdConnectAuthenticationOptions>, OpenIdConnectMessage> AuthenticateCoreDataSet
|
||||||
|
{
|
||||||
|
get
|
||||||
{
|
{
|
||||||
if (errors.ContainsKey(variation))
|
var formater = new AuthenticationPropertiesFormaterKeyValue();
|
||||||
{
|
var dataset = new TheoryData<LogLevel, int[], Action< OpenIdConnectAuthenticationOptions >, OpenIdConnectMessage>();
|
||||||
foreach (var error in localErrors)
|
var properties = new AuthenticationProperties();
|
||||||
{
|
var message = new OpenIdConnectMessage();
|
||||||
errors[variation].Add(error);
|
var validState = UrlEncoder.Default.UrlEncode(formater.Protect(properties));
|
||||||
}
|
message.State = validState;
|
||||||
}
|
|
||||||
else
|
// MessageReceived - Handled / Skipped
|
||||||
{
|
dataset.Add(LogLevel.Debug, new int[] { 0, 1, 2 }, MessageReceivedHandledOptions, message);
|
||||||
errors[variation] = localErrors;
|
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);
|
DefaultOptions(options);
|
||||||
options.Notifications =
|
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);
|
DefaultOptions(options);
|
||||||
options.Notifications =
|
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";
|
DefaultOptions(options);
|
||||||
options.ConfigurationManager = ConfigurationManager.DefaultStaticConfigurationManager;
|
options.Notifications =
|
||||||
options.StateDataFormat = new AuthenticationPropertiesFormater();
|
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)
|
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)
|
private static void SecurityTokenReceivedHandledOptions(OpenIdConnectAuthenticationOptions options)
|
||||||
{
|
{
|
||||||
DefaultOptions(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);
|
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 =
|
options.Notifications =
|
||||||
new OpenIdConnectAuthenticationNotifications
|
new OpenIdConnectAuthenticationNotifications
|
||||||
{
|
{
|
||||||
|
|
@ -410,7 +404,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
|
|
||||||
private static void SecurityTokenValidatedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
private static void SecurityTokenValidatedSkippedOptions(OpenIdConnectAuthenticationOptions options)
|
||||||
{
|
{
|
||||||
DefaultOptions(options);
|
SecurityTokenValidatorValidatesAllTokens(options);
|
||||||
options.Notifications =
|
options.Notifications =
|
||||||
new OpenIdConnectAuthenticationNotifications
|
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(
|
return TestServer.Create(
|
||||||
app =>
|
app =>
|
||||||
{
|
{
|
||||||
app.UseCustomOpenIdConnectAuthentication(options, loggerFactory);
|
app.UseMiddleware<OpenIdConnectAuthenticationMiddlewareForTestingAuthenticate>(options, encoder, handler);
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
await next();
|
await next();
|
||||||
|
|
@ -437,19 +448,18 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
},
|
},
|
||||||
services =>
|
services =>
|
||||||
{
|
{
|
||||||
services.AddAuthentication();
|
|
||||||
services.AddWebEncoders();
|
services.AddWebEncoders();
|
||||||
services.AddDataProtection();
|
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(
|
return TestServer.Create(
|
||||||
app =>
|
app =>
|
||||||
{
|
{
|
||||||
app.UseCustomOpenIdConnectAuthentication(configureOptions, loggerFactory);
|
app.UseMiddleware<OpenIdConnectAuthenticationMiddlewareForTestingAuthenticate>(configureOptions, encoder, loggerFactory, handler);
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
await next();
|
await next();
|
||||||
|
|
@ -457,274 +467,10 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
},
|
},
|
||||||
services =>
|
services =>
|
||||||
{
|
{
|
||||||
services.AddAuthentication();
|
|
||||||
services.AddWebEncoders();
|
services.AddWebEncoders();
|
||||||
services.AddDataProtection();
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Microsoft.AspNet.Authentication.Cookies;
|
using Microsoft.AspNet.Authentication.Cookies;
|
||||||
using Microsoft.AspNet.Authentication.DataHandler;
|
|
||||||
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
using Microsoft.AspNet.Authentication.OpenIdConnect;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.DataProtection;
|
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using Microsoft.Framework.WebEncoders;
|
using Microsoft.Framework.WebEncoders;
|
||||||
using Newtonsoft.Json;
|
using Microsoft.IdentityModel.Protocols;
|
||||||
|
using Moq;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -33,25 +29,31 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
{
|
{
|
||||||
static string noncePrefix = "OpenIdConnect." + "Nonce.";
|
static string noncePrefix = "OpenIdConnect." + "Nonce.";
|
||||||
static string nonceDelimiter = ".";
|
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]
|
[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 =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
SetOptions(options, DefaultParameters(), queryValues);
|
||||||
options.ClientId = "Test Id";
|
|
||||||
options.SignInScheme = OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
|
|
||||||
});
|
});
|
||||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
|
||||||
|
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
var location = transaction.Response.Headers.Location.ToString();
|
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, DefaultParameters());
|
||||||
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=");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -59,95 +61,230 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
{
|
{
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
options.Authority = DefaultAuthority;
|
||||||
options.ClientId = "Test Id";
|
options.ClientId = "Test Id";
|
||||||
|
options.Configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||||
});
|
});
|
||||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||||
transaction.SetCookie.Single().ShouldContain("OpenIdConnect.nonce.");
|
transaction.SetCookie.Single().ShouldContain(OpenIdConnectAuthenticationDefaults.CookieNoncePrefix);
|
||||||
}
|
|
||||||
|
|
||||||
[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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ChallengeWillUseOptionsProperties()
|
public async Task ChallengeWillUseOptionsProperties()
|
||||||
{
|
{
|
||||||
|
var queryValues = new ExpectedQueryValues(DefaultAuthority);
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
SetOptions(options, DefaultParameters(), queryValues);
|
||||||
options.ClientId = "Test Id";
|
|
||||||
options.SignInScheme = OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
|
|
||||||
options.Scope = "https://www.googleapis.com/auth/plus.login";
|
|
||||||
options.ResponseType = "id_token";
|
|
||||||
});
|
});
|
||||||
var transaction = await SendAsync(server, "https://example.com/challenge");
|
|
||||||
|
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
var query = transaction.Response.Headers.Location.Query;
|
queryValues.CheckValues(transaction.Response.Headers.Location.AbsoluteUri, DefaultParameters());
|
||||||
query.ShouldContain("scope=" + UrlEncoder.Default.UrlEncode("https://www.googleapis.com/auth/plus.login"));
|
}
|
||||||
query.ShouldContain("response_type=" + UrlEncoder.Default.UrlEncode("id_token"));
|
|
||||||
|
/// <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]
|
[Fact]
|
||||||
public async Task ChallengeWillUseNotifications()
|
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 =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
SetOptions(options, DefaultParameters(), queryValues);
|
||||||
options.ClientId = "Test Id";
|
|
||||||
options.Notifications = new OpenIdConnectAuthenticationNotifications
|
options.Notifications = new OpenIdConnectAuthenticationNotifications
|
||||||
{
|
{
|
||||||
MessageReceived = notification =>
|
RedirectToIdentityProvider = notification =>
|
||||||
{
|
{
|
||||||
notification.ProtocolMessage.Scope = "test openid profile";
|
notification.ProtocolMessage.ClientId = queryValuesSetInNotification.ClientId;
|
||||||
notification.HandleResponse();
|
notification.ProtocolMessage.RedirectUri = queryValuesSetInNotification.RedirectUri;
|
||||||
return Task.FromResult<object>(null);
|
notification.ProtocolMessage.Resource = queryValuesSetInNotification.Resource;
|
||||||
}
|
notification.ProtocolMessage.Scope = queryValuesSetInNotification.Scope;
|
||||||
|
return Task.FromResult<object>(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var properties = new AuthenticationProperties();
|
var transaction = await SendAsync(server, DefaultHost + Challenge);
|
||||||
var state = stateFormat.Protect(properties);
|
|
||||||
var transaction = await SendAsync(server,"https://example.com/challenge");
|
|
||||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
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]
|
[Fact]
|
||||||
public async Task SignOutWithDefaultRedirectUri()
|
public async Task SignOutWithDefaultRedirectUri()
|
||||||
{
|
{
|
||||||
|
var configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
options.Authority = DefaultAuthority;
|
||||||
options.ClientId = "Test Id";
|
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.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]
|
[Fact]
|
||||||
public async Task SignOutWithCustomRedirectUri()
|
public async Task SignOutWithCustomRedirectUri()
|
||||||
{
|
{
|
||||||
|
var configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
options.Authority = DefaultAuthority;
|
||||||
options.ClientId = "Test Id";
|
options.ClientId = "Test Id";
|
||||||
|
options.Configuration = configuration;
|
||||||
options.PostLogoutRedirectUri = "https://example.com/logout";
|
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.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
transaction.Response.Headers.Location.AbsoluteUri.ShouldContain(UrlEncoder.Default.UrlEncode("https://example.com/logout"));
|
transaction.Response.Headers.Location.AbsoluteUri.ShouldContain(UrlEncoder.Default.UrlEncode("https://example.com/logout"));
|
||||||
}
|
}
|
||||||
|
|
@ -155,10 +292,12 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SignOutWith_Specific_RedirectUri_From_Authentication_Properites()
|
public async Task SignOutWith_Specific_RedirectUri_From_Authentication_Properites()
|
||||||
{
|
{
|
||||||
|
var configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://login.windows.net/common";
|
options.Authority = DefaultAuthority;
|
||||||
options.ClientId = "Test Id";
|
options.ClientId = "Test Id";
|
||||||
|
options.Configuration = configuration;
|
||||||
options.PostLogoutRedirectUri = "https://example.com/logout";
|
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"));
|
transaction.Response.Headers.Location.AbsoluteUri.ShouldContain(UrlEncoder.Default.UrlEncode("http://www.example.com/specific_redirect_uri"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
private static TestServer CreateServer(Action<OpenIdConnectAuthenticationOptions> configureOptions, Func<HttpContext, Task> handler = null, AuthenticationProperties properties = null)
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
return TestServer.Create(app =>
|
return TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
|
|
@ -203,15 +319,25 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
{
|
{
|
||||||
var req = context.Request;
|
var req = context.Request;
|
||||||
var res = context.Response;
|
var res = context.Response;
|
||||||
if (req.Path == new PathString("/challenge"))
|
|
||||||
|
if (req.Path == new PathString(Challenge))
|
||||||
{
|
{
|
||||||
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
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());
|
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);
|
await context.Authentication.SignOutAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
|
|
@ -248,34 +374,39 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
{
|
{
|
||||||
request.Headers.Add("Cookie", cookieHeader);
|
request.Headers.Add("Cookie", cookieHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
var transaction = new Transaction
|
var transaction = new Transaction
|
||||||
{
|
{
|
||||||
Request = request,
|
Request = request,
|
||||||
Response = await server.CreateClient().SendAsync(request),
|
Response = await server.CreateClient().SendAsync(request),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (transaction.Response.Headers.Contains("Set-Cookie"))
|
if (transaction.Response.Headers.Contains("Set-Cookie"))
|
||||||
{
|
{
|
||||||
transaction.SetCookie = transaction.Response.Headers.GetValues("Set-Cookie").ToList();
|
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 &&
|
if (transaction.Response.Content != null &&
|
||||||
transaction.Response.Content.Headers.ContentType != null &&
|
transaction.Response.Content.Headers.ContentType != null &&
|
||||||
transaction.Response.Content.Headers.ContentType.MediaType == "text/xml")
|
transaction.Response.Content.Headers.ContentType.MediaType == "text/xml")
|
||||||
{
|
{
|
||||||
transaction.ResponseElement = XElement.Parse(transaction.ResponseText);
|
transaction.ResponseElement = XElement.Parse(transaction.ResponseText);
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Transaction
|
private class Transaction
|
||||||
{
|
{
|
||||||
public HttpRequestMessage Request { get; set; }
|
public HttpRequestMessage Request { get; set; }
|
||||||
|
|
||||||
public HttpResponseMessage Response { get; set; }
|
public HttpResponseMessage Response { get; set; }
|
||||||
|
|
||||||
public IList<string> SetCookie { get; set; }
|
public IList<string> SetCookie { get; set; }
|
||||||
|
|
||||||
public string ResponseText { get; set; }
|
public string ResponseText { get; set; }
|
||||||
|
|
||||||
public XElement ResponseElement { get; set; }
|
public XElement ResponseElement { get; set; }
|
||||||
|
|
||||||
public string AuthenticationCookieValue
|
public string AuthenticationCookieValue
|
||||||
|
|
@ -294,57 +425,29 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
return null;
|
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)
|
GetNonceExpirationTime(noncePrefix + DateTime.MaxValue.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(DateTime.MaxValue);
|
||||||
{
|
|
||||||
if (Sender != null)
|
|
||||||
{
|
|
||||||
return Task.FromResult(Sender(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
GetNonceExpirationTime(noncePrefix + utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + nonceDelimiter, TimeSpan.FromHours(1)).ShouldBe(utcNow + TimeSpan.FromHours(1));
|
||||||
{
|
|
||||||
var res = new HttpResponseMessage(HttpStatusCode.OK);
|
GetNonceExpirationTime(noncePrefix, TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||||
var text = JsonConvert.SerializeObject(content);
|
|
||||||
res.Content = new StringContent(text, Encoding.UTF8, "application/json");
|
GetNonceExpirationTime("", TimeSpan.FromHours(1)).ShouldBe(DateTime.MinValue);
|
||||||
return res;
|
|
||||||
|
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)
|
private static DateTime GetNonceExpirationTime(string keyname, TimeSpan nonceLifetime)
|
||||||
|
|
@ -373,8 +476,8 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nonceTime;
|
return nonceTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.IdentityModel.Protocols;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
{
|
{
|
||||||
|
|
@ -10,6 +11,8 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TestUtilities
|
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
|
public static bool AreEqual<T>(object obj1, object obj2, Func<object, object, bool> comparer = null) where T : class
|
||||||
{
|
{
|
||||||
if (obj1 == null && obj2 == null)
|
if (obj1 == null && obj2 == null)
|
||||||
|
|
@ -63,11 +66,6 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AreEqual<Exception>(logEntry1.Exception, logEntry2.Exception))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logEntry1.State == null && logEntry2.State == null)
|
if (logEntry1.State == null && logEntry2.State == null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -104,5 +102,26 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
||||||
|
|
||||||
return AreEqual<Exception>(exception1.InnerException, exception2.InnerException);
|
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