Esempio n. 1
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));
            }
        }
Esempio n. 2
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));
            }
        }
Esempio n. 3
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));
            }
        }