From 34bc9c52e1d9bd7aa6fe84fe60aaf7d1f6eb099f Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 3 Feb 2016 16:12:53 -0800 Subject: [PATCH] #456 Unify OIDC Code/IdToken/Hybride flows. --- samples/OpenIdConnectSample/Startup.cs | 50 ++- .../OpenIdConnectHandler.cs | 425 ++++++++---------- 2 files changed, 232 insertions(+), 243 deletions(-) diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index f0f9feb3ae..ddcf9fa2b2 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; @@ -34,6 +35,28 @@ namespace OpenIdConnectSample { loggerfactory.AddConsole(LogLevel.Information); + // Simple error page + app.Use(async (context, next) => + { + try + { + await next(); + } + catch (Exception ex) + { + if (!context.Response.HasStarted) + { + context.Response.Clear(); + context.Response.StatusCode = 500; + await context.Response.WriteAsync(ex.ToString()); + } + else + { + throw; + } + } + }); + app.UseIISPlatformHandler(); app.UseCookieAuthentication(new CookieAuthenticationOptions @@ -52,17 +75,30 @@ namespace OpenIdConnectSample app.Run(async context => { - if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) + if (context.Request.Path.Equals("/signout")) { - await context.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" }); - - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync("Hello First timer"); + await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync($"Signing out {context.User.Identity.Name}
{Environment.NewLine}"); + await context.Response.WriteAsync("Sign In"); + await context.Response.WriteAsync($""); return; } - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync("Hello Authenticated User"); + if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) + { + await context.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" }); + return; + } + + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync($"Hello Authenticated User {context.User.Identity.Name}
{Environment.NewLine}"); + foreach (var claim in context.User.Claims) + { + await context.Response.WriteAsync($"{claim.Type}: {claim.Value}
{Environment.NewLine}"); + } + await context.Response.WriteAsync("Sign Out"); + await context.Response.WriteAsync($""); }); } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs index f7c675ff32..4084e21007 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs @@ -308,16 +308,16 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect { Logger.LogTrace(10, "Entering: {0}." + nameof(HandleRemoteAuthenticateAsync), GetType()); - OpenIdConnectMessage message = null; + OpenIdConnectMessage authorizationResponse = null; if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { - message = new OpenIdConnectMessage(Request.Query.Select(pair => new KeyValuePair(pair.Key, pair.Value))); + authorizationResponse = new OpenIdConnectMessage(Request.Query.Select(pair => new KeyValuePair(pair.Key, pair.Value))); // response_mode=query (explicit or not) and a response_type containing id_token // or token are not considered as a safe combination and MUST be rejected. // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security - if (!string.IsNullOrEmpty(message.IdToken) || !string.IsNullOrEmpty(message.AccessToken)) + if (!string.IsNullOrEmpty(authorizationResponse.IdToken) || !string.IsNullOrEmpty(authorizationResponse.AccessToken)) { if (Options.SkipUnrecognizedRequests) { @@ -336,10 +336,10 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect && Request.Body.CanRead) { var form = await Request.ReadFormAsync(); - message = new OpenIdConnectMessage(form.Select(pair => new KeyValuePair(pair.Key, pair.Value))); + authorizationResponse = new OpenIdConnectMessage(form.Select(pair => new KeyValuePair(pair.Key, pair.Value))); } - if (message == null) + if (authorizationResponse == null) { if (Options.SkipUnrecognizedRequests) { @@ -349,54 +349,52 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect return AuthenticateResult.Fail("No message."); } + AuthenticateResult result; + try { - var messageReceivedContext = await RunMessageReceivedEventAsync(message); - if (messageReceivedContext.HandledResponse) + var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse); + if (CheckEventResult(messageReceivedContext, out result)) { - return AuthenticateResult.Success(messageReceivedContext.Ticket); + return result; } - else if (messageReceivedContext.Skipped) - { - return AuthenticateResult.Skip(); - } - message = messageReceivedContext.ProtocolMessage; + authorizationResponse = messageReceivedContext.ProtocolMessage; // Fail if state is missing, it's required for the correlation id. - if (string.IsNullOrEmpty(message.State)) + if (string.IsNullOrEmpty(authorizationResponse.State)) { // This wasn't a valid OIDC message, it may not have been intended for us. + Logger.LogDebug(11, "message.State is null or empty."); if (Options.SkipUnrecognizedRequests) { return AuthenticateResult.Skip(); } - Logger.LogDebug(11, "message.State is null or empty."); return AuthenticateResult.Fail(Resources.MessageStateIsNullOrEmpty); } // if state exists and we failed to 'unprotect' this is not a message we should process. - var properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(message.State)); + var properties = Options.StateDataFormat.Unprotect(Uri.UnescapeDataString(authorizationResponse.State)); if (properties == null) { + Logger.LogDebug(12, "Unable to read the message.State."); if (Options.SkipUnrecognizedRequests) { // Not for us? return AuthenticateResult.Skip(); } - Logger.LogError(12, "Unable to read the message.State."); return AuthenticateResult.Fail(Resources.MessageStateIsInvalid); } // if any of the error fields are set, throw error null - if (!string.IsNullOrEmpty(message.Error)) + if (!string.IsNullOrEmpty(authorizationResponse.Error)) { - Logger.LogError(13, "Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.", message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null"); - return AuthenticateResult.Fail(new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.MessageContainsError, message.Error, message.ErrorDescription ?? "ErrorDecription null", message.ErrorUri ?? "ErrorUri null"))); + Logger.LogError(13, "Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'.", authorizationResponse.Error, authorizationResponse.ErrorDescription ?? "ErrorDecription null", authorizationResponse.ErrorUri ?? "ErrorUri null"); + return AuthenticateResult.Fail(new OpenIdConnectProtocolException(string.Format(CultureInfo.InvariantCulture, Resources.MessageContainsError, authorizationResponse.Error, authorizationResponse.ErrorDescription ?? "ErrorDecription null", authorizationResponse.ErrorUri ?? "ErrorUri null"))); } string userstate = null; properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out userstate); - message.State = userstate; + authorizationResponse.State = userstate; if (!ValidateCorrelationId(properties)) { @@ -409,38 +407,113 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } - Logger.LogTrace(15, "Authorization response received."); - var authorizationResponseReceivedContext = new AuthorizationResponseReceivedContext(Context, Options, properties) + var authorizationResponseReceivedContext = await RunAuthorizationResponseReceivedEventAsync(authorizationResponse, properties); + if (CheckEventResult(authorizationResponseReceivedContext, out result)) { - ProtocolMessage = message - }; - await Options.Events.AuthorizationResponseReceived(authorizationResponseReceivedContext); - if (authorizationResponseReceivedContext.HandledResponse) - { - Logger.LogDebug(16, "AuthorizationResponseReceived.HandledResponse"); - return AuthenticateResult.Success(authorizationResponseReceivedContext.Ticket); + return result; } - else if (authorizationResponseReceivedContext.Skipped) - { - Logger.LogDebug(17, "AuthorizationResponseReceived.Skipped"); - return AuthenticateResult.Skip(); - } - message = authorizationResponseReceivedContext.ProtocolMessage; + authorizationResponse = authorizationResponseReceivedContext.ProtocolMessage; properties = authorizationResponseReceivedContext.Properties; - if (string.IsNullOrEmpty(message.IdToken) && !string.IsNullOrEmpty(message.Code)) + PopulateSessionProperties(authorizationResponse, properties); + + AuthenticationTicket ticket = null; + JwtSecurityToken jwt = null; + string nonce = null; + var validationParameters = Options.TokenValidationParameters.Clone(); + + // Hybrid or Implicit flow + if (!string.IsNullOrEmpty(authorizationResponse.IdToken)) { - return await HandleCodeOnlyFlow(message, properties); + Logger.LogDebug(23, "'id_token' received."); + ticket = ValidateToken(authorizationResponse.IdToken, properties, validationParameters, out jwt); + + nonce = jwt?.Payload.Nonce; + if (!string.IsNullOrEmpty(nonce)) + { + nonce = ReadNonceCookie(nonce); + } } - else if (!string.IsNullOrEmpty(message.IdToken)) + + Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() { - return await HandleIdTokenFlows(message, properties); - } - else + ClientId = Options.ClientId, + ProtocolMessage = authorizationResponse, + ValidatedIdToken = jwt, + Nonce = nonce + }); + + // TODO: AuthorizationResponseValidated event? + + OpenIdConnectMessage tokenEndpointResponse = null; + + // Authorization Code or Hybrid flow + if (!string.IsNullOrEmpty(authorizationResponse.Code)) { - Logger.LogTrace(18, "Cannot process the message. Both id_token and code are missing."); - return AuthenticateResult.Fail(Resources.IdTokenCodeMissing); + // TODO: Does this event provide any value over AuthorizationResponseReceived or AuthorizationResponseValidated? + var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(authorizationResponse, properties, ticket, jwt); + if (CheckEventResult(authorizationCodeReceivedContext, out result)) + { + return result; + } + authorizationResponse = authorizationCodeReceivedContext.ProtocolMessage; + var code = authorizationCodeReceivedContext.Code; + + tokenEndpointResponse = await RedeemAuthorizationCodeAsync(code, authorizationCodeReceivedContext.RedirectUri); + + var authorizationCodeRedeemedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, properties); + if (CheckEventResult(authorizationCodeRedeemedContext, out result)) + { + return result; + } + authorizationResponse = authorizationCodeRedeemedContext.ProtocolMessage; + tokenEndpointResponse = authorizationCodeRedeemedContext.TokenEndpointResponse; + + // We only have to process the IdToken if we didn't already get one in the AuthorizationResponse + if (ticket == 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]. + validationParameters.RequireSignedTokens = false; + + ticket = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out jwt); + + nonce = jwt?.Payload.Nonce; + if (!string.IsNullOrEmpty(nonce)) + { + nonce = ReadNonceCookie(nonce); + } + } + + Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext() + { + ClientId = Options.ClientId, + ProtocolMessage = tokenEndpointResponse, + ValidatedIdToken = jwt, + Nonce = nonce + }); } + + var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(authorizationResponse, ticket, properties, tokenEndpointResponse); + if (CheckEventResult(authenticationValidatedContext, out result)) + { + return result; + } + authorizationResponse = authenticationValidatedContext.ProtocolMessage; + tokenEndpointResponse = authenticationValidatedContext.TokenEndpointResponse; + ticket = authenticationValidatedContext.Ticket; + + if (Options.SaveTokensAsClaims) + { + SaveTokens(ticket.Principal, tokenEndpointResponse ?? authorizationResponse, jwt.Issuer); + } + + if (Options.GetClaimsFromUserInfoEndpoint) + { + return await GetUserInformationAsync(tokenEndpointResponse ?? authorizationResponse, jwt, ticket); + } + + return AuthenticateResult.Success(ticket); } catch (Exception exception) { @@ -456,179 +529,43 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect } } - var authenticationFailedContext = await RunAuthenticationFailedEventAsync(message, exception); - if (authenticationFailedContext.HandledResponse) + var authenticationFailedContext = await RunAuthenticationFailedEventAsync(authorizationResponse, exception); + if (CheckEventResult(authenticationFailedContext, out result)) { - return AuthenticateResult.Success(authenticationFailedContext.Ticket); - } - else if (authenticationFailedContext.Skipped) - { - return AuthenticateResult.Skip(); + return result; } - throw; + return AuthenticateResult.Fail(exception); } } - // Authorization Code Flow - private async Task HandleCodeOnlyFlow(OpenIdConnectMessage message, AuthenticationProperties properties) + private bool CheckEventResult(BaseControlContext context, out AuthenticateResult result) { - AuthenticationTicket ticket = null; - JwtSecurityToken jwt = null; - - Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() + if (context.HandledResponse) { - ClientId = Options.ClientId, - ProtocolMessage = message, - }); - - var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(message, properties, ticket, jwt); - if (authorizationCodeReceivedContext.HandledResponse) - { - return AuthenticateResult.Success(authorizationCodeReceivedContext.Ticket); + result = AuthenticateResult.Success(context.Ticket); + return true; } - else if (authorizationCodeReceivedContext.Skipped) + else if (context.Skipped) { - return AuthenticateResult.Skip(); + result = AuthenticateResult.Skip(); + return true; } - message = authorizationCodeReceivedContext.ProtocolMessage; - var code = authorizationCodeReceivedContext.Code; - - // Redeeming authorization code for tokens - Logger.LogTrace(21, "Id Token is null. Redeeming code '{0}' for tokens.", code); - - var tokenEndpointResponse = await RedeemAuthorizationCodeAsync(code, authorizationCodeReceivedContext.RedirectUri); - - var authorizationCodeRedeemedContext = await RunTokenResponseReceivedEventAsync(message, tokenEndpointResponse, properties); - if (authorizationCodeRedeemedContext.HandledResponse) - { - return AuthenticateResult.Success(authorizationCodeRedeemedContext.Ticket); - } - else if (authorizationCodeRedeemedContext.Skipped) - { - return AuthenticateResult.Skip(); - } - - message = authorizationCodeRedeemedContext.ProtocolMessage; - tokenEndpointResponse = authorizationCodeRedeemedContext.TokenEndpointResponse; - - // 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.RequireSignedTokens = false; - - ticket = ValidateToken(tokenEndpointResponse.IdToken, message, properties, validationParameters, out jwt); - - var nonce = jwt?.Payload.Nonce; - if (!string.IsNullOrEmpty(nonce)) - { - nonce = ReadNonceCookie(nonce); - } - - Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext() - { - ClientId = Options.ClientId, - ProtocolMessage = tokenEndpointResponse, - ValidatedIdToken = jwt, - Nonce = nonce - }); - - var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(message, ticket, properties, tokenEndpointResponse); - if (authenticationValidatedContext.HandledResponse) - { - return AuthenticateResult.Success(authenticationValidatedContext.Ticket); - } - else if (authenticationValidatedContext.Skipped) - { - return AuthenticateResult.Skip(); - } - ticket = authenticationValidatedContext.Ticket; - - if (Options.SaveTokensAsClaims) - { - // Persist the tokens extracted from the token response. - SaveTokens(ticket.Principal, tokenEndpointResponse, jwt.Issuer, saveRefreshToken: true); - } - - if (Options.GetClaimsFromUserInfoEndpoint) - { - Logger.LogTrace(22, "Sending request to user info endpoint for retrieving claims."); - ticket = await GetUserInformationAsync(tokenEndpointResponse, jwt, ticket); - } - - return AuthenticateResult.Success(ticket); + result = null; + return false; } - // Implicit Flow or Hybrid Flow - private async Task HandleIdTokenFlows(OpenIdConnectMessage message, AuthenticationProperties properties) + private void PopulateSessionProperties(OpenIdConnectMessage message, AuthenticationProperties properties) { - Logger.LogTrace(23, "'id_token' received: '{0}'", message.IdToken); - - JwtSecurityToken jwt = null; - var validationParameters = Options.TokenValidationParameters.Clone(); - var ticket = ValidateToken(message.IdToken, message, properties, validationParameters, out jwt); - - var nonce = jwt?.Payload.Nonce; - if (!string.IsNullOrEmpty(nonce)) + if (!string.IsNullOrEmpty(message.SessionState)) { - nonce = ReadNonceCookie(nonce); + properties.Items[OpenIdConnectSessionProperties.SessionState] = message.SessionState; } - Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() + if (!string.IsNullOrEmpty(_configuration.CheckSessionIframe)) { - ClientId = Options.ClientId, - ProtocolMessage = message, - ValidatedIdToken = jwt, - Nonce = nonce - }); - - var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(message, ticket, properties, tokenEndpointResponse: null); - if (authenticationValidatedContext.HandledResponse) - { - return AuthenticateResult.Success(authenticationValidatedContext.Ticket); + properties.Items[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe; } - else if (authenticationValidatedContext.Skipped) - { - return AuthenticateResult.Skip(); - } - message = authenticationValidatedContext.ProtocolMessage; - ticket = authenticationValidatedContext.Ticket; - - // Hybrid Flow - if (message.Code != null) - { - var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(message, properties, ticket, jwt); - if (authorizationCodeReceivedContext.HandledResponse) - { - return AuthenticateResult.Success(authorizationCodeReceivedContext.Ticket); - } - else if (authorizationCodeReceivedContext.Skipped) - { - return AuthenticateResult.Skip(); - } - message = authorizationCodeReceivedContext.ProtocolMessage; - ticket = authorizationCodeReceivedContext.Ticket; - - if (Options.SaveTokensAsClaims) - { - // TODO: call SaveTokens with the token response and set - // saveRefreshToken to true when the hybrid flow is fully implemented. - SaveTokens(ticket.Principal, message, jwt.Issuer, saveRefreshToken: false); - } - } - // Implicit Flow - else - { - if (Options.SaveTokensAsClaims) - { - // Note: don't save the refresh token when it is extracted from the authorization - // response, since it's not a valid parameter when using the implicit flow. - // See http://openid.net/specs/openid-connect-core-1_0.html#Authentication - // and https://tools.ietf.org/html/rfc6749#section-4.2.2. - SaveTokens(ticket.Principal, message, jwt.Issuer, saveRefreshToken: false); - } - } - - return AuthenticateResult.Success(ticket); } /// @@ -639,6 +576,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// OpenIdConnect message that has tokens inside it. protected virtual async Task RedeemAuthorizationCodeAsync(string authorizationCode, string redirectUri) { + Logger.LogDebug(21, "Redeeming code for tokens."); + var openIdMessage = new OpenIdConnectMessage() { ClientId = Options.ClientId, @@ -648,6 +587,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect RedirectUri = redirectUri }; + // TODO: Event that lets you customize the message. E.g. use certificates, specify resources. + var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint); requestMessage.Content = new FormUrlEncodedContent(openIdMessage.Parameters); var responseMessage = await Backchannel.SendAsync(requestMessage); @@ -663,21 +604,28 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// 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(OpenIdConnectMessage message, JwtSecurityToken jwt, AuthenticationTicket ticket) + protected virtual async Task GetUserInformationAsync(OpenIdConnectMessage message, JwtSecurityToken jwt, AuthenticationTicket ticket) { var userInfoEndpoint = _configuration?.UserInfoEndpoint; if (string.IsNullOrEmpty(userInfoEndpoint)) { - Logger.LogWarning(24, nameof(_configuration.UserInfoEndpoint) + " is not set. Request to retrieve claims cannot be completed."); - return ticket; + Logger.LogDebug(24, $"{nameof(_configuration.UserInfoEndpoint)} is not set. Claims cannot be retrieved."); + return AuthenticateResult.Success(ticket); } + if (string.IsNullOrEmpty(message.AccessToken)) + { + Logger.LogDebug(47, "The access_token is not available. Claims cannot be retrieved."); + return AuthenticateResult.Success(ticket); + } + Logger.LogTrace(22, "Retrieving claims from the user info endpoint."); 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(); + JObject user; var contentType = responseMessage.Content.Headers.ContentType; if (contentType.MediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase)) @@ -691,17 +639,14 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect } else { - throw new NotSupportedException("Unknown response type: " + contentType.MediaType); + return AuthenticateResult.Fail("Unknown response type: " + contentType.MediaType); } var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(ticket, message, user); - if (userInformationReceivedContext.HandledResponse) + AuthenticateResult result; + if (CheckEventResult(userInformationReceivedContext, out result)) { - return userInformationReceivedContext.Ticket; - } - else if (userInformationReceivedContext.Skipped) - { - return ticket; + return result; } ticket = userInformationReceivedContext.Ticket; user = userInformationReceivedContext.User; @@ -742,7 +687,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, jwt.Issuer)); } - return ticket; + return AuthenticateResult.Success(ticket); } /// @@ -750,8 +695,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// /// The principal in which tokens are saved. /// The OpenID Connect response. - /// A indicating whether the refresh token should be stored. - private void SaveTokens(ClaimsPrincipal principal, OpenIdConnectMessage message, string issuer, bool saveRefreshToken) + private void SaveTokens(ClaimsPrincipal principal, OpenIdConnectMessage message, string issuer) { var identity = (ClaimsIdentity)principal.Identity; @@ -767,7 +711,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect ClaimValueTypes.String, issuer)); } - if (saveRefreshToken && !string.IsNullOrEmpty(message.RefreshToken)) + if (!string.IsNullOrEmpty(message.RefreshToken)) { identity.AddClaim(new Claim(OpenIdConnectParameterNames.RefreshToken, message.RefreshToken, ClaimValueTypes.String, issuer)); @@ -911,6 +855,25 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect return messageReceivedContext; } + private async Task RunAuthorizationResponseReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties) + { + Logger.LogTrace(15, "Authorization response received."); + var authorizationResponseReceivedContext = new AuthorizationResponseReceivedContext(Context, Options, properties) + { + ProtocolMessage = message + }; + await Options.Events.AuthorizationResponseReceived(authorizationResponseReceivedContext); + if (authorizationResponseReceivedContext.HandledResponse) + { + Logger.LogDebug(16, "AuthorizationResponseReceived.HandledResponse"); + } + else if (authorizationResponseReceivedContext.Skipped) + { + Logger.LogDebug(17, "AuthorizationResponseReceived.Skipped"); + } + return authorizationResponseReceivedContext; + } + private async Task RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt) { var redirectUri = properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]; @@ -960,13 +923,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect return tokenResponseReceivedContext; } - private async Task RunAuthenticationValidatedEventAsync(OpenIdConnectMessage message, AuthenticationTicket ticket, AuthenticationProperties properties, OpenIdConnectMessage tokenEndpointResponse) + private async Task RunAuthenticationValidatedEventAsync(OpenIdConnectMessage message, AuthenticationTicket ticket, AuthenticationProperties properties, OpenIdConnectMessage tokenResponse) { var authenticationValidatedContext = new AuthenticationValidatedContext(Context, Options, properties) { Ticket = ticket, ProtocolMessage = message, - TokenEndpointResponse = tokenEndpointResponse, + TokenEndpointResponse = tokenResponse, }; await Options.Events.AuthenticationValidated(authenticationValidatedContext); @@ -1027,10 +990,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect return authenticationFailedContext; } - private AuthenticationTicket ValidateToken(string idToken, OpenIdConnectMessage message, AuthenticationProperties properties, TokenValidationParameters validationParameters, out JwtSecurityToken jwt) + private AuthenticationTicket ValidateToken(string idToken, AuthenticationProperties properties, TokenValidationParameters validationParameters, out JwtSecurityToken jwt) { - AuthenticationTicket ticket = null; - jwt = null; + if (!Options.SecurityTokenValidator.CanReadToken(idToken)) + { + Logger.LogError(48, "Unable to read the 'id_token', no suitable ISecurityTokenValidator was found for: '{0}'.", idToken); + throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.UnableToValidateToken, idToken)); + } if (_configuration != null) { @@ -1047,16 +1013,12 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect } SecurityToken validatedToken = null; - ClaimsPrincipal principal = null; - if (Options.SecurityTokenValidator.CanReadToken(idToken)) + var principal = Options.SecurityTokenValidator.ValidateToken(idToken, validationParameters, out validatedToken); + jwt = validatedToken as JwtSecurityToken; + if (jwt == null) { - principal = Options.SecurityTokenValidator.ValidateToken(idToken, validationParameters, out validatedToken); - jwt = validatedToken as JwtSecurityToken; - if (jwt == null) - { - Logger.LogError(45, "The Validated Security Token must be of type JwtSecurityToken, but instead its type is: '{0}'", validatedToken?.GetType()); - throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.ValidatedSecurityTokenNotJwt, validatedToken?.GetType())); - } + Logger.LogError(45, "The Validated Security Token must be of type JwtSecurityToken, but instead its type is: '{0}'", validatedToken?.GetType()); + throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.ValidatedSecurityTokenNotJwt, validatedToken?.GetType())); } if (validatedToken == null) @@ -1065,16 +1027,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect throw new SecurityTokenException(string.Format(CultureInfo.InvariantCulture, Resources.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; - } + var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme); if (Options.UseTokenLifetime) {