public Task <List <Claim> > FromJwtToSamlClaims(IEnumerable <Claim> jwtClaims) { try { var mappings = GetMappings(RouteBinding); var samlClaims = new List <Claim>(); FromJwtAuthTimeToSaml(samlClaims, jwtClaims); FromJwtAmrToSaml(samlClaims, jwtClaims); foreach (var jwtClaim in jwtClaims.Where(c => c.Type != JwtClaimTypes.AuthTime && c.Type != JwtClaimTypes.Amr)) { var claimMap = mappings.FirstOrDefault(m => m.JwtClaim.Equals(jwtClaim.Type, StringComparison.InvariantCultureIgnoreCase)); if (claimMap != null) { samlClaims.Add(new Claim(claimMap.SamlClaim, jwtClaim.Value, jwtClaim.ValueType, jwtClaim.Issuer, jwtClaim.OriginalIssuer)); } else { samlClaims.Add(jwtClaim); } } return(Task.FromResult(samlClaims)); } catch (Exception ex) { logger.Error(ex, "Failed to map JWT claims to SAML claims."); throw; } }
public async Task <bool> VerifyConfirmationAsync() { try { try { var sequenceData = await sequenceLogic.GetSequenceDataAsync <ConfirmationSequenceData>(remove : true); logger.ScopeTrace($"Verify confirmation email '{sequenceData.Email}'."); var id = await User.IdFormat(new User.IdKey { TenantName = RouteBinding.TenantName, TrackName = RouteBinding.TrackName, Email = sequenceData.Email }); var user = await tenantRepository.GetAsync <User>(id, required : false); if (user == null || user.DisableAccount) { throw new ConfirmationException($"User with email '{sequenceData.Email}' do not exists or is disabled."); } var db = redisConnectionMultiplexer.GetDatabase(); await db.KeyDeleteAsync(ConfirmationEmailWaitPeriodRadisKey(user.Email)); if (!user.EmailVerified) { user.EmailVerified = true; await tenantRepository.SaveAsync(user); logger.ScopeTrace($"User confirmation with email '{user.Email}' and id '{user.UserId}'.", triggerEvent: true); } else { logger.ScopeTrace($"User re-confirmation with email '{user.Email}' and id '{user.UserId}'.", triggerEvent: true); } return(true); } catch (SequenceException sexc) { throw new ConfirmationException("Unable to read confirmation sequence data. Maybe the link have been used before.", sexc); } } catch (ConfirmationException ex) { logger.Error(ex); return(false); } }
private async Task <IActionResult> LogoutRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding) { var samlConfig = await saml2ConfigurationLogic.GetSamlDownConfigAsync(party); var saml2LogoutRequest = new Saml2LogoutRequest(samlConfig); binding.ReadSamlRequest(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest); logger.ScopeTrace(() => $"SAML Logout request '{saml2LogoutRequest.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); try { ValidateLogoutRequest(party, saml2LogoutRequest); try { binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest); logger.ScopeTrace(() => "Down, SAML Logout request accepted.", triggerEvent: true); } catch (Exception ex) { var isex = saml2ConfigurationLogic.GetInvalidSignatureValidationCertificateException(samlConfig, ex); if (isex != null) { throw isex; } throw; } await sequenceLogic.SaveSequenceDataAsync(new SamlDownSequenceData { Id = saml2LogoutRequest.Id.Value, RelayState = binding.RelayState }); var type = RouteBinding.ToUpParties.First().Type; logger.ScopeTrace(() => $"Request, Up type '{type}'."); switch (type) { case PartyTypes.Login: return(await serviceProvider.GetService <LogoutUpLogic>().LogoutRedirect(RouteBinding.ToUpParties.First(), GetLogoutRequest(party, saml2LogoutRequest))); case PartyTypes.OAuth2: throw new NotImplementedException(); case PartyTypes.Oidc: return(await serviceProvider.GetService <OidcRpInitiatedLogoutUpLogic <OidcUpParty, OidcUpClient> >().EndSessionRequestRedirectAsync(RouteBinding.ToUpParties.First(), GetLogoutRequest(party, saml2LogoutRequest))); case PartyTypes.Saml2: return(await serviceProvider.GetService <SamlLogoutUpLogic>().LogoutRequestRedirectAsync(RouteBinding.ToUpParties.First(), GetSamlLogoutRequest(party, saml2LogoutRequest))); default: throw new NotSupportedException($"Party type '{type}' not supported."); } } catch (SamlRequestException ex) { logger.Error(ex); return(await LogoutResponseAsync(party, samlConfig, saml2LogoutRequest.Id.Value, binding.RelayState, ex.Status)); } }
private void LogError(TelemetryScopedLogger scopedLogger, Exception ex) { scopedLogger.Error(ex); if (environment.IsDevelopment()) { Debug.WriteLine(ex.ToString()); } }
private async Task <IActionResult> LogoutRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding) { var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party); var saml2LogoutRequest = new Saml2LogoutRequest(samlConfig); binding.ReadSamlRequest(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest); logger.ScopeTrace($"SAML Logout request '{saml2LogoutRequest.XmlDocument.OuterXml}'."); try { ValidateLogoutRequest(party, saml2LogoutRequest); binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutRequest); logger.ScopeTrace("Down, SAML Logout request accepted.", triggerEvent: true); await sequenceLogic.SaveSequenceDataAsync(new SamlDownSequenceData { Id = saml2LogoutRequest.Id.Value, RelayState = binding.RelayState, ResponseUrl = party.LoggedOutUrl, }); await formActionLogic.CreateFormActionByUrlAsync(party.LoggedOutUrl); var type = RouteBinding.ToUpParties.First().Type; logger.ScopeTrace($"Request, Up type '{type}'."); switch (type) { case PartyTypes.Login: return(await serviceProvider.GetService <LogoutUpLogic>().LogoutRedirect(RouteBinding.ToUpParties.First(), new LogoutRequest { DownParty = party, SessionId = saml2LogoutRequest.SessionIndex, RequireLogoutConsent = false, PostLogoutRedirect = true, })); case PartyTypes.OAuth2: throw new NotImplementedException(); case PartyTypes.Oidc: throw new NotImplementedException(); case PartyTypes.Saml2: return(await serviceProvider.GetService <SamlLogoutUpLogic>().LogoutAsync(RouteBinding.ToUpParties.First(), GetSamlUpLogoutRequest(saml2LogoutRequest, party))); default: throw new NotSupportedException($"Party type '{type}' not supported."); } } catch (SamlRequestException ex) { logger.Error(ex); return(await LogoutResponseAsync(party.Id, samlConfig, saml2LogoutRequest.Id.Value, binding.RelayState, saml2LogoutRequest.Destination?.OriginalString, party.AuthnBinding.ResponseBinding, ex.Status)); } }
private async Task <IActionResult> LogoutResponseAsync <T>(SamlUpParty party, Saml2Binding <T> binding) { var samlConfig = saml2ConfigurationLogic.GetSamlUpConfig(party); var saml2LogoutResponse = new Saml2LogoutResponse(samlConfig); binding.ReadSamlResponse(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse); await sequenceLogic.ValidateSequenceAsync(binding.RelayState); var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(); try { logger.ScopeTrace($"SAML Logout response '{saml2LogoutResponse.XmlDocument.OuterXml}'."); logger.SetScopeProperty("status", saml2LogoutResponse.Status.ToString()); logger.ScopeTrace("Up, SAML Logout response.", triggerEvent: true); if (saml2LogoutResponse.Status != Saml2StatusCodes.Success) { throw new SamlRequestException("Unsuccessful Logout response.") { RouteBinding = RouteBinding, Status = saml2LogoutResponse.Status }; } binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse); logger.ScopeTrace("Up, Successful SAML Logout response.", triggerEvent: true); return(await LogoutResponseDownAsync(sequenceData, saml2LogoutResponse.Status, saml2LogoutResponse.SessionIndex)); } catch (SamlRequestException ex) { logger.Error(ex); return(await LogoutResponseDownAsync(sequenceData, ex.Status)); } catch (Exception ex) { logger.Error(ex); return(await LogoutResponseDownAsync(sequenceData, Saml2StatusCodes.Responder)); } }
private async Task <IActionResult> AuthnRequestAsync <T>(SamlDownParty party, Saml2Binding <T> binding) { var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party); var request = HttpContext.Request; var saml2AuthnRequest = new Saml2AuthnRequest(samlConfig); binding.ReadSamlRequest(request.ToGenericHttpRequest(), saml2AuthnRequest); logger.ScopeTrace($"SAML Authn request '{saml2AuthnRequest.XmlDocument.OuterXml}'."); try { ValidateAuthnRequest(party, saml2AuthnRequest); binding.Unbind(request.ToGenericHttpRequest(), saml2AuthnRequest); logger.ScopeTrace("Down, SAML Auth request accepted.", triggerEvent: true); var responseUrl = GetAcsUrl(party, saml2AuthnRequest); await sequenceLogic.SaveSequenceDataAsync(new SamlDownSequenceData { Id = saml2AuthnRequest.Id.Value, RelayState = binding.RelayState, ResponseUrl = responseUrl, }); await formActionLogic.CreateFormActionByUrlAsync(responseUrl); var type = RouteBinding.ToUpParties.First().Type; logger.ScopeTrace($"Request, Up type '{type}'."); switch (type) { case PartyTypes.Login: return(await serviceProvider.GetService <LoginUpLogic>().LoginRedirectAsync(RouteBinding.ToUpParties.First(), GetLoginRequestAsync(party, saml2AuthnRequest))); case PartyTypes.OAuth2: throw new NotImplementedException(); case PartyTypes.Oidc: return(await serviceProvider.GetService <OidcAuthUpLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationRequestAsync(RouteBinding.ToUpParties.First())); case PartyTypes.Saml2: return(await serviceProvider.GetService <SamlAuthnUpLogic>().AuthnRequestAsync(RouteBinding.ToUpParties.First(), GetLoginRequestAsync(party, saml2AuthnRequest))); default: throw new NotSupportedException($"Party type '{type}' not supported."); } } catch (SamlRequestException ex) { logger.Error(ex); return(await AuthnResponseAsync(party.Id, samlConfig, saml2AuthnRequest.Id.Value, binding.RelayState, GetAcsUrl(party, saml2AuthnRequest), party.AuthnBinding.ResponseBinding, ex.Status)); } }
public async Task <ClaimsPrincipal> ValidatePartyClientTokenAsync(TClient client, string token, bool validateLifetime = true) { var issuerSigningKeys = new List <JsonWebKey>(); issuerSigningKeys.Add(RouteBinding.PrimaryKey.Key); if (RouteBinding.SecondaryKey != null) { issuerSigningKeys.Add(RouteBinding.SecondaryKey.Key); } try { (var claimsPrincipal, var securityToken) = await Task.FromResult(JwtHandler.ValidateToken(token, Issuer(RouteBinding), issuerSigningKeys, audience: client.ClientId, validateLifetime: validateLifetime)); return(claimsPrincipal); } catch (Exception ex) { logger.Error(ex, $"Party client JWT not valid. Client id '{client.ClientId}', Route '{RouteBinding.Route}'."); return(null); } }
public override void OnException(ExceptionContext context) { if (context.Exception != null) { logger.Error(context.Exception); if (context.Exception is OAuthRequestException) { context.Result = OAuthRequestExceptionToJsonResult(context.Exception as OAuthRequestException); } else if (context.Exception.InnerException is OAuthRequestException) { context.Result = OAuthRequestExceptionToJsonResult(context.Exception.InnerException as OAuthRequestException); } else { context.Result = new JsonResult(new TokenResponse { Error = IdentityConstants.ResponseErrors.InvalidRequest }); } } }
public async Task <IActionResult> AuthenticationResponseAsync(string partyId) { logger.ScopeTrace(() => $"Up, OIDC Authentication response."); logger.SetScopeProperty(Constants.Logs.UpPartyId, partyId); var party = await tenantRepository.GetAsync <TParty>(partyId); logger.SetScopeProperty(Constants.Logs.UpPartyClientId, party.Client.ClientId); var formOrQueryDictionary = HttpContext.Request.Method switch { "POST" => party.Client.ResponseMode == IdentityConstants.ResponseModes.FormPost ? HttpContext.Request.Form.ToDictionary() : throw new NotSupportedException($"POST not supported by response mode '{party.Client.ResponseMode}'."), "GET" => party.Client.ResponseMode == IdentityConstants.ResponseModes.Query ? HttpContext.Request.Query.ToDictionary() : throw new NotSupportedException($"GET not supported by response mode '{party.Client.ResponseMode}'."), _ => throw new NotSupportedException($"Request method not supported by response mode '{party.Client.ResponseMode}'") }; var authenticationResponse = formOrQueryDictionary.ToObject <AuthenticationResponse>(); logger.ScopeTrace(() => $"Up, Authentication response '{authenticationResponse.ToJsonIndented()}'.", traceType: TraceTypes.Message); if (authenticationResponse.State.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(authenticationResponse.State), authenticationResponse.GetTypeName()); } await sequenceLogic.ValidateExternalSequenceIdAsync(authenticationResponse.State); var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcUpSequenceData>(remove : true); var sessionResponse = formOrQueryDictionary.ToObject <SessionResponse>(); if (sessionResponse != null) { logger.ScopeTrace(() => $"Up, Session response '{sessionResponse.ToJsonIndented()}'.", traceType: TraceTypes.Message); } try { logger.ScopeTrace(() => "Up, OIDC Authentication response.", triggerEvent: true); bool isImplicitFlow = !party.Client.ResponseType.Contains(IdentityConstants.ResponseTypes.Code); ValidateAuthenticationResponse(party, authenticationResponse, sessionResponse, isImplicitFlow); (var claims, var idToken) = isImplicitFlow switch { true => await ValidateTokensAsync(party, sequenceData, authenticationResponse.IdToken, authenticationResponse.AccessToken, true), false => await HandleAuthorizationCodeResponseAsync(party, sequenceData, authenticationResponse.Code) }; logger.ScopeTrace(() => "Up, Successful OIDC Authentication response.", triggerEvent: true); logger.ScopeTrace(() => $"Up, OIDC received JWT claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim); var externalSessionId = claims.FindFirstValue(c => c.Type == JwtClaimTypes.SessionId); externalSessionId.ValidateMaxLength(IdentityConstants.MessageLength.SessionIdMax, nameof(externalSessionId), "Session state or claim"); claims = claims.Where(c => c.Type != JwtClaimTypes.SessionId && c.Type != Constants.JwtClaimTypes.UpParty && c.Type != Constants.JwtClaimTypes.UpPartyType).ToList(); claims.AddClaim(Constants.JwtClaimTypes.UpParty, party.Name); claims.AddClaim(Constants.JwtClaimTypes.UpPartyType, party.Type.ToString().ToLower()); var transformedClaims = await claimTransformLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims); var validClaims = ValidateClaims(party, transformedClaims); var sessionId = await sessionUpPartyLogic.CreateOrUpdateSessionAsync(party, party.DisableSingleLogout?null : sequenceData.DownPartyLink, validClaims, externalSessionId, idToken); if (!sessionId.IsNullOrEmpty()) { validClaims.AddClaim(JwtClaimTypes.SessionId, sessionId); } logger.ScopeTrace(() => $"Up, OIDC output JWT claims '{validClaims.ToFormattedString()}'", traceType: TraceTypes.Claim); return(await AuthenticationResponseDownAsync(sequenceData, claims : validClaims)); } catch (StopSequenceException) { throw; } catch (OAuthRequestException orex) { logger.SetScopeProperty(Constants.Logs.UpPartyStatus, orex.Error); logger.Error(orex); return(await AuthenticationResponseDownAsync(sequenceData, error : orex.Error, errorDescription : orex.ErrorDescription)); } catch (ResponseErrorException rex) { logger.SetScopeProperty(Constants.Logs.UpPartyStatus, rex.Error); logger.Error(rex); return(await AuthenticationResponseDownAsync(sequenceData, error : rex.Error, errorDescription : $"{party.Name}|{rex.Message}")); } catch (Exception ex) { logger.Error(ex); return(await AuthenticationResponseDownAsync(sequenceData, error : IdentityConstants.ResponseErrors.InvalidRequest)); } }
public async Task <IActionResult> AuthenticationRequestAsync(string partyId) { logger.ScopeTrace("Down, OIDC Authentication request."); logger.SetScopeProperty("downPartyId", partyId); var party = await tenantRepository.GetAsync <TParty>(partyId); if (party.Client == null) { throw new NotSupportedException($"Party Client not configured."); } var queryDictionary = HttpContext.Request.Query.ToDictionary(); var authenticationRequest = queryDictionary.ToObject <AuthenticationRequest>(); logger.ScopeTrace($"Authentication request '{authenticationRequest.ToJsonIndented()}'."); logger.SetScopeProperty("clientId", authenticationRequest.ClientId); var codeChallengeSecret = party.Client.EnablePkce.Value ? queryDictionary.ToObject <CodeChallengeSecret>() : null; if (codeChallengeSecret != null) { codeChallengeSecret.Validate(); logger.ScopeTrace($"CodeChallengeSecret '{codeChallengeSecret.ToJsonIndented()}'."); } try { var requireCodeFlow = party.Client.EnablePkce.Value && codeChallengeSecret != null; ValidateAuthenticationRequest(party.Client, authenticationRequest, requireCodeFlow); logger.ScopeTrace("Down, OIDC Authentication request accepted.", triggerEvent: true); if (!authenticationRequest.UiLocales.IsNullOrWhiteSpace()) { await sequenceLogic.SetCultureAsync(authenticationRequest.UiLocales.ToSpaceList()); } await sequenceLogic.SaveSequenceDataAsync(new OidcDownSequenceData { ResponseType = authenticationRequest.ResponseType, RedirectUri = authenticationRequest.RedirectUri, Scope = authenticationRequest.Scope, State = authenticationRequest.State, ResponseMode = authenticationRequest.ResponseMode, Nonce = authenticationRequest.Nonce, CodeChallenge = codeChallengeSecret?.CodeChallenge, CodeChallengeMethod = codeChallengeSecret?.CodeChallengeMethod, }); await formActionLogic.CreateFormActionByUrlAsync(authenticationRequest.RedirectUri); var type = RouteBinding.ToUpParties.First().Type; logger.ScopeTrace($"Request, Up type '{type}'."); switch (type) { case PartyTypes.Login: return(await serviceProvider.GetService <LoginUpLogic>().LoginRedirectAsync(RouteBinding.ToUpParties.First(), await GetLoginRequestAsync(party, authenticationRequest))); case PartyTypes.OAuth2: throw new NotImplementedException(); case PartyTypes.Oidc: return(await serviceProvider.GetService <OidcAuthUpLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationRequestAsync(RouteBinding.ToUpParties.First())); case PartyTypes.Saml2: return(await serviceProvider.GetService <SamlAuthnUpLogic>().AuthnRequestAsync(RouteBinding.ToUpParties.First(), await GetLoginRequestAsync(party, authenticationRequest))); default: throw new NotSupportedException($"Party type '{type}' not supported."); } } catch (OAuthRequestException ex) { logger.Error(ex); return(await AuthenticationResponseErrorAsync(partyId, authenticationRequest, ex)); } }
private async Task <IActionResult> AuthnResponseAsync <T>(SamlUpParty party, Saml2Binding <T> binding) { var request = HttpContext.Request; var samlConfig = saml2ConfigurationLogic.GetSamlUpConfig(party); var saml2AuthnResponse = new Saml2AuthnResponse(samlConfig); binding.ReadSamlResponse(request.ToGenericHttpRequest(), saml2AuthnResponse); await sequenceLogic.ValidateSequenceAsync(binding.RelayState); var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(); try { logger.ScopeTrace($"SAML Authn response '{saml2AuthnResponse.XmlDocument.OuterXml}'."); logger.SetScopeProperty("status", saml2AuthnResponse.Status.ToString()); logger.ScopeTrace("Up, SAML Authn response.", triggerEvent: true); if (saml2AuthnResponse.Status != Saml2StatusCodes.Success) { throw new SamlRequestException("Unsuccessful Authn response.") { RouteBinding = RouteBinding, Status = saml2AuthnResponse.Status }; } binding.Unbind(request.ToGenericHttpRequest(), saml2AuthnResponse); logger.ScopeTrace("Up, Successful SAML Authn response.", triggerEvent: true); var claims = saml2AuthnResponse.ClaimsIdentity?.Claims; if (saml2AuthnResponse.ClaimsIdentity?.Claims?.Count() <= 0) { throw new SamlRequestException("Empty claims collection.") { RouteBinding = RouteBinding, Status = Saml2StatusCodes.Responder }; } if (!claims.Any(c => c.Type == ClaimTypes.NameIdentifier)) { claims = AddNameIdClaim(claims); } claims = await claimTransformationsLogic.Transform(party.ClaimTransformations?.ConvertAll(t => (ClaimTransformation)t), claims); claims = ValidateClaims(party, claims); return(await AuthnResponseDownAsync(sequenceData, saml2AuthnResponse.Status, claims)); } catch (SamlRequestException ex) { logger.Error(ex); return(await AuthnResponseDownAsync(sequenceData, ex.Status)); } catch (Exception ex) { logger.Error(ex); return(await AuthnResponseDownAsync(sequenceData, Saml2StatusCodes.Responder)); } }
private async Task <IActionResult> AuthnResponseAsync <T>(SamlUpParty party, Saml2Binding <T> binding) { var request = HttpContext.Request; var samlConfig = await saml2ConfigurationLogic.GetSamlUpConfigAsync(party, includeSigningAndDecryptionCertificate : true); var saml2AuthnResponse = new Saml2AuthnResponse(samlConfig); try { binding.ReadSamlResponse(request.ToGenericHttpRequest(), saml2AuthnResponse); } catch (Exception ex) { if (samlConfig.SecondaryDecryptionCertificate != null && binding is Saml2PostBinding && ex.Source.Contains("cryptography", StringComparison.OrdinalIgnoreCase)) { samlConfig.DecryptionCertificate = samlConfig.SecondaryDecryptionCertificate; saml2AuthnResponse = new Saml2AuthnResponse(samlConfig); binding.ReadSamlResponse(request.ToGenericHttpRequest(), saml2AuthnResponse); logger.ScopeTrace(() => $"SAML Authn response decrypted with secondary certificate.", traceType: TraceTypes.Message); } else { throw; } } if (binding.RelayState.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(binding.RelayState), binding.GetTypeName()); } await sequenceLogic.ValidateExternalSequenceIdAsync(binding.RelayState); var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(); try { logger.ScopeTrace(() => $"SAML Authn response '{saml2AuthnResponse.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); logger.SetScopeProperty(Constants.Logs.UpPartyStatus, saml2AuthnResponse.Status.ToString()); logger.ScopeTrace(() => "Up, SAML Authn response.", triggerEvent: true); if (saml2AuthnResponse.Status != Saml2StatusCodes.Success) { throw new SamlRequestException("Unsuccessful Authn response.") { RouteBinding = RouteBinding, Status = saml2AuthnResponse.Status }; } try { binding.Unbind(request.ToGenericHttpRequest(), saml2AuthnResponse); logger.ScopeTrace(() => "Up, Successful SAML Authn response.", triggerEvent: true); } catch (Exception ex) { var isex = saml2ConfigurationLogic.GetInvalidSignatureValidationCertificateException(samlConfig, ex); if (isex != null) { throw isex; } throw; } if (saml2AuthnResponse.ClaimsIdentity?.Claims?.Count() <= 0) { throw new SamlRequestException("Empty claims collection.") { RouteBinding = RouteBinding, Status = Saml2StatusCodes.Responder }; } var claims = new List <Claim>(saml2AuthnResponse.ClaimsIdentity.Claims.Where(c => c.Type != ClaimTypes.NameIdentifier)); var nameIdClaim = GetNameIdClaim(party.Name, saml2AuthnResponse.ClaimsIdentity.Claims); if (nameIdClaim != null) { claims.Add(nameIdClaim); } logger.ScopeTrace(() => $"Up, SAML Authn received SAML claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim); var externalSessionId = claims.FindFirstValue(c => c.Type == Saml2ClaimTypes.SessionIndex); externalSessionId.ValidateMaxLength(IdentityConstants.MessageLength.SessionIdMax, nameof(externalSessionId), "Session index claim"); claims = claims.Where(c => c.Type != Saml2ClaimTypes.SessionIndex && c.Type != Constants.SamlClaimTypes.UpParty && c.Type != Constants.SamlClaimTypes.UpPartyType).ToList(); claims.AddClaim(Constants.SamlClaimTypes.UpParty, party.Name); claims.AddClaim(Constants.SamlClaimTypes.UpPartyType, party.Type.ToString().ToLower()); var transformedClaims = await claimTransformLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims); var validClaims = ValidateClaims(party, transformedClaims); logger.ScopeTrace(() => $"Up, SAML Authn output SAML claims '{validClaims.ToFormattedString()}'", traceType: TraceTypes.Claim); var jwtValidClaims = await claimsDownLogic.FromSamlToJwtClaimsAsync(validClaims); var sessionId = await sessionUpPartyLogic.CreateOrUpdateSessionAsync(party, party.DisableSingleLogout?null : sequenceData.DownPartyLink, jwtValidClaims, externalSessionId); if (!sessionId.IsNullOrEmpty()) { jwtValidClaims.AddClaim(JwtClaimTypes.SessionId, sessionId); } logger.ScopeTrace(() => $"Up, SAML Authn output JWT claims '{jwtValidClaims.ToFormattedString()}'", traceType: TraceTypes.Claim); return(await AuthnResponseDownAsync(sequenceData, saml2AuthnResponse.Status, jwtValidClaims)); } catch (StopSequenceException) { throw; } catch (SamlRequestException ex) { logger.Error(ex); return(await AuthnResponseDownAsync(sequenceData, ex.Status)); } catch (Exception ex) { logger.Error(ex); return(await AuthnResponseDownAsync(sequenceData, Saml2StatusCodes.Responder)); } }
private async Task <IActionResult> LogoutResponseAsync <T>(SamlUpParty party, Saml2Binding <T> binding) { var samlConfig = await saml2ConfigurationLogic.GetSamlUpConfigAsync(party); var saml2LogoutResponse = new Saml2LogoutResponse(samlConfig); binding.ReadSamlResponse(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse); await sequenceLogic.ValidateExternalSequenceIdAsync(binding.RelayState); var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(remove : party.DisableSingleLogout); try { logger.ScopeTrace(() => $"SAML Logout response '{saml2LogoutResponse.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); logger.SetScopeProperty(Constants.Logs.Status, saml2LogoutResponse.Status.ToString()); logger.ScopeTrace(() => "Up, SAML Logout response.", triggerEvent: true); if (saml2LogoutResponse.Status != Saml2StatusCodes.Success) { throw new SamlRequestException("Unsuccessful Logout response.") { RouteBinding = RouteBinding, Status = saml2LogoutResponse.Status }; } try { binding.Unbind(HttpContext.Request.ToGenericHttpRequest(), saml2LogoutResponse); logger.ScopeTrace(() => "Up, Successful SAML Logout response.", triggerEvent: true); } catch (Exception ex) { var isex = saml2ConfigurationLogic.GetInvalidSignatureValidationCertificateException(samlConfig, ex); if (isex != null) { throw isex; } throw; } if (party.DisableSingleLogout) { return(await LogoutResponseDownAsync(sequenceData)); } else { (var doSingleLogout, var singleLogoutSequenceData) = await singleLogoutDownLogic.InitializeSingleLogoutAsync(new UpPartyLink { Name = party.Name, Type = party.Type }, sequenceData.DownPartyLink, sequenceData.SessionDownPartyLinks, sequenceData.SessionClaims); if (doSingleLogout) { return(await singleLogoutDownLogic.StartSingleLogoutAsync(singleLogoutSequenceData)); } else { await sequenceLogic.RemoveSequenceDataAsync <SamlUpSequenceData>(); return(await LogoutResponseDownAsync(sequenceData)); } } } catch (StopSequenceException) { throw; } catch (SamlRequestException ex) { logger.Error(ex); return(await LogoutResponseDownAsync(sequenceData, status : ex.Status)); } catch (Exception ex) { logger.Error(ex); return(await LogoutResponseDownAsync(sequenceData, status : Saml2StatusCodes.Responder)); } }
private async Task <IActionResult> AuthnResponseAsync <T>(SamlUpParty party, Saml2Binding <T> binding) { var request = HttpContext.Request; var samlConfig = saml2ConfigurationLogic.GetSamlUpConfig(party); var saml2AuthnResponse = new Saml2AuthnResponse(samlConfig); binding.ReadSamlResponse(request.ToGenericHttpRequest(), saml2AuthnResponse); if (binding.RelayState.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(binding.RelayState), binding.GetTypeName()); } await sequenceLogic.ValidateSequenceAsync(binding.RelayState); var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(); try { logger.ScopeTrace($"SAML Authn response '{saml2AuthnResponse.XmlDocument.OuterXml}'."); logger.SetScopeProperty("upPartyStatus", saml2AuthnResponse.Status.ToString()); logger.ScopeTrace("Up, SAML Authn response.", triggerEvent: true); if (saml2AuthnResponse.Status != Saml2StatusCodes.Success) { throw new SamlRequestException("Unsuccessful Authn response.") { RouteBinding = RouteBinding, Status = saml2AuthnResponse.Status }; } binding.Unbind(request.ToGenericHttpRequest(), saml2AuthnResponse); logger.ScopeTrace("Up, Successful SAML Authn response.", triggerEvent: true); if (saml2AuthnResponse.ClaimsIdentity?.Claims?.Count() <= 0) { throw new SamlRequestException("Empty claims collection.") { RouteBinding = RouteBinding, Status = Saml2StatusCodes.Responder }; } var claims = new List <Claim>(saml2AuthnResponse.ClaimsIdentity.Claims.Where(c => c.Type != ClaimTypes.NameIdentifier)); var nameIdClaim = GetNameIdClaim(party.Name, saml2AuthnResponse.ClaimsIdentity.Claims); if (nameIdClaim != null) { claims.Add(nameIdClaim); } var externalSessionId = claims.FindFirstValue(c => c.Type == Saml2ClaimTypes.SessionIndex); externalSessionId.ValidateMaxLength(IdentityConstants.MessageLength.SessionIdMax, nameof(externalSessionId), "Session index claim"); claims = claims.Where(c => c.Type != Saml2ClaimTypes.SessionIndex && c.Type != Constants.SamlClaimTypes.UpPary && c.Type != Constants.SamlClaimTypes.UpParyType).ToList(); claims.AddClaim(Constants.SamlClaimTypes.UpPary, party.Name); claims.AddClaim(Constants.SamlClaimTypes.UpParyType, party.Type.ToString().ToLower()); var transformedClaims = await claimTransformationsLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims); var validClaims = ValidateClaims(party, transformedClaims); var jwtValidClaims = await claimsDownLogic.FromSamlToJwtClaimsAsync(validClaims); var sessionId = await sessionUpPartyLogic.CreateOrUpdateSessionAsync(party, party.DisableSingleLogout?null : sequenceData.DownPartyLink, jwtValidClaims, externalSessionId); if (!sessionId.IsNullOrEmpty()) { jwtValidClaims.AddClaim(JwtClaimTypes.SessionId, sessionId); } return(await AuthnResponseDownAsync(sequenceData, saml2AuthnResponse.Status, jwtValidClaims)); } catch (StopSequenceException) { throw; } catch (SamlRequestException ex) { logger.Error(ex); return(await AuthnResponseDownAsync(sequenceData, ex.Status)); } catch (Exception ex) { logger.Error(ex); return(await AuthnResponseDownAsync(sequenceData, Saml2StatusCodes.Responder)); } }
public async Task <IActionResult> Index() { var errorViewModel = new ErrorViewModel { CreateTime = DateTimeOffset.Now, RequestId = HttpContext.TraceIdentifier }; var exceptionHandlerPathFeature = HttpContext.Features.Get <IExceptionHandlerPathFeature>(); var exception = exceptionHandlerPathFeature?.Error; if (exceptionHandlerPathFeature != null && exceptionHandlerPathFeature.Path.EndsWith($"/{Constants.Routes.OAuthController}/{Constants.Endpoints.Token}", StringComparison.OrdinalIgnoreCase)) { return(HandleOAuthTokenException(exception)); } if (RouteBinding != null && !exceptionHandlerPathFeature.Path.IsNullOrEmpty()) { try { var sequenceStartIndex = exceptionHandlerPathFeature.Path.IndexOf('_') + 1; if (exceptionHandlerPathFeature.Path.Length > sequenceStartIndex) { var sequence = await sequenceLogic.TryReadSequenceAsync(exceptionHandlerPathFeature.Path.Substring(sequenceStartIndex)); if (sequence != null) { var uiLoginUpParty = await tenantRepository.GetAsync <UiLoginUpPartyData>(sequence.UiUpPartyId); securityHeaderLogic.AddImgSrc(uiLoginUpParty.IconUrl); securityHeaderLogic.AddImgSrcFromCss(uiLoginUpParty.Css); errorViewModel.Title = uiLoginUpParty.Title; errorViewModel.IconUrl = uiLoginUpParty.IconUrl; errorViewModel.Css = uiLoginUpParty.Css; } } } catch (Exception ex) { logger.Error(ex); } } var sequenceTimeoutException = FindException <SequenceTimeoutException>(exception); if (sequenceTimeoutException != null) { return(HandleSequenceTimeoutException(errorViewModel, sequenceTimeoutException)); } var routeCreationException = FindException <RouteCreationException>(exception); if (routeCreationException != null) { return(HandleRouteCreationException(errorViewModel, routeCreationException)); } var externalKeyIsNotReadyException = FindException <ExternalKeyIsNotReadyException>(exception); if (externalKeyIsNotReadyException != null) { return(HandleexternalKeyIsNotReadyException(errorViewModel)); } if (environment.IsDevelopment()) { errorViewModel.TechnicalErrors = new List <string>(exception.ToString().Split('\n')); } else { errorViewModel.TechnicalErrors = exception.GetAllMessages(); } return(View(errorViewModel)); }