diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationDefaults.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationDefaults.cs
index 763ef11d3..2e1a80889 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationDefaults.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationDefaults.cs
@@ -9,14 +9,14 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
public static class OpenIdConnectAuthenticationDefaults
{
///
- /// The default value used for OpenIdConnectAuthenticationOptions.AuthenticationScheme
+ /// Constant used to identify state in openIdConnect protocol message.
///
- public const string AuthenticationScheme = "OpenIdConnect";
+ public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
///
- /// The prefix used to provide a default OpenIdConnectAuthenticationOptions.CookieName
+ /// The default value used for OpenIdConnectAuthenticationOptions.AuthenticationScheme.
///
- public const string CookiePrefix = ".AspNet.OpenIdConnect.";
+ public const string AuthenticationScheme = "OpenIdConnect";
///
/// The default value for OpenIdConnectAuthenticationOptions.Caption.
@@ -24,18 +24,23 @@ public static class OpenIdConnectAuthenticationDefaults
public const string Caption = "OpenIdConnect";
///
- /// The prefix used to for the a nonce in the cookie
+ /// The prefix used to provide a default OpenIdConnectAuthenticationOptions.CookieName.
+ ///
+ public const string CookiePrefix = ".AspNet.OpenIdConnect.";
+
+ ///
+ /// The prefix used to for the a nonce in the cookie.
///
public const string CookieNoncePrefix = ".AspNet.OpenIdConnect.Nonce.";
///
- /// 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'.
///
- public const string RedirectUriUsedForCodeKey = "OpenIdConnect.Code.RedirectUri";
+ public const string RedirectUriForCodePropertiesKey = "OpenIdConnect.Code.RedirectUri";
///
- /// Constant used to identify state in openIdConnect protocal message
+ /// Constant used to identify userstate inside AuthenticationProperties that have been serialized in the 'state' parameter.
///
- public const string AuthenticationPropertiesKey = "OpenIdConnect.AuthenticationProperties";
+ public const string UserstatePropertiesKey = "OpenIdConnect.Userstate";
}
}
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
index 8b9c4683c..f3e381968 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
@@ -28,19 +28,6 @@ public class OpenIdConnectAuthenticationHandler : AuthenticationHandler
/// Handles Signout
///
@@ -54,7 +41,7 @@ protected override async Task HandleSignOutAsync(SignOutContext signout)
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
}
- var openIdConnectMessage = new OpenIdConnectMessage()
+ var message = new OpenIdConnectMessage()
{
IssuerAddress = _configuration == null ? string.Empty : (_configuration.EndSessionEndpoint ?? string.Empty),
RequestType = OpenIdConnectRequestType.LogoutRequest,
@@ -66,30 +53,42 @@ protected override async Task HandleSignOutAsync(SignOutContext signout)
var properties = new AuthenticationProperties(signout.Properties);
if (!string.IsNullOrEmpty(properties.RedirectUri))
{
- openIdConnectMessage.PostLogoutRedirectUri = properties.RedirectUri;
+ message.PostLogoutRedirectUri = properties.RedirectUri;
}
- else if (!string.IsNullOrWhiteSpace(Options.PostLogoutRedirectUri))
+ else if (!string.IsNullOrEmpty(Options.PostLogoutRedirectUri))
{
- openIdConnectMessage.PostLogoutRedirectUri = Options.PostLogoutRedirectUri;
+ message.PostLogoutRedirectUri = Options.PostLogoutRedirectUri;
}
- var notification = new RedirectToIdentityProviderNotification(Context, Options)
+ if (Options.Notifications.RedirectToIdentityProvider != null)
{
- ProtocolMessage = openIdConnectMessage
- };
-
- await Options.Notifications.RedirectToIdentityProvider(notification);
+ var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification(Context, Options)
+ {
+ ProtocolMessage = message
+ };
- if (!notification.HandledResponse)
- {
- var redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
- if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
+ await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
+ if (redirectToIdentityProviderNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
+ return;
+ }
+ else if (redirectToIdentityProviderNotification.Skipped)
{
- Logger.LogWarning(Resources.OIDCH_0051_RedirectUriLogoutIsNotWellFormed, redirectUri);
+ 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 @@ protected override async Task HandleUnauthorizedAsync([NotNull] ChallengeC
// 2. CurrentUri if Options.DefaultToCurrentUriOnRedirect is true)
AuthenticationProperties properties = new AuthenticationProperties(context.Properties);
- if (!string.IsNullOrWhiteSpace(properties.RedirectUri))
+ if (!string.IsNullOrEmpty(properties.RedirectUri))
{
Logger.LogDebug(Resources.OIDCH_0030_Using_Properties_RedirectUri, properties.RedirectUri);
}
@@ -117,15 +116,15 @@ protected override async Task HandleUnauthorizedAsync([NotNull] ChallengeC
properties.RedirectUri = CurrentUri;
}
- if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
+ if (!string.IsNullOrEmpty(Options.RedirectUri))
{
Logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri);
}
// When redeeming a 'code' for an AccessToken, this value is needed
- if (!string.IsNullOrWhiteSpace(Options.RedirectUri))
+ if (!string.IsNullOrEmpty(Options.RedirectUri))
{
- properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey, Options.RedirectUri);
+ properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey, Options.RedirectUri);
}
if (_configuration == null && Options.ConfigurationManager != null)
@@ -138,13 +137,12 @@ protected override async Task HandleUnauthorizedAsync([NotNull] ChallengeC
ClientId = Options.ClientId,
IssuerAddress = _configuration?.AuthorizationEndpoint ?? string.Empty,
RedirectUri = Options.RedirectUri,
- // [brentschmaltz] - this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
+ // [brentschmaltz] - #215 this should be a property on RedirectToIdentityProviderNotification not on the OIDCMessage.
RequestType = OpenIdConnectRequestType.AuthenticationRequest,
Resource = Options.Resource,
ResponseMode = Options.ResponseMode,
ResponseType = Options.ResponseType,
- Scope = Options.Scope,
- State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + UrlEncoder.UrlEncode(Options.StateDataFormat.Protect(properties))
+ Scope = Options.Scope
};
if (Options.ProtocolValidator.RequireNonce)
@@ -169,24 +167,37 @@ protected override async Task HandleUnauthorizedAsync([NotNull] ChallengeC
}
}
- var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification(Context, Options)
+ if (Options.Notifications.RedirectToIdentityProvider != null)
{
- ProtocolMessage = message
- };
+ var redirectToIdentityProviderNotification =
+ new RedirectToIdentityProviderNotification(Context, Options)
+ {
+ ProtocolMessage = message
+ };
- await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
- if (redirectToIdentityProviderNotification.HandledResponse)
- {
- Logger.LogInformation(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
- return true; // REVIEW: Make sure this should stop all other handlers
- }
- else if (redirectToIdentityProviderNotification.Skipped)
- {
- Logger.LogInformation(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
- return false; // REVIEW: Make sure this should not stop all other handlers
+ await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
+ if (redirectToIdentityProviderNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
+ return true;
+ }
+ else if (redirectToIdentityProviderNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
+ return false;
+ }
+
+ if (!string.IsNullOrEmpty(redirectToIdentityProviderNotification.ProtocolMessage.State))
+ {
+ properties.Items[OpenIdConnectAuthenticationDefaults.UserstatePropertiesKey] = redirectToIdentityProviderNotification.ProtocolMessage.State;
+ }
+
+ message = redirectToIdentityProviderNotification.ProtocolMessage;
}
- var redirectUri = redirectToIdentityProviderNotification.ProtocolMessage.CreateAuthenticationRequestUrl();
+ message.State = Options.StateDataFormat.Protect(properties);
+
+ var redirectUri = message.CreateAuthenticationRequestUrl();
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
{
Logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
@@ -246,66 +257,74 @@ public override async Task AuthenticateAsync()
await Options.Notifications.MessageReceived(messageReceivedNotification);
if (messageReceivedNotification.HandledResponse)
{
- Logger.LogInformation(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
+ Logger.LogVerbose(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
return messageReceivedNotification.AuthenticationTicket;
}
if (messageReceivedNotification.Skipped)
{
- Logger.LogInformation(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
+ Logger.LogVerbose(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
return null;
}
- // runtime always adds state, if we don't find it OR we failed to 'unprotect' it this is not a message we should process.
- if (string.IsNullOrWhiteSpace(message.State))
+ var properties = new AuthenticationProperties();
+
+ // if state is missing, just log it
+ if (string.IsNullOrEmpty(message.State))
{
- Logger.LogError(Resources.OIDCH_0004_MessageStateIsNullOrWhiteSpace);
- return null;
+ Logger.LogWarning(Resources.OIDCH_0004_MessageStateIsNullOrEmpty);
}
-
- var properties = GetPropertiesFromState(message.State);
- if (properties == null)
+ else
{
- Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid);
- return null;
+ // 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;
}
- // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users.
- if (!string.IsNullOrWhiteSpace(message.Error))
+ // if any of the error fields are set, throw error null
+ if (!string.IsNullOrEmpty(message.Error))
{
- Logger.LogError(Resources.OIDCH_0006_MessageErrorNotNull, message.Error);
- throw new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageErrorNotNull, message.Error));
+ Logger.LogError(Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null");
+ throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0006_MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null"));
}
- AuthenticationTicket ticket = null;
- JwtSecurityToken jwt = null;
-
if (_configuration == null && Options.ConfigurationManager != null)
{
- Logger.LogDebug(Resources.OIDCH_0007_UpdatingConfiguration);
+ Logger.LogVerbose(Resources.OIDCH_0007_UpdatingConfiguration);
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
}
+ AuthenticationTicket ticket = null;
+ JwtSecurityToken jwt = null;
+
// OpenIdConnect protocol allows a Code to be received without the id_token
- if (!string.IsNullOrWhiteSpace(message.IdToken))
+ if (!string.IsNullOrEmpty(message.IdToken))
{
Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
var securityTokenReceivedNotification =
new SecurityTokenReceivedNotification(Context, Options)
{
- ProtocolMessage = message
+ ProtocolMessage = message,
};
await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
if (securityTokenReceivedNotification.HandledResponse)
{
- Logger.LogInformation(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
+ Logger.LogVerbose(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
return securityTokenReceivedNotification.AuthenticationTicket;
}
if (securityTokenReceivedNotification.Skipped)
{
- Logger.LogInformation(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
+ Logger.LogVerbose(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
return null;
}
@@ -313,11 +332,11 @@ public override async Task AuthenticateAsync()
var validationParameters = Options.TokenValidationParameters.Clone();
if (_configuration != null)
{
- if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer))
+ if (string.IsNullOrEmpty(validationParameters.ValidIssuer))
{
validationParameters.ValidIssuer = _configuration.Issuer;
}
- else if (!string.IsNullOrWhiteSpace(_configuration.Issuer))
+ else if (!string.IsNullOrEmpty(_configuration.Issuer))
{
validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(new[] { _configuration.Issuer }) ?? new[] { _configuration.Issuer };
}
@@ -336,7 +355,7 @@ public override async Task AuthenticateAsync()
if (jwt == null)
{
Logger.LogError(Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType());
- throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
+ throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
}
}
}
@@ -344,16 +363,16 @@ public override async Task AuthenticateAsync()
if (validatedToken == null)
{
Logger.LogError(Resources.OIDCH_0011_UnableToValidateToken, message.IdToken);
- throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0011_UnableToValidateToken, message.IdToken));
+ throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0011_UnableToValidateToken, message.IdToken));
}
ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
- if (!string.IsNullOrWhiteSpace(message.SessionState))
+ if (!string.IsNullOrEmpty(message.SessionState))
{
ticket.Properties.Items[OpenIdConnectSessionProperties.SessionState] = message.SessionState;
}
- if (_configuration != null && !string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe))
+ if (_configuration != null && !string.IsNullOrEmpty(_configuration.CheckSessionIframe))
{
ticket.Properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
}
@@ -384,13 +403,13 @@ public override async Task AuthenticateAsync()
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
if (securityTokenValidatedNotification.HandledResponse)
{
- Logger.LogInformation(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
+ Logger.LogVerbose(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
return securityTokenValidatedNotification.AuthenticationTicket;
}
if (securityTokenValidatedNotification.Skipped)
{
- Logger.LogInformation(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
+ Logger.LogVerbose(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
return null;
}
@@ -416,7 +435,7 @@ public override async Task AuthenticateAsync()
var protocolValidationContext = new OpenIdConnectProtocolValidationContext
{
AuthorizationCode = message.Code,
- Nonce = nonce,
+ Nonce = nonce,
};
Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
@@ -424,7 +443,7 @@ public override async Task AuthenticateAsync()
if (message.Code != null)
{
- Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code);
+ Logger.LogDebug(Resources.OIDCH_0014_AuthorizationCodeReceived, message.Code);
if (ticket == null)
{
ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme);
@@ -436,20 +455,20 @@ public override async Task AuthenticateAsync()
Code = message.Code,
JwtSecurityToken = jwt,
ProtocolMessage = message,
- RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ?
- ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty,
+ RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey) ?
+ ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey] : string.Empty,
};
await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
if (authorizationCodeReceivedNotification.HandledResponse)
{
- Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse);
+ Logger.LogVerbose(Resources.OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse);
return authorizationCodeReceivedNotification.AuthenticationTicket;
}
if (authorizationCodeReceivedNotification.Skipped)
{
- Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped);
+ Logger.LogVerbose(Resources.OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped);
return null;
}
}
@@ -463,7 +482,11 @@ public override async Task AuthenticateAsync()
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
{
- Options.ConfigurationManager.RequestRefresh();
+ if (Options.ConfigurationManager != null)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0021_AutomaticConfigurationRefresh);
+ Options.ConfigurationManager.RequestRefresh();
+ }
}
var authenticationFailedNotification =
@@ -476,13 +499,13 @@ public override async Task AuthenticateAsync()
await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
if (authenticationFailedNotification.HandledResponse)
{
- Logger.LogInformation(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
+ Logger.LogVerbose(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
return authenticationFailedNotification.AuthenticationTicket;
}
if (authenticationFailedNotification.Skipped)
{
- Logger.LogInformation(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
+ Logger.LogVerbose(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
return null;
}
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs
index c999f5999..67c06b96d 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs
@@ -93,7 +93,7 @@ internal static string OIDCH_0026_ApplyResponseChallengeAsync
}
///
- /// OIDCH_0027: converted 401 to 403.
+ /// OIDCH_0027: Converted 401 to 403.
///
internal static string OIDCH_0027_401_ConvertedTo_403
{
@@ -117,7 +117,7 @@ internal static string OIDCH_0029_ChallengeContextEqualsNull
}
///
- /// OIDCH_0030: using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
+ /// OIDCH_0030: Using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
///
internal static string OIDCH_0030_Using_Properties_RedirectUri
{
@@ -125,7 +125,7 @@ internal static string OIDCH_0030_Using_Properties_RedirectUri
}
///
- /// OIDCH_0031: using Options.RedirectUri for 'redirect_uri': '{0}'.
+ /// OIDCH_0031: Using Options.RedirectUri for 'redirect_uri': '{0}'.
///
internal static string OIDCH_0031_Using_Options_RedirectUri
{
@@ -133,7 +133,7 @@ internal static string OIDCH_0031_Using_Options_RedirectUri
}
///
- /// OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.
+ /// OIDCH_0032: Using the CurrentUri for 'local redirect' post authentication: '{0}'.
///
internal static string OIDCH_0032_UsingCurrentUriRedirectUri
{
@@ -145,11 +145,11 @@ internal static string OIDCH_0032_UsingCurrentUriRedirectUri
///
internal static string OIDCH_0033_NonceAlreadyExists
{
- get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); }
+ get { return ResourceManager.GetString("OIDCH_0033_NonceAlreadyExists"); }
}
///
- /// OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse
+ /// OIDCH_0034: RedirectToIdentityProviderNotification.HandledResponse
///
internal static string OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse
{
@@ -157,7 +157,7 @@ internal static string OIDCH_0034_RedirectToIdentityProviderNotificationHandledR
}
///
- /// OIDCH_0035: redirectToIdentityProviderNotification.Skipped
+ /// OIDCH_0035: RedirectToIdentityProviderNotification.Skipped
///
internal static string OIDCH_0035_RedirectToIdentityProviderNotificationSkipped
{
@@ -165,13 +165,21 @@ internal static string OIDCH_0035_RedirectToIdentityProviderNotificationSkipped
}
///
- /// 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}'.)
///
internal static string OIDCH_0036_UriIsNotWellFormed
{
get { return ResourceManager.GetString("OIDCH_0036_UriIsNotWellFormed"); }
}
+ ///
+ /// OIDCH_0036: RedirectUri is: '{0}'.
+ ///
+ internal static string OIDCH_0037_RedirectUri
+ {
+ get { return ResourceManager.GetString("OIDCH_0037_RedirectUri"); }
+ }
+
///
/// OIDCH_0000: Entering: '{0}'.
///
@@ -197,7 +205,7 @@ internal static string FormatOIDCH_0001_MessageReceived(object p0)
}
///
- /// OIDCH_0002: messageReceivedNotification.HandledResponse
+ /// OIDCH_0002: MessageReceivedNotification.HandledResponse
///
internal static string OIDCH_0002_MessageReceivedNotificationHandledResponse
{
@@ -205,7 +213,7 @@ internal static string OIDCH_0002_MessageReceivedNotificationHandledResponse
}
///
- /// OIDCH_0003: messageReceivedNotification.Skipped
+ /// OIDCH_0003: MessageReceivedNotification.Skipped
///
internal static string OIDCH_0003_MessageReceivedNotificationSkipped
{
@@ -213,15 +221,15 @@ internal static string OIDCH_0003_MessageReceivedNotificationSkipped
}
///
- /// 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.
///
- 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"); }
}
///
- /// OIDCH_0005: unable to unprotect the message.State
+ /// OIDCH_0005: Unable to unprotect the message.State
///
internal static string OIDCH_0005_MessageStateIsInvalid
{
@@ -229,15 +237,15 @@ internal static string OIDCH_0005_MessageStateIsInvalid
}
///
- /// OIDCH_0006_MessageErrorNotNull: '{0}'.
+ /// OIDCH_0006: Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.
///
- 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"); }
}
///
- /// OIDCH_0007: updating configuration
+ /// OIDCH_0007: Updating configuration
///
internal static string OIDCH_0007_UpdatingConfiguration
{
@@ -245,7 +253,7 @@ internal static string OIDCH_0007_UpdatingConfiguration
}
///
- /// OIDCH_0008: securityTokenReceivedNotification.HandledResponse
+ /// OIDCH_0008: SecurityTokenReceivedNotification.HandledResponse
///
internal static string OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse
{
@@ -253,7 +261,7 @@ internal static string OIDCH_0008_SecurityTokenReceivedNotificationHandledRespon
}
///
- /// OIDCH_0009: securityTokenReceivedNotification.Skipped
+ /// OIDCH_0009: SecurityTokenReceivedNotification.Skipped
///
internal static string OIDCH_0009_SecurityTokenReceivedNotificationSkipped
{
@@ -269,7 +277,7 @@ internal static string OIDCH_0010_ValidatedSecurityTokenNotJwt
}
///
- /// 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}'.
///
internal static string OIDCH_0011_UnableToValidateToken
{
@@ -277,7 +285,7 @@ internal static string OIDCH_0011_UnableToValidateToken
}
///
- /// OIDCH_0012: securityTokenValidatedNotification.HandledResponse
+ /// OIDCH_0012: SecurityTokenValidatedNotification.HandledResponse
///
internal static string OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse
{
@@ -285,7 +293,7 @@ internal static string OIDCH_0012_SecurityTokenValidatedNotificationHandledRespo
}
///
- /// OIDCH_0013: securityTokenValidatedNotification.Skipped
+ /// OIDCH_0013: SecurityTokenValidatedNotification.Skipped
///
internal static string OIDCH_0013_SecurityTokenValidatedNotificationSkipped
{
@@ -293,31 +301,31 @@ internal static string OIDCH_0013_SecurityTokenValidatedNotificationSkipped
}
///
- /// OIDCH_0014: 'code' received: '{0}'
+ /// OIDCH_0014: AuthorizationCode received: '{0}'
///
- 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"); }
}
///
- /// OIDCH_0015: codeReceivedNotification.HandledResponse")
+ /// OIDCH_0015: AuthorizationCodeReceivedNotification.HandledResponse
///
- 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"); }
}
///
/// OIDCH_0016: codeReceivedNotification.Skipped
///
- 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"); }
}
///
- /// OIDCH_0017: Exception occurred while processing message
+ /// OIDCH_0017: Exception occurred while processing message.
///
internal static string OIDCH_0017_ExceptionOccurredWhileProcessingMessage
{
@@ -341,11 +349,20 @@ internal static string OIDCH_0019_AuthenticationFailedNotificationSkipped
}
///
- /// OIDCH_0020: 'id_token' received: '{0}'
+ /// OIDCH_0020: 'id_token' received: '{0}'.
///
internal static string OIDCH_0020_IdTokenReceived
{
get { return ResourceManager.GetString("OIDCH_0020_IdTokenReceived"); }
}
+
+ ///
+ /// OIDCH_0021: exception of type 'SecurityTokenSignatureKeyNotFoundException' thrown, Options.ConfigurationManager.RequestRefresh() called.
+ ///
+ internal static string OIDCH_0021_AutomaticConfigurationRefresh
+ {
+ get { return ResourceManager.GetString("OIDCH_0021_AutomaticConfigurationRefresh"); }
+ }
+
}
}
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx
index 3f2ad6022..f998d95ad 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx
@@ -130,7 +130,7 @@
OIDCH_0026: Entering: '{0}'
- OIDCH_0027: converted 401 to 403.
+ OIDCH_0027: Converted 401 to 403.
OIDCH_0028: Response.StatusCode != 401, StatusCode: '{0}'.
@@ -139,10 +139,10 @@
OIDCH_0029: ChallengeContext == null AND !Options.AutomaticAuthentication
- OIDCH_0030: using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
+ OIDCH_0030: Using properties.RedirectUri for 'local redirect' post authentication: '{0}'.
- OIDCH_0031: using Options.RedirectUri for 'redirect_uri': '{0}'.
+ OIDCH_0031: Using Options.RedirectUri for 'redirect_uri': '{0}'.
OIDCH_0032: using the CurrentUri for 'local redirect' post authentication: '{0}'.
@@ -151,13 +151,16 @@
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}'.
- OIDCH_0034: redirectToIdentityProviderNotification.HandledResponse
+ OIDCH_0034: RedirectToIdentityProviderNotification.HandledResponse
- OIDCH_0035: redirectToIdentityProviderNotification.Skipped
+ OIDCH_0035: RedirectToIdentityProviderNotification.Skipped
- 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}'.
+
+
+ OIDCH_0037: RedirectUri is: '{0}'.
OIDCH_0000: Entering: '{0}'.
@@ -166,60 +169,63 @@
OIDCH_0001: MessageReceived: '{0}'.
- OIDCH_0002: messageReceivedNotification.HandledResponse
+ OIDCH_0002: MessageReceivedNotification.HandledResponse
- OIDCH_0003: messageReceivedNotification.Skipped
+ OIDCH_0003: MessageReceivedNotification.Skipped
-
- 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.
-
- OIDCH_0005: unable to unprotect the message.State
+
+ OIDCH_0005: Unable to unprotect the message.State.
-
- OIDCH_0006_MessageErrorNotNull: '{0}'.
+
+ OIDCH_0006: Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.
- OIDCH_0007: updating configuration
+ OIDCH_0007: Updating configuration
- OIDCH_0008: securityTokenReceivedNotification.HandledResponse
+ OIDCH_0008: SecurityTokenReceivedNotification.HandledResponse
- OIDCH_0009: securityTokenReceivedNotification.Skipped
+ OIDCH_0009: SecurityTokenReceivedNotification.Skipped
OIDCH_0010: Validated Security Token must be a JwtSecurityToken was: '{0}'.
- 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}'."
- OIDCH_0012: securityTokenValidatedNotification.HandledResponse
+ OIDCH_0012: SecurityTokenValidatedNotification.HandledResponse
- OIDCH_0013: securityTokenValidatedNotification.Skipped
+ OIDCH_0013: SecurityTokenValidatedNotification.Skipped
-
- OIDCH_0014: 'code' received: '{0}'
+
+ OIDCH_0014: AuthorizationCode received: '{0}'.
-
- OIDCH_0015: codeReceivedNotification.HandledResponse
+
+ OIDCH_0015: AuthorizationCodeReceivedNotification.HandledResponse
-
- OIDCH_0016: codeReceivedNotification.Skipped
+
+ OIDCH_0016: AuthorizationCodeReceivedNotification.Skipped
-
- OIDCH_0017: Exception occurred while processing message
+
+ OIDCH_0017: Exception occurred while processing message.
- OIDCH_0018: authenticationFailedNotification.HandledResponse
+ OIDCH_0018: AuthenticationFailedNotification.HandledResponse
- OIDCH_0019: authenticationFailedNotification.Skipped
+ OIDCH_0019: AuthenticationFailedNotification.Skipped
OIDCH_0020: 'id_token' received: '{0}'
-
\ No newline at end of file
+
+ OIDCH_0021: exception of type 'SecurityTokenSignatureKeyNotFoundException' thrown, Options.ConfigurationManager.RequestRefresh() called.
+
+
diff --git a/src/Microsoft.AspNet.Authentication/Notifications/RedirectToIdentityProviderNotification.cs b/src/Microsoft.AspNet.Authentication/Notifications/RedirectToIdentityProviderNotification.cs
index a4b2d979e..d8b7dcac9 100644
--- a/src/Microsoft.AspNet.Authentication/Notifications/RedirectToIdentityProviderNotification.cs
+++ b/src/Microsoft.AspNet.Authentication/Notifications/RedirectToIdentityProviderNotification.cs
@@ -1,16 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using Microsoft.AspNet.Http;
+using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Authentication.Notifications
{
+ ///
+ /// When a user configures the to be notified prior to redirecting to an IdentityProvider
+ /// an instance of is passed to the 'RedirectToIdentityProviderNotification".
+ ///
+ /// protocol specific message.
+ /// protocol specific options.
public class RedirectToIdentityProviderNotification : BaseNotification
{
- 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; }
+ ///
+ /// Gets or sets the .
+ ///
+ /// if 'value' is null.
+ public TMessage ProtocolMessage { get; [param: NotNull] set; }
}
-}
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/AuthenticationPropertiesFormaterKeyValue.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/AuthenticationPropertiesFormaterKeyValue.cs
new file mode 100644
index 000000000..683f03563
--- /dev/null
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/AuthenticationPropertiesFormaterKeyValue.cs
@@ -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
+{
+ ///
+ /// This formatter creates an easy to read string of the format: "'key1' 'value1' ..."
+ ///
+ public class AuthenticationPropertiesFormaterKeyValue : ISecureDataFormat
+ {
+ 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.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;
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/ExpectedQueryValues.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/ExpectedQueryValues.cs
new file mode 100644
index 000000000..ddf7a0e7e
--- /dev/null
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/ExpectedQueryValues.cs
@@ -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
+{
+ ///
+ /// This helper class is used to check that query string parameters are as expected.
+ ///
+ 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 parameters)
+ {
+ var errors = new List();
+ 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); }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/InMemoryLogger.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/InMemoryLogger.cs
new file mode 100644
index 000000000..67a2668c6
--- /dev/null
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/InMemoryLogger.cs
@@ -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 _logEntries = new List();
+
+ 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