diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Notifications/AuthorizationCodeRedeemedNotification.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Notifications/AuthorizationCodeRedeemedNotification.cs
new file mode 100644
index 0000000000..5d8a106867
--- /dev/null
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Notifications/AuthorizationCodeRedeemedNotification.cs
@@ -0,0 +1,36 @@
+using System;
+using Microsoft.AspNet.Authentication.OpenIdConnect;
+using Microsoft.AspNet.Http;
+using Microsoft.IdentityModel.Protocols;
+
+namespace Microsoft.AspNet.Authentication.Notifications
+{
+ ///
+ /// This Notification can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint.
+ ///
+ public class AuthorizationCodeRedeemedNotification : BaseNotification
+ {
+ ///
+ /// Creates a
+ ///
+ public AuthorizationCodeRedeemedNotification(HttpContext context, OpenIdConnectAuthenticationOptions options) : base(context, options)
+ {
+ }
+
+ ///
+ /// Gets or sets the 'code'.
+ ///
+ public string Code { get; set; }
+
+ ///
+ /// Gets or sets the that contains the tokens and json response received after redeeming the code at the token endpoint.
+ ///
+ public OpenIdConnectTokenEndpointResponse TokenEndpointResponse { get; set; }
+
+ ///
+ /// Gets or sets the .
+ ///
+ public OpenIdConnectMessage ProtocolMessage { get; set; }
+
+ }
+}
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
index 6981f48e56..b3203bb8ab 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
@@ -6,6 +6,8 @@ using System.Globalization;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Authentication.Notifications;
@@ -16,6 +18,7 @@ using Microsoft.Framework.Caching.Distributed;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Logging;
using Microsoft.IdentityModel.Protocols;
+using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
@@ -28,6 +31,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
private const string UriSchemeDelimiter = "://";
private OpenIdConnectConfiguration _configuration;
+ protected HttpClient Backchannel { get; private set; }
+
+ public OpenIdConnectAuthenticationHandler(HttpClient backchannel)
+ {
+ Backchannel = backchannel;
+ }
+
///
/// Handles Signout
///
@@ -116,17 +126,6 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
properties.RedirectUri = CurrentUri;
}
- 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.IsNullOrEmpty(Options.RedirectUri))
- {
- properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey, Options.RedirectUri);
- }
-
if (_configuration == null && Options.ConfigurationManager != null)
{
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
@@ -195,6 +194,19 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
message = redirectToIdentityProviderNotification.ProtocolMessage;
}
+ var redirectUriForCode = message.RedirectUri;
+ if (string.IsNullOrEmpty(redirectUriForCode))
+ {
+ Logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri);
+ redirectUriForCode = Options.RedirectUri;
+ }
+
+ if (!string.IsNullOrEmpty(redirectUriForCode))
+ {
+ // When redeeming a 'code' for an AccessToken, this value is needed
+ properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey, redirectUriForCode);
+ }
+
message.State = Options.StateDataFormat.Protect(properties);
var redirectUri = message.CreateAuthenticationRequestUrl();
@@ -243,27 +255,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
try
{
- if (Logger.IsEnabled(LogLevel.Debug))
- {
- Logger.LogDebug(Resources.OIDCH_0001_MessageReceived, message.BuildRedirectUrl());
- }
-
- var messageReceivedNotification =
- new MessageReceivedNotification(Context, Options)
- {
- ProtocolMessage = message
- };
-
- await Options.Notifications.MessageReceived(messageReceivedNotification);
+ var messageReceivedNotification = await RunMessageReceivedNotificationAsync(message);
if (messageReceivedNotification.HandledResponse)
{
- Logger.LogVerbose(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
return messageReceivedNotification.AuthenticationTicket;
}
-
- if (messageReceivedNotification.Skipped)
+ else if (messageReceivedNotification.Skipped)
{
- Logger.LogVerbose(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
return null;
}
@@ -302,178 +300,19 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
_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.IsNullOrEmpty(message.IdToken))
+ if (string.IsNullOrEmpty(message.IdToken) && !string.IsNullOrEmpty(message.Code))
{
- Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
- var securityTokenReceivedNotification =
- new SecurityTokenReceivedNotification(Context, Options)
- {
- ProtocolMessage = message,
- };
-
- await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
- if (securityTokenReceivedNotification.HandledResponse)
- {
- Logger.LogVerbose(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
- return securityTokenReceivedNotification.AuthenticationTicket;
- }
-
- if (securityTokenReceivedNotification.Skipped)
- {
- Logger.LogVerbose(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
- return null;
- }
-
- // Copy and augment to avoid cross request race conditions for updated configurations.
- var validationParameters = Options.TokenValidationParameters.Clone();
- if (_configuration != null)
- {
- if (string.IsNullOrEmpty(validationParameters.ValidIssuer))
- {
- validationParameters.ValidIssuer = _configuration.Issuer;
- }
- else if (!string.IsNullOrEmpty(_configuration.Issuer))
- {
- validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(new[] { _configuration.Issuer }) ?? new[] { _configuration.Issuer };
- }
-
- validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys) ?? _configuration.SigningKeys;
- }
-
- SecurityToken validatedToken = null;
- ClaimsPrincipal principal = null;
- foreach (var validator in Options.SecurityTokenValidators)
- {
- if (validator.CanReadToken(message.IdToken))
- {
- principal = validator.ValidateToken(message.IdToken, validationParameters, out validatedToken);
- jwt = validatedToken as JwtSecurityToken;
- if (jwt == null)
- {
- Logger.LogError(Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType());
- throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
- }
- }
- }
-
- if (validatedToken == null)
- {
- Logger.LogError(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.IsNullOrEmpty(message.SessionState))
- {
- ticket.Properties.Items[OpenIdConnectSessionProperties.SessionState] = message.SessionState;
- }
-
- if (_configuration != null && !string.IsNullOrEmpty(_configuration.CheckSessionIframe))
- {
- ticket.Properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
- }
-
- // Rename?
- if (Options.UseTokenLifetime)
- {
- var issued = validatedToken.ValidFrom;
- if (issued != DateTime.MinValue)
- {
- ticket.Properties.IssuedUtc = issued;
- }
-
- var expires = validatedToken.ValidTo;
- if (expires != DateTime.MinValue)
- {
- ticket.Properties.ExpiresUtc = expires;
- }
- }
-
- var securityTokenValidatedNotification =
- new SecurityTokenValidatedNotification(Context, Options)
- {
- AuthenticationTicket = ticket,
- ProtocolMessage = message
- };
-
- await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
- if (securityTokenValidatedNotification.HandledResponse)
- {
- Logger.LogVerbose(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
- return securityTokenValidatedNotification.AuthenticationTicket;
- }
-
- if (securityTokenValidatedNotification.Skipped)
- {
- Logger.LogVerbose(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
- return null;
- }
-
- string nonce = jwt.Payload.Nonce;
- if (Options.CacheNonces)
- {
- if (await Options.NonceCache.GetAsync(nonce) != null)
- {
- await Options.NonceCache.RemoveAsync(nonce);
- }
- else
- {
- // If the nonce cannot be removed, it was
- // already used and MUST be rejected.
- nonce = null;
- }
- }
- else
- {
- nonce = ReadNonceCookie(nonce);
- }
-
- var protocolValidationContext = new OpenIdConnectProtocolValidationContext
- {
- AuthorizationCode = message.Code,
- Nonce = nonce,
- };
-
- Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
+ return await HandleCodeOnlyFlow(message, properties);
}
-
- if (message.Code != null)
+ else if (!string.IsNullOrEmpty(message.IdToken))
{
- Logger.LogDebug(Resources.OIDCH_0014_AuthorizationCodeReceived, message.Code);
- if (ticket == null)
- {
- ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme);
- }
-
- var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
- {
- AuthenticationTicket = ticket,
- Code = message.Code,
- JwtSecurityToken = jwt,
- ProtocolMessage = message,
- RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey) ?
- ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey] : string.Empty,
- };
-
- await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
- if (authorizationCodeReceivedNotification.HandledResponse)
- {
- Logger.LogVerbose(Resources.OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse);
- return authorizationCodeReceivedNotification.AuthenticationTicket;
- }
-
- if (authorizationCodeReceivedNotification.Skipped)
- {
- Logger.LogVerbose(Resources.OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped);
- return null;
- }
+ return await HandleIdTokenFlows(message, properties);
+ }
+ else
+ {
+ Logger.LogDebug(Resources.OIDCH_0045_Id_Token_Code_Missing);
+ return null;
}
-
- return ticket;
}
catch (Exception exception)
{
@@ -489,23 +328,13 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
}
}
- var authenticationFailedNotification =
- new AuthenticationFailedNotification(Context, Options)
- {
- ProtocolMessage = message,
- Exception = exception
- };
-
- await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
+ var authenticationFailedNotification = await RunAuthenticationFailedNotificationAsync(message, exception);
if (authenticationFailedNotification.HandledResponse)
{
- Logger.LogVerbose(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
return authenticationFailedNotification.AuthenticationTicket;
}
-
- if (authenticationFailedNotification.Skipped)
+ else if (authenticationFailedNotification.Skipped)
{
- Logger.LogVerbose(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
return null;
}
@@ -513,6 +342,215 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
}
}
+ private async Task HandleCodeOnlyFlow(OpenIdConnectMessage message, AuthenticationProperties properties)
+ {
+ AuthenticationTicket ticket = null;
+ JwtSecurityToken jwt = null;
+
+ OpenIdConnectTokenEndpointResponse tokenEndpointResponse = null;
+ string idToken = null;
+ var authorizationCodeReceivedNotification = await RunAuthorizationCodeReceivedNotificationAsync(message, properties, ticket, jwt);
+ if (authorizationCodeReceivedNotification.HandledResponse)
+ {
+ return authorizationCodeReceivedNotification.AuthenticationTicket;
+ }
+ else if (authorizationCodeReceivedNotification.Skipped)
+ {
+ return null;
+ }
+
+ // Redeeming authorization code for tokens
+ Logger.LogDebug(Resources.OIDCH_0038_Redeeming_Auth_Code, message.Code);
+
+ tokenEndpointResponse = await RedeemAuthorizationCodeAsync(message.Code, authorizationCodeReceivedNotification.RedirectUri);
+ idToken = tokenEndpointResponse.Message.IdToken;
+
+ var authorizationCodeRedeemedNotification = await RunAuthorizationCodeRedeemedNotificationAsync(message, tokenEndpointResponse);
+ if (authorizationCodeRedeemedNotification.HandledResponse)
+ {
+ return authorizationCodeRedeemedNotification.AuthenticationTicket;
+ }
+ else if (authorizationCodeRedeemedNotification.Skipped)
+ {
+ return null;
+ }
+
+ // no need to validate signature when token is received using "code flow" as per spec [http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation].
+ var validationParameters = Options.TokenValidationParameters.Clone();
+ validationParameters.ValidateSignature = false;
+
+ ticket = ValidateToken(idToken, message, properties, validationParameters, out jwt);
+
+ if (Options.GetClaimsFromUserInfoEndpoint)
+ {
+ Logger.LogDebug(Resources.OIDCH_0040_Sending_Request_UIEndpoint);
+ ticket = await GetUserInformationAsync(properties, tokenEndpointResponse.Message, ticket);
+ }
+
+ var securityTokenValidatedNotification = await RunSecurityTokenValidatedNotificationAsync(message, ticket);
+ if (securityTokenValidatedNotification.HandledResponse)
+ {
+ return securityTokenValidatedNotification.AuthenticationTicket;
+ }
+ else if (securityTokenValidatedNotification.Skipped)
+ {
+ return null;
+ }
+
+ // If id_token is received using code only flow, no need to validate chash.
+ await ValidateOpenIdConnectProtocolAsync(jwt, message, false);
+
+ return ticket;
+ }
+
+ private async Task HandleIdTokenFlows(OpenIdConnectMessage message, AuthenticationProperties properties)
+ {
+ AuthenticationTicket ticket = null;
+ JwtSecurityToken jwt = null;
+
+ var securityTokenReceivedNotification = await RunSecurityTokenReceivedNotificationAsync(message);
+ if (securityTokenReceivedNotification.HandledResponse)
+ {
+ return securityTokenReceivedNotification.AuthenticationTicket;
+ }
+ else if (securityTokenReceivedNotification.Skipped)
+ {
+ return null;
+ }
+
+ var validationParameters = Options.TokenValidationParameters.Clone();
+ ticket = ValidateToken(message.IdToken, message, properties, validationParameters, out jwt);
+
+ var securityTokenValidatedNotification = await RunSecurityTokenValidatedNotificationAsync(message, ticket);
+ if (securityTokenValidatedNotification.HandledResponse)
+ {
+ return securityTokenValidatedNotification.AuthenticationTicket;
+ }
+ else if (securityTokenValidatedNotification.Skipped)
+ {
+ return null;
+ }
+
+ await ValidateOpenIdConnectProtocolAsync(jwt, message);
+
+ if (message.Code != null)
+ {
+ var authorizationCodeReceivedNotification = await RunAuthorizationCodeReceivedNotificationAsync(message, properties, ticket, jwt);
+ if (authorizationCodeReceivedNotification.HandledResponse)
+ {
+ return authorizationCodeReceivedNotification.AuthenticationTicket;
+ }
+ else if (authorizationCodeReceivedNotification.Skipped)
+ {
+ return null;
+ }
+ }
+
+ return ticket;
+ }
+
+ ///
+ /// Redeems the authorization code for tokens at the token endpoint
+ ///
+ /// The authorization code to redeem.
+ /// Uri that was passed in the request sent for the authorization code.
+ /// OpenIdConnect message that has tokens inside it.
+ protected virtual async Task RedeemAuthorizationCodeAsync(string authorizationCode, string redirectUri)
+ {
+ var openIdMessage = new OpenIdConnectMessage()
+ {
+ ClientId = Options.ClientId,
+ ClientSecret = Options.ClientSecret,
+ Code = authorizationCode,
+ GrantType = "authorization_code",
+ RedirectUri = redirectUri
+ };
+
+ var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint);
+ requestMessage.Content = new FormUrlEncodedContent(openIdMessage.Parameters);
+ var responseMessage = await Backchannel.SendAsync(requestMessage);
+ responseMessage.EnsureSuccessStatusCode();
+ var tokenResonse = await responseMessage.Content.ReadAsStringAsync();
+ var jsonTokenResponse = JObject.Parse(tokenResonse);
+ return new OpenIdConnectTokenEndpointResponse(jsonTokenResponse);
+ }
+
+ ///
+ /// Goes to UserInfo endpoint to retrieve additional claims and add any unique claims to the given identity.
+ ///
+ /// Authentication Properties
+ /// message that is being processed
+ /// authentication ticket with claims principal and identities
+ /// Authentication ticket with identity with additional claims, if any.
+ protected virtual async Task GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket)
+ {
+ string userInfoEndpoint = null;
+ if (_configuration != null)
+ {
+ userInfoEndpoint = _configuration.UserInfoEndpoint;
+ }
+
+ if (string.IsNullOrEmpty(userInfoEndpoint))
+ {
+ Logger.LogWarning(Resources.OIDCH_0046_UserInfo_Endpoint_Not_Set);
+ return ticket;
+ }
+
+ var requestMessage = new HttpRequestMessage(HttpMethod.Get, userInfoEndpoint);
+ requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", message.AccessToken);
+ var responseMessage = await Backchannel.SendAsync(requestMessage);
+ responseMessage.EnsureSuccessStatusCode();
+ var userInfoResponse = await responseMessage.Content.ReadAsStringAsync();
+ var user = JObject.Parse(userInfoResponse);
+
+ var identity = (ClaimsIdentity)ticket.Principal.Identity;
+ var subjectClaimType = identity.FindFirst(ClaimTypes.NameIdentifier);
+ if (subjectClaimType == null)
+ {
+ Logger.LogError(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0041_Subject_Claim_Not_Found, identity.ToString()));
+ return ticket;
+ }
+
+ var userInfoSubjectClaimValue = user.Value(JwtRegisteredClaimNames.Sub);
+
+ // check if the sub claim matches
+ if (userInfoSubjectClaimValue == null || !string.Equals(userInfoSubjectClaimValue, subjectClaimType.Value, StringComparison.Ordinal))
+ {
+ Logger.LogError(Resources.OIDCH_0039_Subject_Claim_Mismatch);
+ return ticket;
+ }
+
+ foreach (var claim in identity.Claims)
+ {
+ // If this claimType is mapped by the JwtSeurityTokenHandler, then this property will be set
+ var shortClaimTypeName = claim.Properties.ContainsKey(JwtSecurityTokenHandler.ShortClaimTypeProperty) ?
+ claim.Properties[JwtSecurityTokenHandler.ShortClaimTypeProperty] : string.Empty;
+
+ // checking if claim in the identity (generated from id_token) has the same type as a claim retrieved from userinfo endpoint
+ JToken value;
+ var isClaimIncluded = user.TryGetValue(claim.Type, out value) || user.TryGetValue(shortClaimTypeName, out value);
+
+ // if a same claim exists (matching both type and value) both in id_token identity and userinfo response, remove the json entry from the userinfo response
+ if (isClaimIncluded && claim.Value.Equals(value.ToString(), StringComparison.Ordinal))
+ {
+ if (!user.Remove(claim.Type))
+ {
+ user.Remove(shortClaimTypeName);
+ }
+ }
+ }
+
+ // adding remaining unique claims from userinfo endpoint to the identity
+ foreach (var pair in user)
+ {
+ JToken value;
+ var claimValue = user.TryGetValue(pair.Key, out value) ? value.ToString() : null;
+ identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, Options.ClaimsIssuer));
+ }
+
+ return new AuthenticationTicket(new ClaimsPrincipal(identity), ticket.Properties, ticket.AuthenticationScheme);
+ }
+
///
/// Adds the nonce to .
///
@@ -609,6 +647,251 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
}
}
+ private async Task> RunMessageReceivedNotificationAsync(OpenIdConnectMessage message)
+ {
+ Logger.LogDebug(Resources.OIDCH_0001_MessageReceived, message.BuildRedirectUrl());
+ var messageReceivedNotification =
+ new MessageReceivedNotification(Context, Options)
+ {
+ ProtocolMessage = message
+ };
+
+ await Options.Notifications.MessageReceived(messageReceivedNotification);
+ if (messageReceivedNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0002_MessageReceivedNotificationHandledResponse);
+ }
+ else if (messageReceivedNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0003_MessageReceivedNotificationSkipped);
+ }
+
+ return messageReceivedNotification;
+ }
+
+ private async Task RunAuthorizationCodeReceivedNotificationAsync(OpenIdConnectMessage message, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt)
+ {
+ var redirectUri = properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey) ?
+ properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriForCodePropertiesKey] : Options.RedirectUri;
+
+ Logger.LogDebug(Resources.OIDCH_0014_AuthorizationCodeReceived, message.Code);
+
+ var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options)
+ {
+ Code = message.Code,
+ ProtocolMessage = message,
+ RedirectUri = redirectUri,
+ AuthenticationTicket = ticket,
+ JwtSecurityToken = jwt
+ };
+
+ await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification);
+ if (authorizationCodeReceivedNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0015_AuthorizationCodeReceivedNotificationHandledResponse);
+ }
+ else if (authorizationCodeReceivedNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0016_AuthorizationCodeReceivedNotificationSkipped);
+ }
+
+ return authorizationCodeReceivedNotification;
+ }
+
+ private async Task RunAuthorizationCodeRedeemedNotificationAsync(OpenIdConnectMessage message, OpenIdConnectTokenEndpointResponse tokenEndpointResponse)
+ {
+ Logger.LogDebug(Resources.OIDCH_0042_AuthorizationCodeRedeemed, message.Code);
+ var authorizationCodeRedeemedNotification = new AuthorizationCodeRedeemedNotification(Context, Options)
+ {
+ Code = message.Code,
+ ProtocolMessage = message,
+ TokenEndpointResponse = tokenEndpointResponse
+ };
+
+ await Options.Notifications.AuthorizationCodeRedeemed(authorizationCodeRedeemedNotification);
+ if (authorizationCodeRedeemedNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0043_AuthorizationCodeRedeemedNotificationHandledResponse);
+ }
+ else if (authorizationCodeRedeemedNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0044_AuthorizationCodeRedeemedNotificationSkipped);
+ }
+ return authorizationCodeRedeemedNotification;
+ }
+
+ private async Task> RunSecurityTokenReceivedNotificationAsync(OpenIdConnectMessage message)
+ {
+ Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken);
+ var securityTokenReceivedNotification =
+ new SecurityTokenReceivedNotification(Context, Options)
+ {
+ ProtocolMessage = message,
+ };
+
+ await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification);
+ if (securityTokenReceivedNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0008_SecurityTokenReceivedNotificationHandledResponse);
+ }
+ else if (securityTokenReceivedNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0009_SecurityTokenReceivedNotificationSkipped);
+ }
+
+ return securityTokenReceivedNotification;
+ }
+
+ private async Task> RunSecurityTokenValidatedNotificationAsync(OpenIdConnectMessage message, AuthenticationTicket ticket)
+ {
+ var securityTokenValidatedNotification =
+ new SecurityTokenValidatedNotification(Context, Options)
+ {
+ AuthenticationTicket = ticket,
+ ProtocolMessage = message
+ };
+
+ await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
+ if (securityTokenValidatedNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0012_SecurityTokenValidatedNotificationHandledResponse);
+ }
+ else if (securityTokenValidatedNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0013_SecurityTokenValidatedNotificationSkipped);
+ }
+
+ return securityTokenValidatedNotification;
+ }
+
+ private async Task> RunAuthenticationFailedNotificationAsync(OpenIdConnectMessage message, Exception exception)
+ {
+ var authenticationFailedNotification =
+ new AuthenticationFailedNotification(Context, Options)
+ {
+ ProtocolMessage = message,
+ Exception = exception
+ };
+
+ await Options.Notifications.AuthenticationFailed(authenticationFailedNotification);
+ if (authenticationFailedNotification.HandledResponse)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0018_AuthenticationFailedNotificationHandledResponse);
+ }
+ else if (authenticationFailedNotification.Skipped)
+ {
+ Logger.LogVerbose(Resources.OIDCH_0019_AuthenticationFailedNotificationSkipped);
+ }
+
+ return authenticationFailedNotification;
+ }
+
+ private AuthenticationTicket ValidateToken(string idToken, OpenIdConnectMessage message, AuthenticationProperties properties, TokenValidationParameters validationParameters, out JwtSecurityToken jwt)
+ {
+ AuthenticationTicket ticket = null;
+ jwt = null;
+
+ if (_configuration != null)
+ {
+ if (string.IsNullOrEmpty(validationParameters.ValidIssuer))
+ {
+ validationParameters.ValidIssuer = _configuration.Issuer;
+ }
+ else if (!string.IsNullOrEmpty(_configuration.Issuer))
+ {
+ validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(new[] { _configuration.Issuer }) ?? new[] { _configuration.Issuer };
+ }
+
+ validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys) ?? _configuration.SigningKeys;
+ }
+
+ SecurityToken validatedToken = null;
+ ClaimsPrincipal principal = null;
+ foreach (var validator in Options.SecurityTokenValidators)
+ {
+ if (validator.CanReadToken(idToken))
+ {
+ principal = validator.ValidateToken(idToken, validationParameters, out validatedToken);
+ jwt = validatedToken as JwtSecurityToken;
+ if (jwt == null)
+ {
+ Logger.LogError(Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType());
+ throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0010_ValidatedSecurityTokenNotJwt, validatedToken?.GetType()));
+ }
+ }
+ }
+
+ if (validatedToken == null)
+ {
+ Logger.LogError(Resources.OIDCH_0011_UnableToValidateToken, idToken);
+ throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.OIDCH_0011_UnableToValidateToken, idToken));
+ }
+
+ ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
+ if (!string.IsNullOrEmpty(message.SessionState))
+ {
+ ticket.Properties.Items[OpenIdConnectSessionProperties.SessionState] = message.SessionState;
+ }
+
+ if (_configuration != null && !string.IsNullOrEmpty(_configuration.CheckSessionIframe))
+ {
+ ticket.Properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe;
+ }
+
+ // Rename?
+ if (Options.UseTokenLifetime)
+ {
+ var issued = validatedToken.ValidFrom;
+ if (issued != DateTime.MinValue)
+ {
+ ticket.Properties.IssuedUtc = issued;
+ }
+
+ var expires = validatedToken.ValidTo;
+ if (expires != DateTime.MinValue)
+ {
+ ticket.Properties.ExpiresUtc = expires;
+ }
+ }
+
+ return ticket;
+ }
+
+ private async Task ValidateOpenIdConnectProtocolAsync(JwtSecurityToken jwt, OpenIdConnectMessage message, bool ValidateCHash = true)
+ {
+ string nonce = jwt.Payload.Nonce;
+ if (Options.CacheNonces)
+ {
+ if (await Options.NonceCache.GetAsync(nonce) != null)
+ {
+ await Options.NonceCache.RemoveAsync(nonce);
+ }
+ else
+ {
+ // If the nonce cannot be removed, it was
+ // already used and MUST be rejected.
+ nonce = null;
+ }
+ }
+ else
+ {
+ nonce = ReadNonceCookie(nonce);
+ }
+
+ var protocolValidationContext = new OpenIdConnectProtocolValidationContext
+ {
+ Nonce = nonce
+ };
+
+ // If authorization code is null, protocol validator does not validate the chash
+ if (ValidateCHash)
+ {
+ protocolValidationContext.AuthorizationCode = message.Code;
+ }
+
+ Options.ProtocolValidator.Validate(jwt, protocolValidationContext);
+ }
+
///
/// Calls InvokeReplyPathAsync
///
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs
index 532ca9518e..29f1144aaf 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs
@@ -103,6 +103,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
Options.TokenValidationParameters.ValidAudience = Options.ClientId;
}
+ Backchannel = new HttpClient(ResolveHttpMessageHandler(Options));
+ Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OpenIdConnect middleware");
+ Backchannel.Timeout = Options.BackchannelTimeout;
+ Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
+
if (Options.ConfigurationManager == null)
{
if (Options.Configuration != null)
@@ -122,10 +127,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
Options.MetadataAddress += ".well-known/openid-configuration";
}
- var httpClient = new HttpClient(ResolveHttpMessageHandler(Options));
- httpClient.Timeout = Options.BackchannelTimeout;
- httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
- Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, httpClient);
+ Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, Backchannel);
}
}
@@ -137,13 +139,15 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
}
}
+ protected HttpClient Backchannel { get; private set; }
+
///
/// Provides the object for processing authentication-related requests.
///
/// An configured with the supplied to the constructor.
protected override AuthenticationHandler CreateHandler()
{
- return new OpenIdConnectAuthenticationHandler();
+ return new OpenIdConnectAuthenticationHandler(Backchannel);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationNotifications.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationNotifications.cs
index 11c8c9ccc2..81eeb08d32 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationNotifications.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationNotifications.cs
@@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
AuthenticationFailed = notification => Task.FromResult(0);
AuthorizationCodeReceived = notification => Task.FromResult(0);
+ AuthorizationCodeRedeemed = notificaion => Task.FromResult(0);
MessageReceived = notification => Task.FromResult(0);
SecurityTokenReceived = notification => Task.FromResult(0);
SecurityTokenValidated = notification => Task.FromResult(0);
@@ -36,6 +37,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
///
public Func AuthorizationCodeReceived { get; set; }
+ ///
+ /// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
+ ///
+ public Func AuthorizationCodeRedeemed { get; set; }
+
///
/// Invoked when a protocol message is first received.
///
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs
index 05b33548c6..df8f0e9d58 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs
@@ -56,6 +56,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
AuthenticationScheme = authenticationScheme;
BackchannelTimeout = TimeSpan.FromMinutes(1);
Caption = OpenIdConnectAuthenticationDefaults.Caption;
+ GetClaimsFromUserInfoEndpoint = false;
ProtocolValidator = new OpenIdConnectProtocolValidator();
RefreshOnIssuerKeyNotFound = true;
ResponseMode = OpenIdConnectResponseModes.FormPost;
@@ -164,6 +165,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
///
public bool DefaultToCurrentUriOnRedirect { get; set; }
+ ///
+ /// Boolean to set whether the middleware should go to user info endpoint to retrieve additional claims or not after creating an identity from id_token received from token endpoint.
+ ///
+ public bool GetClaimsFromUserInfoEndpoint { get; set; }
+
///
/// Gets or sets the discovery endpoint for obtaining metadata
///
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectTokenEndpointResponse.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectTokenEndpointResponse.cs
new file mode 100644
index 0000000000..a0cc6ccb85
--- /dev/null
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectTokenEndpointResponse.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.IdentityModel.Protocols;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.AspNet.Authentication.OpenIdConnect
+{
+ ///
+ /// Class to store the response returned from token endpoint
+ ///
+ public class OpenIdConnectTokenEndpointResponse
+ {
+ public OpenIdConnectTokenEndpointResponse(JObject jsonResponse)
+ {
+ JsonResponse = jsonResponse;
+ Message = new OpenIdConnectMessage()
+ {
+ AccessToken = JsonResponse.Value(OpenIdConnectParameterNames.AccessToken),
+ IdToken = JsonResponse.Value(OpenIdConnectParameterNames.IdToken),
+ TokenType = JsonResponse.Value(OpenIdConnectParameterNames.TokenType),
+ ExpiresIn = JsonResponse.Value(OpenIdConnectParameterNames.ExpiresIn)
+ };
+ }
+
+ ///
+ /// OpenIdConnect message that contains the id token and access tokens
+ ///
+ public OpenIdConnectMessage Message { get; set; }
+
+ ///
+ /// Json response returned from the token endpoint
+ ///
+ public JObject JsonResponse { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs
index 67c06b96d8..cdef48e368 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs
@@ -180,6 +180,78 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
get { return ResourceManager.GetString("OIDCH_0037_RedirectUri"); }
}
+ ///
+ /// OIDCH_0038: Id Token is null. Redeeming code : {0} for tokens.
+ ///
+ internal static string OIDCH_0038_Redeeming_Auth_Code
+ {
+ get { return ResourceManager.GetString("OIDCH_0038_Redeeming_Auth_Code"); }
+ }
+
+ ///
+ /// OIDCH_0039: Subject claim received from userinfo endpoint does not match the one in the id token.
+ ///
+ internal static string OIDCH_0039_Subject_Claim_Mismatch
+ {
+ get { return ResourceManager.GetString("OIDCH_0039_Subject_Claim_Mismatch"); }
+ }
+
+ ///
+ /// OIDCH_0040: Sending request to user info endpoint for retrieving claims.
+ ///
+ internal static string OIDCH_0040_Sending_Request_UIEndpoint
+ {
+ get { return ResourceManager.GetString("OIDCH_0040_Sending_Request_UIEndpoint"); }
+ }
+
+ ///
+ /// OIDCH_0041: Subject claim not found in {0}.
+ ///
+ internal static string OIDCH_0041_Subject_Claim_Not_Found
+ {
+ get { return ResourceManager.GetString("OIDCH_0041_Subject_Claim_Not_Found"); }
+ }
+
+ ///
+ /// OIDCH_0042: AuthorizationCode redeemed: '{0}'
+ ///
+ internal static string OIDCH_0042_AuthorizationCodeRedeemed
+ {
+ get { return ResourceManager.GetString("OIDCH_0042_AuthorizationCodeRedeemed"); }
+ }
+
+ ///
+ /// OIDCH_0043: AuthorizationCodeRedeemedNotification.HandledResponse
+ ///
+ internal static string OIDCH_0043_AuthorizationCodeRedeemedNotificationHandledResponse
+ {
+ get { return ResourceManager.GetString("OIDCH_0043_AuthorizationCodeRedeemedNotificationHandledResponse"); }
+ }
+
+ ///
+ /// OIDCH_0044: AuthorizationCodeRedeemedNotification.Skipped
+ ///
+ internal static string OIDCH_0044_AuthorizationCodeRedeemedNotificationSkipped
+ {
+ get { return ResourceManager.GetString("OIDCH_0044_AuthorizationCodeRedeemedNotificationSkipped"); }
+ }
+
+ ///
+ /// OIDCH_0045: Cannot process the message.Both id_token and code are missing.
+ ///
+ internal static string OIDCH_0045_Id_Token_Code_Missing
+ {
+ get { return ResourceManager.GetString("OIDCH_0045_Id_Token_Code_Missing"); }
+ }
+
+ ///
+ /// OIDCH_0046: UserInfo endpoint is not set. Request to retrieve claims from userinfo endpoint cannot be completed.
+ ///
+ internal static string OIDCH_0046_UserInfo_Endpoint_Not_Set
+ {
+ get { return ResourceManager.GetString("OIDCH_0046_UserInfo_Endpoint_Not_Set"); }
+ }
+
///
/// OIDCH_0000: Entering: '{0}'.
///
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx
index f998d95ad7..fe1d1837d5 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx
@@ -162,6 +162,15 @@
OIDCH_0037: RedirectUri is: '{0}'.
+
+ OIDCH_0038: Id Token is null. Redeeming code : {0} for tokens.
+
+
+ OIDCH_0039: Subject claim received from userinfo endpoint does not match the one in the id token.
+
+
+ OIDCH_0040: Sending request to user info endpoint for retrieving claims.
+
OIDCH_0000: Entering: '{0}'.
@@ -228,4 +237,22 @@
OIDCH_0021: exception of type 'SecurityTokenSignatureKeyNotFoundException' thrown, Options.ConfigurationManager.RequestRefresh() called.
-
+
+ OIDCH_0041: Subject claim not found in {0}.
+
+
+ OIDCH_0042: Authorization Code redeemed: '{0}'
+
+
+ OIDCH_0043: AuthorizationCodeRedeemedNotification.HandledResponse
+
+
+ OIDCH_0044: AuthorizationCodeRedeemedNotification.Skipped
+
+
+ OIDCH_0045: Cannot process the message. Both id_token and code are missing.
+
+
+ OIDCH_0046: UserInfo endpoint is not set. Request to retrieve claims from userinfo endpoint cannot be completed.
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/LoggingUtilities.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/LoggingUtilities.cs
index 64f17d967c..f8886e4842 100644
--- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/LoggingUtilities.cs
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/LoggingUtilities.cs
@@ -43,6 +43,12 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
{ "OIDCH_0020:", LogLevel.Debug },
{ "OIDCH_0021:", LogLevel.Verbose },
{ "OIDCH_0026:", LogLevel.Error },
+ { "OIDCH_0038:", LogLevel.Debug },
+ { "OIDCH_0040:", LogLevel.Debug },
+ { "OIDCH_0042:", LogLevel.Debug },
+ { "OIDCH_0043:", LogLevel.Verbose },
+ { "OIDCH_0044:", LogLevel.Verbose },
+ { "OIDCH_0045:", LogLevel.Debug }
};
BuildLogEntryList();
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectAuthenticationHandlerForTestingAuthenticate.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectAuthenticationHandlerForTestingAuthenticate.cs
index bf24d77914..02caabacdb 100644
--- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectAuthenticationHandlerForTestingAuthenticate.cs
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectAuthenticationHandlerForTestingAuthenticate.cs
@@ -2,9 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Authentication.OpenIdConnect;
+using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Http.Features.Authentication;
+using Microsoft.IdentityModel.Protocols;
+using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
{
@@ -14,7 +18,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
public class OpenIdConnectAuthenticationHandlerForTestingAuthenticate : OpenIdConnectAuthenticationHandler
{
public OpenIdConnectAuthenticationHandlerForTestingAuthenticate()
- : base()
+ : base(null)
{
}
@@ -32,6 +36,23 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
{
return Task.FromResult(0);
}
+ protected override async Task RedeemAuthorizationCodeAsync(string authorizationCode, string redirectUri)
+ {
+ var jsonResponse = new JObject();
+ jsonResponse.Add(OpenIdConnectParameterNames.IdToken, "test token");
+ return new OpenIdConnectTokenEndpointResponse(jsonResponse);
+ }
+
+ protected override async Task GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket)
+ {
+ var claimsIdentity = (ClaimsIdentity)ticket.Principal.Identity;
+ if (claimsIdentity == null)
+ {
+ claimsIdentity = new ClaimsIdentity();
+ }
+ claimsIdentity.AddClaim(new Claim("test claim", "test value"));
+ return new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), ticket.Properties, ticket.AuthenticationScheme);
+ }
//public override bool ShouldHandleScheme(string authenticationScheme)
//{
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs
index a5cf7074c5..2492a8fcdb 100644
--- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs
@@ -104,8 +104,9 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
options.StateDataFormat = new AuthenticationPropertiesFormaterKeyValue();
options.Notifications = new OpenIdConnectAuthenticationNotifications
{
- AuthorizationCodeReceived = notification =>
+ AuthorizationCodeRedeemed = notification =>
{
+ notification.HandleResponse();
if (notification.ProtocolMessage.State == null && !notification.ProtocolMessage.Parameters.ContainsKey(ExpectedStateParameter))
return Task.FromResult