Beispiel #1
0
        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;
            }
        }
Beispiel #2
0
        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);
            }
        }
Beispiel #3
0
        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));
            }
        }
Beispiel #4
0
 private void LogError(TelemetryScopedLogger scopedLogger, Exception ex)
 {
     scopedLogger.Error(ex);
     if (environment.IsDevelopment())
     {
         Debug.WriteLine(ex.ToString());
     }
 }
Beispiel #5
0
        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));
            }
        }
Beispiel #6
0
        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));
            }
        }
Beispiel #7
0
        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));
            }
        }
Beispiel #8
0
        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
                        });
                    }
                }
            }
Beispiel #10
0
        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));
            }
        }
Beispiel #11
0
        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));
            }
        }
Beispiel #12
0
        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));
            }
        }
Beispiel #13
0
        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));
            }
        }
Beispiel #14
0
        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));
            }
        }
Beispiel #15
0
        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));
            }
        }
Beispiel #16
0
        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));
        }