private static Task SecurityTokenReceived(SecurityTokenReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions> notification) { List <Claim> claims = new List <Claim> { new Claim(ClaimTypes.Email, "*****@*****.**"), new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"), }; notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsIdentity(claims, notification.Options.AuthenticationType), new Http.Security.AuthenticationProperties()); notification.HandleResponse(); return(Task.FromResult <object>(null)); }
private async Task <SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> > RunSecurityTokenReceivedNotificationAsync(OpenIdConnectMessage message) { Logger.LogDebug(Resources.OIDCH_0020_IdTokenReceived, message.IdToken); var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(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 Task SecurityTokenReceivedNotification(SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification) { //notification.Response.Redirect($"/Home/Index?message={notification.ProtocolMessage.IdToken}"); // Buffer the response //var stream = arg.Response.Body; //var buffer = new MemoryStream(); //buffer.Seek(0, SeekOrigin.Begin); //var reader = new StreamReader(buffer); //string responseBody = await reader.ReadToEndAsync(); //// Now, you can access response body. //var result = responseBody; //// You need to do this so that the response we buffered //// is flushed out to the client application. //buffer.Seek(0, SeekOrigin.Begin); //await buffer.CopyToAsync(stream); return(Task.FromResult(0)); }
internal static async Task SecurityTokenReceived(SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context) { notificationsFired.Add(nameof(SecurityTokenReceived)); await Task.FromResult(0); }
/// <summary> /// Invoked to process incoming authentication messages. /// </summary> /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. Need to make this runtime configurable. if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) { return(null); } OpenIdConnectMessage openIdConnectMessage = null; if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase) && Request.Query.Any(q => q.Key == "code")) { _logger.WriteVerbose("Processing request query string"); openIdConnectMessage = new OpenIdConnectMessage(Request.Query); // 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(openIdConnectMessage.IdToken) || !string.IsNullOrEmpty(openIdConnectMessage.AccessToken)) { _logger.WriteWarning("An OpenID Connect response cannot contain an " + "identity token or an access token when using response_mode=query"); return(null); } } // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { _logger.WriteVerbose("Buffering request body"); // Buffer in case this body was not meant for us. MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); // TODO: a delegate on OpenIdConnectAuthenticationOptions would allow for users to hook their own custom message. openIdConnectMessage = new OpenIdConnectMessage(form); } if (openIdConnectMessage == null) { return(null); } ExceptionDispatchInfo authFailedEx = null; try { // Run MessageReceived notification { var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { 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. AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State); if (properties == null) { _logger.WriteWarning("The state field is missing or invalid."); return(null); } // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error)) { throw new OpenIdConnectProtocolException( string.Format(CultureInfo.InvariantCulture, Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.Error, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty)); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); } JwtSecurityToken jwt = null; ClaimsPrincipal principal = null; AuthenticationTicket ticket = null; string nonce = null; OpenIdConnectMessage tokenEndpointResponse = null; JwtSecurityToken tokenEndpointJwt = null; // Copy and augment to avoid cross request race conditions for updated configurations. TokenValidationParameters tvp = Options.TokenValidationParameters.Clone(); // Hybrid or Implicit flow if (!string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken)) { // Run SecurityTokenReceived notification { var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } } principal = ValidateToken(openIdConnectMessage.IdToken, properties, tvp, out jwt); ticket = new AuthenticationTicket(principal.Identity as ClaimsIdentity, properties); if (Options.ProtocolValidator.RequireNonce) { if (String.IsNullOrWhiteSpace(openIdConnectMessage.Nonce)) { openIdConnectMessage.Nonce = jwt.Payload.Nonce; } // deletes the nonce cookie nonce = RetrieveNonce(openIdConnectMessage); } // remember 'session_state' and 'check_session_iframe' if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState)) { properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState; } if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe)) { properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe; } // Run SecurityTokenValidated notification { var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; } } Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() { ClientId = Options.ClientId, ProtocolMessage = openIdConnectMessage, ValidatedIdToken = jwt, Nonce = nonce }); // Authorization Code or Hybrid flow if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Code)) { var tokenEndpointRequest = new OpenIdConnectMessage() { ClientId = Options.ClientId, ClientSecret = Options.ClientSecret, Code = openIdConnectMessage.Code, GrantType = OpenIdConnectGrantTypes.AuthorizationCode, RedirectUri = properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty }; // Run AuthorizationCodeReceived notification { var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { TokenEndpointRequest = tokenEndpointRequest, Code = openIdConnectMessage.Code, JwtSecurityToken = jwt, ProtocolMessage = openIdConnectMessage, RedirectUri = tokenEndpointRequest.RedirectUri, }; await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); if (authorizationCodeReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authorizationCodeReceivedNotification.Skipped) { return(null); } tokenEndpointRequest = authorizationCodeReceivedNotification.TokenEndpointRequest; openIdConnectMessage = authorizationCodeReceivedNotification.ProtocolMessage; } // Authorization Code flow - no Id Token was received if (ticket == null) { // Redeem token using the received authorization code tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest); // Run SecurityTokenReceived notification { var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = tokenEndpointResponse, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } } // Validate token // 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]. tvp.RequireSignedTokens = false; // At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response. // And we'll want to validate the new JWT in ValidateTokenResponse. principal = ValidateToken(tokenEndpointResponse.IdToken, properties, tvp, out tokenEndpointJwt); ticket = new AuthenticationTicket(principal.Identity as ClaimsIdentity, properties); nonce = tokenEndpointJwt.Payload.Nonce; if (String.IsNullOrWhiteSpace(nonce)) { nonce = ReadNonceCookie(nonce); } // Run SecurityTokenValidated notification { var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = tokenEndpointResponse, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; } } } if (Options.GetClaimsFromUserInfoEndpoint) { await GetUserInformationAsync( tokenEndpointResponse ?? openIdConnectMessage, tokenEndpointJwt ?? jwt, principal, properties); } return(ticket); } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException); // 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 && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
/// <summary> /// Invoked to process incoming authentication messages. /// </summary> /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. Need to make this runtime configurable. if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) { return(null); } OpenIdConnectMessage authorizationResponse = null; if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase) && Request.Query.Any()) { authorizationResponse = new OpenIdConnectMessage(Request.Query.Select(pair => new KeyValuePair <string, string[]>(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(authorizationResponse.IdToken) || !string.IsNullOrEmpty(authorizationResponse.AccessToken)) { var invalidResponseEx = new OpenIdConnectProtocolException("An OpenID Connect response cannot contain an identity token or an access token when using response_mode=query"); _logger.WriteError("Exception occurred while processing message: ", invalidResponseEx); var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = authorizationResponse, Exception = invalidResponseEx }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } throw invalidResponseEx; } } // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { _logger.WriteVerbose("Buffering request body"); // Buffer in case this body was not meant for us. MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); // TODO: a delegate on OpenIdConnectAuthenticationOptions would allow for users to hook their own custom message. authorizationResponse = new OpenIdConnectMessage(form); } if (authorizationResponse == null) { return(null); } ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = authorizationResponse }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { 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. AuthenticationProperties properties = GetPropertiesFromState(authorizationResponse.State); if (properties == null) { _logger.WriteWarning("The state field is missing or invalid."); return(null); } // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(authorizationResponse.Error)) { throw CreateOpenIdConnectProtocolException(authorizationResponse); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); } PopulateSessionProperties(authorizationResponse, properties); ClaimsPrincipal user = null; AuthenticationTicket ticket = null; JwtSecurityToken jwt = null; string nonce = null; // Copy and augment to avoid cross request race conditions for updated configurations. var validationParameters = Options.TokenValidationParameters.Clone(); // Hybrid or Implicit flow if (!string.IsNullOrEmpty(authorizationResponse.IdToken)) { var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = authorizationResponse, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } user = ValidateToken(authorizationResponse.IdToken, properties, validationParameters, out jwt); if (Options.ProtocolValidator.RequireNonce) { if (string.IsNullOrWhiteSpace(authorizationResponse.Nonce)) { authorizationResponse.Nonce = jwt.Payload.Nonce; } // deletes the nonce cookie nonce = RetrieveNonce(authorizationResponse); } ClaimsIdentity claimsIdentity = user.Identity as ClaimsIdentity; ticket = new AuthenticationTicket(claimsIdentity, properties); var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = authorizationResponse, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; } Options.ProtocolValidator.ValidateAuthenticationResponse(new OpenIdConnectProtocolValidationContext() { ClientId = Options.ClientId, ProtocolMessage = authorizationResponse, ValidatedIdToken = jwt, Nonce = nonce }); OpenIdConnectMessage tokenEndpointResponse = null; // Authorization Code or Hybrid flow if (!string.IsNullOrEmpty(authorizationResponse.Code)) { var tokenEndpointRequest = new OpenIdConnectMessage() { ClientId = Options.ClientId, ClientSecret = Options.ClientSecret, Code = authorizationResponse.Code, GrantType = OpenIdConnectGrantTypes.AuthorizationCode, RedirectUri = properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, }; var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { AuthenticationTicket = ticket, Code = authorizationResponse.Code, JwtSecurityToken = jwt, ProtocolMessage = authorizationResponse, RedirectUri = tokenEndpointRequest.RedirectUri, TokenEndpointRequest = tokenEndpointRequest }; // PKCE https://tools.ietf.org/html/rfc7636#section-4.5 string codeVerifier; if (properties.Dictionary.TryGetValue(OAuthConstants.CodeVerifierKey, out codeVerifier)) { tokenEndpointRequest.Parameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier); properties.Dictionary.Remove(OAuthConstants.CodeVerifierKey); } await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); if (authorizationCodeReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authorizationCodeReceivedNotification.Skipped) { return(null); } // Flow possible changes authorizationResponse = authorizationCodeReceivedNotification.ProtocolMessage; ticket = authorizationCodeReceivedNotification.AuthenticationTicket; tokenEndpointRequest = authorizationCodeReceivedNotification.TokenEndpointRequest; tokenEndpointResponse = authorizationCodeReceivedNotification.TokenEndpointResponse; jwt = authorizationCodeReceivedNotification.JwtSecurityToken; if (!authorizationCodeReceivedNotification.HandledCodeRedemption && Options.RedeemCode) { tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest); } if (tokenEndpointResponse != null) { var tokenResponseReceivedNotification = new TokenResponseReceivedNotification(Context, Options) { ProtocolMessage = authorizationResponse, TokenEndpointResponse = tokenEndpointResponse }; await Options.Notifications.TokenResponseReceived(tokenResponseReceivedNotification); if (tokenResponseReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (tokenResponseReceivedNotification.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]. validationParameters.RequireSignedTokens = false; // At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response. // And we'll want to validate the new JWT in ValidateTokenResponse. JwtSecurityToken tokenEndpointJwt = null; var tokenEndpointUser = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out tokenEndpointJwt); // Avoid running the event, etc, if it was already done as part of the authorization response validation. if (user == null) { if (Options.ProtocolValidator.RequireNonce) { if (string.IsNullOrWhiteSpace(tokenEndpointResponse.Nonce)) { tokenEndpointResponse.Nonce = tokenEndpointJwt.Payload.Nonce; } // deletes the nonce cookie if (nonce == null) { nonce = RetrieveNonce(tokenEndpointResponse); } } ClaimsIdentity claimsIdentity = tokenEndpointUser.Identity as ClaimsIdentity; ticket = new AuthenticationTicket(claimsIdentity, properties); var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = tokenEndpointResponse, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; } else { if (!string.Equals(jwt.Subject, tokenEndpointJwt.Subject, StringComparison.Ordinal)) { throw new SecurityTokenException("The sub claim does not match in the id_token's from the authorization and token endpoints."); } } jwt = tokenEndpointJwt; } // Validate the token response if it wasn't provided manually if (!authorizationCodeReceivedNotification.HandledCodeRedemption && Options.RedeemCode) { Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext() { ClientId = Options.ClientId, ProtocolMessage = tokenEndpointResponse, ValidatedIdToken = jwt, Nonce = nonce }); } } if (Options.SaveTokens && ticket != null) { SaveTokens(ticket.Properties, tokenEndpointResponse ?? authorizationResponse); } return(ticket); } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException); // 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 && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = authorizationResponse, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
/// <summary> /// Invoked to process incoming authentication messages. /// </summary> /// <returns></returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) { return(null); } WsFederationMessage wsFederationMessage = null; // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { _logger.WriteVerbose("Buffering request body"); // Buffer in case this body was not meant for us. MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); // TODO: a delegate on WsFederationAuthenticationOptions would allow for users to hook their own custom message. wsFederationMessage = new WsFederationMessage(form); } if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage) { return(null); } ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { return(null); } if (wsFederationMessage.Wresult == null) { _logger.WriteWarning("Received a sign-in message without a WResult."); return(null); } string token = wsFederationMessage.GetToken(); if (string.IsNullOrWhiteSpace(token)) { _logger.WriteWarning("Received a sign-in message without a token."); return(null); } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); } // Copy and augment to avoid cross request race conditions for updated configurations. TokenValidationParameters tvp = Options.TokenValidationParameters.Clone(); IEnumerable <string> issuers = new[] { _configuration.Issuer }; tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers)); tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys)); SecurityToken parsedToken; ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(token, tvp, out parsedToken); ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; // Retrieve our cached redirect uri string state = wsFederationMessage.Wctx; // WsFed allows for uninitiated logins, state may be missing. AuthenticationProperties properties = GetPropertiesFromWctx(state); AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); if (Options.UseTokenLifetime) { // Override any session persistence to match the token lifetime. DateTime issued = parsedToken.ValidFrom; if (issued != DateTime.MinValue) { ticket.Properties.IssuedUtc = issued.ToUniversalTime(); } DateTime expires = parsedToken.ValidTo; if (expires != DateTime.MinValue) { ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); } ticket.Properties.AllowRefresh = false; } var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = wsFederationMessage, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; return(ticket); } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException); // 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 && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = new AuthenticationFailedNotification <WsFederationMessage, WsFederationAuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
internal static async Task SecurityTokenReceived(SecurityTokenReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context) { notificationsFired.Add(nameof(SecurityTokenReceived)); await Task.FromResult(0); }
/// <summary> /// Invoked to process incoming OpenIdConnect messages. /// </summary> /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. Need to make this runtime configurable. if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) { return(null); } OpenIdConnectMessage openIdConnectMessage = null; // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); openIdConnectMessage = new OpenIdConnectMessage(form); } if (openIdConnectMessage == null) { return(null); } try { var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(messageReceivedNotification.AuthenticationTicket); } if (messageReceivedNotification.Skipped) { 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. AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State); if (properties == null) { _logger.LogWarning("The state field is missing or invalid."); return(null); } // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error)) { throw new OpenIdConnectProtocolException( string.Format(CultureInfo.InvariantCulture, openIdConnectMessage.Error, Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty)); } // code is only accepted with id_token, in this version, hence check for code is inside this if // OpenIdConnect protocol allows a Code to be received without the id_token if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken)) { _logger.LogWarning("The id_token is missing."); return(null); } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(securityTokenReceivedNotification.AuthenticationTicket); } if (securityTokenReceivedNotification.Skipped) { return(null); } if (_configuration == null && Options.ConfigurationManager != null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } // Copy and augment to avoid cross request race conditions for updated configurations. TokenValidationParameters validationParameters = Options.TokenValidationParameters.Clone(); if (_configuration != null) { if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer)) { validationParameters.ValidIssuer = _configuration.Issuer; } else if (!string.IsNullOrWhiteSpace(_configuration.Issuer)) { validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? new[] { _configuration.Issuer } : validationParameters.ValidIssuers.Concat(new[] { _configuration.Issuer })); } validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys)); } AuthenticationTicket ticket; SecurityToken validatedToken = null; ClaimsPrincipal principal = null; JwtSecurityToken jwt = null; foreach (var validator in Options.SecurityTokenValidators) { if (validator.CanReadToken(openIdConnectMessage.IdToken)) { principal = validator.ValidateToken(openIdConnectMessage.IdToken, validationParameters, out validatedToken); jwt = validatedToken as JwtSecurityToken; if (jwt == null) { throw new InvalidOperationException("Validated Security Token must be a JwtSecurityToken was: " + (validatedToken == null ? "null" : validatedToken.GetType().ToString())); } } } if (validatedToken == null) { throw new InvalidOperationException("No SecurityTokenValidator found for token: " + openIdConnectMessage.IdToken); } ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme); if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState; } if (_configuration != null && !string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe; } if (Options.UseTokenLifetime) { // Override any session persistence to match the token lifetime. DateTime issued = validatedToken.ValidFrom; if (issued != DateTime.MinValue) { ticket.Properties.IssuedUtc = issued; } DateTime expires = validatedToken.ValidTo; if (expires != DateTime.MinValue) { ticket.Properties.ExpiresUtc = expires; } ticket.Properties.AllowRefresh = false; } var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = openIdConnectMessage }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(securityTokenValidatedNotification.AuthenticationTicket); } if (securityTokenValidatedNotification.Skipped) { return(null); } var protocolValidationContext = new OpenIdConnectProtocolValidationContext { AuthorizationCode = openIdConnectMessage.Code, Nonce = RetrieveNonce(jwt.Payload.Nonce), }; Options.ProtocolValidator.Validate(jwt, protocolValidationContext); if (openIdConnectMessage.Code != null) { var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { AuthenticationTicket = ticket, Code = openIdConnectMessage.Code, JwtSecurityToken = jwt, ProtocolMessage = openIdConnectMessage, RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, }; await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); if (authorizationCodeReceivedNotification.HandledResponse) { return(authorizationCodeReceivedNotification.AuthenticationTicket); } if (authorizationCodeReceivedNotification.Skipped) { return(null); } } return(ticket); } catch (Exception exception) { _logger.LogError("Exception occurred while processing message", exception); // 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(); } var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, Exception = exception }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(authenticationFailedNotification.AuthenticationTicket); } if (authenticationFailedNotification.Skipped) { return(null); } throw; } }
private Task OnSecurityTokenReceived(SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> aArg) { return(Task.FromResult(0)); }
/// <summary> /// Invoked to process incoming authentication messages. /// </summary> /// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. Need to make this runtime configurable. if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) { return(null); } OpenIdConnectMessage openIdConnectMessage = null; // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { _logger.WriteVerbose("Buffering request body"); // Buffer in case this body was not meant for us. MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); // TODO: a delegate on OpenIdConnectAuthenticationOptions would allow for users to hook their own custom message. openIdConnectMessage = new OpenIdConnectMessage(form); } if (openIdConnectMessage == null) { return(null); } ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { 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. AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State); if (properties == null) { _logger.WriteWarning("The state field is missing or invalid."); return(null); } // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error)) { throw new OpenIdConnectProtocolException( string.Format(CultureInfo.InvariantCulture, openIdConnectMessage.Error, Resources.Exception_OpenIdConnectMessageError, openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty)); } // code is only accepted with id_token, in this version, hence check for code is inside this if // OpenIdConnect protocol allows a Code to be received without the id_token if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken)) { _logger.WriteWarning("The id_token is missing."); return(null); } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); } // Copy and augment to avoid cross request race conditions for updated configurations. TokenValidationParameters tvp = Options.TokenValidationParameters.Clone(); IEnumerable <string> issuers = new[] { _configuration.Issuer }; tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers)); tvp.IssuerSigningTokens = (tvp.IssuerSigningTokens == null ? _configuration.SigningTokens : tvp.IssuerSigningTokens.Concat(_configuration.SigningTokens)); SecurityToken validatedToken; ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken); ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; // claims principal could have changed claim values, use bits received on wire for validation. JwtSecurityToken jwt = validatedToken as JwtSecurityToken; AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); string nonce = null; if (Options.ProtocolValidator.RequireNonce) { if (String.IsNullOrWhiteSpace(openIdConnectMessage.Nonce)) { openIdConnectMessage.Nonce = jwt.Payload.Nonce; } // deletes the nonce cookie nonce = RetrieveNonce(openIdConnectMessage); } // remember 'session_state' and 'check_session_iframe' if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState; } if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe; } if (Options.UseTokenLifetime) { // Override any session persistence to match the token lifetime. DateTime issued = jwt.ValidFrom; if (issued != DateTime.MinValue) { ticket.Properties.IssuedUtc = issued.ToUniversalTime(); } DateTime expires = jwt.ValidTo; if (expires != DateTime.MinValue) { ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); } ticket.Properties.AllowRefresh = false; } var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; var protocolValidationContext = new OpenIdConnectProtocolValidationContext { AuthorizationCode = openIdConnectMessage.Code, Nonce = nonce, }; Options.ProtocolValidator.Validate(jwt, protocolValidationContext); if (openIdConnectMessage.Code != null) { var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { AuthenticationTicket = ticket, Code = openIdConnectMessage.Code, JwtSecurityToken = jwt, ProtocolMessage = openIdConnectMessage, RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, }; await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); if (authorizationCodeReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authorizationCodeReceivedNotification.Skipped) { return(null); } // Flow possible changes ticket = authorizationCodeReceivedNotification.AuthenticationTicket; } return(ticket); } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: '" + authFailedEx.ToString()); // 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 && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) { return(null); } OpenIdConnectMessage openIdConnectMessage = null; if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { _logger.WriteVerbose("Buffering request body"); MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); openIdConnectMessage = new OpenIdConnectMessage(form); } if (openIdConnectMessage == null) { return(null); } ExceptionDispatchInfo authFailedEx = null; string policy = string.Empty; try { var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { return(null); } AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State); if (properties == null) { _logger.WriteWarning("The state field is missing or invalid."); return(null); } string nonce = null; if (Options.ProtocolValidator.RequireNonce) { nonce = RetrieveNonce(openIdConnectMessage); } if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error)) { throw new OpenIdConnectProtocolException( string.Format(CultureInfo.InvariantCulture, openIdConnectMessage.Error, "", openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty)); } if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken)) { _logger.WriteWarning("The id_token is missing."); return(null); } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } // Enable Per-Policy Metadata Retreival if (properties.Dictionary.TryGetValue(PolicyParameter, out policy)) { B2CConfigurationManager mgr = Options.ConfigurationManager as B2CConfigurationManager; _configuration = await mgr.GetConfigurationAsync(Context.Request.CallCancelled, policy); } else { _logger.WriteWarning("No policy identifier was found in the Authentication Properties of the request."); return(null); } TokenValidationParameters tvp = Options.TokenValidationParameters.Clone(); IEnumerable <string> issuers = new[] { _configuration.Issuer }; tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers)); tvp.IssuerSigningTokens = (tvp.IssuerSigningTokens == null ? _configuration.SigningTokens : tvp.IssuerSigningTokens.Concat(_configuration.SigningTokens)); SecurityToken validatedToken; ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken); ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; JwtSecurityToken jwt = validatedToken as JwtSecurityToken; AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState; } if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe; } if (Options.UseTokenLifetime) { DateTime issued = jwt.ValidFrom; if (issued != DateTime.MinValue) { ticket.Properties.IssuedUtc = issued.ToUniversalTime(); } DateTime expires = jwt.ValidTo; if (expires != DateTime.MinValue) { ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); } ticket.Properties.AllowRefresh = false; } var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } ticket = securityTokenValidatedNotification.AuthenticationTicket; var protocolValidationContext = new OpenIdConnectProtocolValidationContext { AuthorizationCode = openIdConnectMessage.Code, Nonce = nonce, }; Options.ProtocolValidator.Validate(jwt, protocolValidationContext); if (openIdConnectMessage.Code != null) { var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { AuthenticationTicket = ticket, Code = openIdConnectMessage.Code, JwtSecurityToken = jwt, ProtocolMessage = openIdConnectMessage, RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, }; await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); if (authorizationCodeReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authorizationCodeReceivedNotification.Skipped) { return(null); } ticket = authorizationCodeReceivedNotification.AuthenticationTicket; } return(ticket); } catch (Exception exception) { authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: '" + authFailedEx.ToString()); if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { B2CConfigurationManager mgr = Options.ConfigurationManager as B2CConfigurationManager; mgr.RequestRefresh(policy); } var authenticationFailedNotification = new AuthenticationFailedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. if (Options.CallbackPath.HasValue && Options.CallbackPath != Request.Path) { return(null); } // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { // Buffer in case this body was not meant for us. MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); // Post preview release: a delegate on WsFederationAuthenticationOptions would allow for users to hook their own custom message. WsFederationMessage wsFederationMessage = new WsFederationMessage(form); if (!wsFederationMessage.IsSignInMessage) { Request.Body.Seek(0, SeekOrigin.Begin); return(null); } MessageReceivedNotification <WsFederationMessage> messageReceivedNotification = null; if (Options.Notifications != null && Options.Notifications.MessageReceived != null) { messageReceivedNotification = new MessageReceivedNotification <WsFederationMessage> { ProtocolMessage = wsFederationMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); } if (messageReceivedNotification != null && messageReceivedNotification.Cancel) { return(null); } if (wsFederationMessage.Wresult != null) { string token = wsFederationMessage.GetToken(); if (Options.Notifications != null && Options.Notifications.SecurityTokenReceived != null) { SecurityTokenReceivedNotification securityTokenReceivedNotification = new SecurityTokenReceivedNotification { SecurityToken = token }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.Cancel) { return(null); } } ExceptionDispatchInfo authFailedEx = null; try { ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(token, Options.TokenValidationParameters); ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; // Retrieve our cached redirect uri string state = wsFederationMessage.Wctx; AuthenticationProperties properties = GetPropertiesFromWctx(state); AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); Request.Context.Authentication.SignIn(claimsIdentity); if (Options.Notifications != null && Options.Notifications.SecurityTokenValidated != null) { await Options.Notifications.SecurityTokenValidated(new SecurityTokenValidatedNotification { AuthenticationTicket = ticket }); } return(ticket); } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { if (Options.Notifications != null && Options.Notifications.AuthenticationFailed != null) { // Post preview release: user can update metadata, need consistent messaging. var authenticationFailedNotification = new AuthenticationFailedNotification <WsFederationMessage>() { ProtocolMessage = wsFederationMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (!authenticationFailedNotification.Cancel) { authFailedEx.Throw(); } } else { authFailedEx.Throw(); } } } } return(null); }
/// <summary> /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options. /// </summary> /// <returns></returns> protected override async Task <AuthenticationTicket> HandleAuthenticateAsync() { string token = null; try { // Give application opportunity to find from a different location, adjust, or reject token var messageReceivedNotification = new MessageReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options) { ProtocolMessage = Context, }; // notification can set the token await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(messageReceivedNotification.AuthenticationTicket); } if (messageReceivedNotification.Skipped) { return(null); } // If application retrieved token from somewhere else, use that. token = messageReceivedNotification.Token; if (string.IsNullOrEmpty(token)) { var authorization = Request.Headers.Get("Authorization"); // If no authorization header found, nothing to process further if (string.IsNullOrEmpty(authorization)) { return(null); } if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = authorization.Substring("Bearer ".Length).Trim(); } // If no token found, no further work possible if (string.IsNullOrEmpty(token)) { return(null); } } // notify user token was received var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options) { ProtocolMessage = Context, SecurityToken = token, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(securityTokenReceivedNotification.AuthenticationTicket); } if (securityTokenReceivedNotification.Skipped) { return(null); } if (_configuration == null && Options.ConfigurationManager != null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } var validationParameters = Options.TokenValidationParameters.Clone(); if (_configuration != null) { if (validationParameters.ValidIssuer == null && !string.IsNullOrWhiteSpace(_configuration.Issuer)) { validationParameters.ValidIssuer = _configuration.Issuer; } else { var issuers = new[] { _configuration.Issuer }; validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? issuers : validationParameters.ValidIssuers.Concat(issuers)); } validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys)); } SecurityToken validatedToken; foreach (var validator in Options.SecurityTokenValidators) { if (validator.CanReadToken(token)) { var principal = validator.ValidateToken(token, validationParameters, out validatedToken); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme); var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options) { ProtocolMessage = Context, AuthenticationTicket = ticket }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(securityTokenValidatedNotification.AuthenticationTicket); } if (securityTokenValidatedNotification.Skipped) { return(null); } return(ticket); } } throw new InvalidOperationException("No SecurityTokenValidator available for token: " + token ?? "null"); } catch (Exception ex) { Logger.LogError("Exception occurred while processing message", ex); // 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 && ex.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = new AuthenticationFailedNotification <HttpContext, OAuthBearerAuthenticationOptions>(Context, Options) { ProtocolMessage = Context, Exception = ex }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(authenticationFailedNotification.AuthenticationTicket); } if (authenticationFailedNotification.Skipped) { return(null); } throw; } }
/// <summary> /// Invoked to process incoming authentication messages. /// </summary> /// <returns></returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. //if (Options.CallbackPath.HasValue && Options.CallbackPath != (Request.PathBase + Request.Path)) //{ // return null; //} var SamlMessage = await SamlMessageFromRequest(); if (SamlMessage == null || !SamlMessage.IsSignInMessage()) { return(null); } ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { ProtocolMessage = SamlMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { return(null); } //if (SamlMessage.Wresult == null) { // _logger.WriteWarning("Received a sign-in message without a WResult."); // return null; //} string token = SamlMessage.GetToken(); if (string.IsNullOrWhiteSpace(token)) { _logger.WriteWarning("Received a sign-in message without a token."); return(null); } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { ProtocolMessage = SamlMessage }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } // Copy and augment to avoid cross request race conditions for updated configurations. ClaimsPrincipal principal = null; ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; // Retrieve our cached redirect uri // string state = SamlMessage.Wctx; // WsFed allows for uninitiated logins, state may be missing. AuthenticationProperties properties = null;// GetPropertiesFromWctx(state); AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); //if (Options.UseTokenLifetime) { // // Override any session persistence to match the token lifetime. // DateTime issued = parsedToken.ValidFrom; // if (issued != DateTime.MinValue) { // ticket.Properties.IssuedUtc = issued.ToUniversalTime(); // } // DateTime expires = parsedToken.ValidTo; // if (expires != DateTime.MinValue) { // ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); // } // ticket.Properties.AllowRefresh = false; //} var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = SamlMessage, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; return(ticket); } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException); // 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 && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { // Options.ConfigurationManager.RequestRefresh(); //} var authenticationFailedNotification = new AuthenticationFailedNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { ProtocolMessage = SamlMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
private async Task <AuthenticationTicket> CreateAuthenticationTicket(OpenIdConnectMessage openIdConnectMessage) { var messageReceivedNotification = new MessageReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage }; await Options.Notifications.MessageReceived(messageReceivedNotification).ConfigureAwait(false); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { 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. AuthenticationProperties properties = GetPropertiesFromState(openIdConnectMessage.State); if (properties == null) { _logger.WriteWarning("The state field is missing or invalid."); return(null); } // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(openIdConnectMessage.Error)) { throw new OpenIdConnectProtocolException( string.Format(CultureInfo.InvariantCulture, openIdConnectMessage.Error, "Exception_OpenIdConnectMessageError", openIdConnectMessage.ErrorDescription ?? string.Empty, openIdConnectMessage.ErrorUri ?? string.Empty)); } // tokens.Item1 contains id token // tokens.Item2 contains access token Tuple <string, string> tokens = await GetTokens(openIdConnectMessage.Code, Options) .ConfigureAwait(false); if (string.IsNullOrWhiteSpace(openIdConnectMessage.IdToken)) { openIdConnectMessage.IdToken = tokens.Item1; } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification).ConfigureAwait(false); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled) .ConfigureAwait(false); } // Copy and augment to avoid cross request race conditions for updated configurations. TokenValidationParameters tvp = Options.TokenValidationParameters.Clone(); IEnumerable <string> issuers = new[] { _configuration.Issuer }; tvp.ValidIssuers = tvp.ValidIssuers?.Concat(issuers) ?? issuers; tvp.IssuerSigningTokens = tvp.IssuerSigningTokens?.Concat(_configuration.SigningTokens) ?? _configuration.SigningTokens; SecurityToken validatedToken; ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(openIdConnectMessage.IdToken, tvp, out validatedToken); ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; var claims = await GetClaims(tokens.Item2).ConfigureAwait(false); AddClaim(claims, claimsIdentity, "sub", ClaimTypes.NameIdentifier, Options.AuthenticationType); AddClaim(claims, claimsIdentity, "given_name", ClaimTypes.GivenName); AddClaim(claims, claimsIdentity, "family_name", ClaimTypes.Surname); AddClaim(claims, claimsIdentity, "preferred_username", ClaimTypes.Name); AddClaim(claims, claimsIdentity, "email", ClaimTypes.Email); // claims principal could have changed claim values, use bits received on wire for validation. JwtSecurityToken jwt = validatedToken as JwtSecurityToken; AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); if (Options.ProtocolValidator.RequireNonce) { if (String.IsNullOrWhiteSpace(openIdConnectMessage.Nonce)) { openIdConnectMessage.Nonce = jwt.Payload.Nonce; } // deletes the nonce cookie RetrieveNonce(openIdConnectMessage); } // remember 'session_state' and 'check_session_iframe' if (!string.IsNullOrWhiteSpace(openIdConnectMessage.SessionState)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = openIdConnectMessage.SessionState; } if (!string.IsNullOrWhiteSpace(_configuration.CheckSessionIframe)) { ticket.Properties.Dictionary[OpenIdConnectSessionProperties.CheckSessionIFrame] = _configuration.CheckSessionIframe; } if (Options.UseTokenLifetime) { // Override any session persistence to match the token lifetime. DateTime issued = jwt.ValidFrom; if (issued != DateTime.MinValue) { ticket.Properties.IssuedUtc = issued.ToUniversalTime(); } DateTime expires = jwt.ValidTo; if (expires != DateTime.MinValue) { ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); } ticket.Properties.AllowRefresh = false; } var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options) { AuthenticationTicket = ticket, ProtocolMessage = openIdConnectMessage, }; await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification).ConfigureAwait(false); if (securityTokenValidatedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenValidatedNotification.Skipped) { return(null); } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; // there is no hash of the code (c_hash) in the jwt obtained from the server // I don't know how to perform the validation using ProtocolValidator without the hash // that is why the code below is commented //var protocolValidationContext = new OpenIdConnectProtocolValidationContext //{ // AuthorizationCode = openIdConnectMessage.Code, // Nonce = nonce //}; //Options.ProtocolValidator.Validate(jwt, protocolValidationContext); if (openIdConnectMessage.Code != null) { var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { AuthenticationTicket = ticket, Code = openIdConnectMessage.Code, JwtSecurityToken = jwt, ProtocolMessage = openIdConnectMessage, RedirectUri = ticket.Properties.Dictionary.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, }; await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification) .ConfigureAwait(false); if (authorizationCodeReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authorizationCodeReceivedNotification.Skipped) { return(null); } // Flow possible changes ticket = authorizationCodeReceivedNotification.AuthenticationTicket; } return(ticket); }
/// <summary> /// Invokes the login procedure (2nd leg of SP-Initiated login). Analagous to Saml20SignonHandler from ASP.Net DLL /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <AuthenticationTicket> Invoke(IOwinContext context) { Logger.Debug(TraceMessages.SignOnHandlerCalled); ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = new SamlMessage(context, configuration, null) }; await options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(null); // GetHandledResponseTicket() } if (messageReceivedNotification.Skipped) { return(null); } var requestParams = await HandleResponse(context); var assertion = context.Get <Saml20Assertion>("Saml2:assertion"); var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = new SamlMessage(context, configuration, assertion) }; await options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(null); // GetHandledResponseTicket(); } if (securityTokenReceivedNotification.Skipped) { return(null); } var ticket = await GetAuthenticationTicket(context, requestParams); var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { AuthenticationTicket = ticket, ProtocolMessage = new SamlMessage(context, configuration, assertion) }; await options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(null); // GetHandledResponseTicket(); } if (securityTokenValidatedNotification.Skipped) { return(null); // null; } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(ticket.Identity, ticket.Properties); return(ticket); } catch (Exception ex) { authFailedEx = ExceptionDispatchInfo.Capture(ex); } if (authFailedEx != null) { Logger.Error("Exception occurred while processing message: " + authFailedEx.SourceException); var message = new SamlMessage(context, configuration, context.Get <Saml20Assertion>("Saml2:assertion")); var authenticationFailedNotification = new AuthenticationFailedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = message, Exception = authFailedEx.SourceException }; await options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(null);//GetHandledResponseTicket(); } if (authenticationFailedNotification.Skipped) { return(null); //null } authFailedEx.Throw(); } return(null); }
private static Task SecurityTokenReceived(SecurityTokenReceivedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification) { List<Claim> claims = new List<Claim> { new Claim(ClaimTypes.Email, "*****@*****.**"), new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"), }; notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)), new Http.Authentication.AuthenticationProperties(), notification.Options.AuthenticationScheme); notification.HandleResponse(); return Task.FromResult<object>(null); }