Beispiel #1
0
        public async Task <IActionResult> AuthnRequestAsync(string partyId)
        {
            logger.ScopeTrace(() => "Up, SAML Authn request.");
            logger.SetScopeProperty(Constants.Logs.UpPartyId, partyId);
            var samlUpSequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(remove : false);

            if (!samlUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal))
            {
                throw new Exception("Invalid up-party id.");
            }

            var party = await tenantRepository.GetAsync <SamlUpParty>(samlUpSequenceData.UpPartyId);

            await samlMetadataReadUpLogic.CheckMetadataAndUpdateUpPartyAsync(party);

            switch (party.AuthnBinding.RequestBinding)
            {
            case SamlBindingTypes.Redirect:
                return(await AuthnRequestAsync(party, new Saml2RedirectBinding(), samlUpSequenceData));

            case SamlBindingTypes.Post:
                return(await AuthnRequestAsync(party, new Saml2PostBinding(), samlUpSequenceData));

            default:
                throw new NotSupportedException($"Binding '{party.AuthnBinding.RequestBinding}' not supported.");
            }
        }
        public async Task <IActionResult> EndSessionRequestAsync(string partyId)
        {
            logger.ScopeTrace("Up, OIDC End session request.");
            var oidcUpSequenceData = await sequenceLogic.GetSequenceDataAsync <OidcUpSequenceData>(remove : false);

            if (!oidcUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal))
            {
                throw new Exception("Invalid up-party id.");
            }
            logger.SetScopeProperty("upPartyId", oidcUpSequenceData.UpPartyId);

            var party = await tenantRepository.GetAsync <OidcUpParty>(oidcUpSequenceData.UpPartyId);

            logger.SetScopeProperty("upPartyClientId", party.Client.ClientId);
            ValidatePartyLogoutSupport(party);

            var postLogoutRedirectUrl    = HttpContext.GetUpPartyUrl(party.Name, Constants.Routes.OAuthController, Constants.Endpoints.EndSessionResponse, partyBindingPattern: party.PartyBindingPattern);
            var rpInitiatedLogoutRequest = new RpInitiatedLogoutRequest
            {
                PostLogoutRedirectUri = postLogoutRedirectUrl,
                State = SequenceString
            };

            var session = await sessionUpPartyLogic.GetSessionAsync(party);

            if (session != null)
            {
                try
                {
                    if (!oidcUpSequenceData.SessionId.Equals(session.SessionId, StringComparison.Ordinal))
                    {
                        throw new Exception("Requested session ID do not match up-party session ID.");
                    }
                }
                catch (Exception ex)
                {
                    logger.Warning(ex);
                }

                rpInitiatedLogoutRequest.IdTokenHint = session.IdToken;

                oidcUpSequenceData.SessionDownPartyLinks = session.DownPartyLinks;
                oidcUpSequenceData.SessionClaims         = session.Claims;
                await sequenceLogic.SaveSequenceDataAsync(oidcUpSequenceData);
            }
            logger.ScopeTrace($"Up, End session request '{rpInitiatedLogoutRequest.ToJsonIndented()}'.");

            _ = await sessionUpPartyLogic.DeleteSessionAsync(session);

            await oauthRefreshTokenGrantLogic.DeleteRefreshTokenGrantsAsync(oidcUpSequenceData.SessionId);

            securityHeaderLogic.AddFormActionAllowAll();

            var nameValueCollection = rpInitiatedLogoutRequest.ToDictionary();

            logger.ScopeTrace($"Up, End session request URL '{party.Client.EndSessionUrl}'.");
            logger.ScopeTrace("Up, Sending OIDC End session request.", triggerEvent: true);
            return(await nameValueCollection.ToRedirectResultAsync(party.Client.EndSessionUrl));
        }
Beispiel #3
0
        public async Task AddFormActionByUrlAsync(string url)
        {
            if (!url.IsNullOrEmpty())
            {
                var formActionSequenceData = await sequenceLogic.GetSequenceDataAsync <FormActionSequenceData>(remove : false);

                if (AddUrlToDomains(url, formActionSequenceData.Domains))
                {
                    await sequenceLogic.SaveSequenceDataAsync(formActionSequenceData);
                }
            }
        }
Beispiel #4
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 #5
0
        public async Task <IActionResult> EndSessionResponseAsync(string partyId)
        {
            logger.ScopeTrace(() => "Down, End session response.");
            logger.SetScopeProperty(Constants.Logs.DownPartyId, partyId);

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcDownSequenceData>(false);

            var rpInitiatedLogoutResponse = new RpInitiatedLogoutResponse
            {
                State = sequenceData.State,
            };

            logger.ScopeTrace(() => $"End session response '{rpInitiatedLogoutResponse.ToJsonIndented()}'.", traceType: TraceTypes.Message);
            var nameValueCollection = rpInitiatedLogoutResponse.ToDictionary();

            logger.ScopeTrace(() => $"Redirect Uri '{sequenceData.RedirectUri}'.");
            logger.ScopeTrace(() => "Down, OIDC End session response.", triggerEvent: true);

            await sequenceLogic.RemoveSequenceDataAsync <OidcDownSequenceData>();

            if (sequenceData.RestrictFormAction)
            {
                securityHeaderLogic.AddFormAction(sequenceData.RedirectUri);
            }
            else
            {
                securityHeaderLogic.AddFormActionAllowAll();
            }
            return(await nameValueCollection.ToRedirectResultAsync(sequenceData.RedirectUri));
        }
Beispiel #6
0
        public async Task <IActionResult> LogoutResponseAsync(string partyId, Saml2StatusCodes status = Saml2StatusCodes.Success, string sessionIndex = null)
        {
            logger.ScopeTrace($"Down, SAML Logout response{(status != Saml2StatusCodes.Success ? " error" : string.Empty )}, Status code '{status}'.");
            logger.SetScopeProperty("downPartyId", partyId);

            var party = await tenantRepository.GetAsync <SamlDownParty>(partyId);

            ValidatePartyLogoutSupport(party);

            var samlConfig = saml2ConfigurationLogic.GetSamlDownConfig(party, true);

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlDownSequenceData>(false);

            logger.ScopeTrace($"Binding '{party.LogoutBinding.ResponseBinding}'");
            switch (party.LogoutBinding.ResponseBinding)
            {
            case SamlBindingType.Redirect:
                return(await LogoutResponseAsync(samlConfig, sequenceData.Id, sequenceData.RelayState, sequenceData.ResponseUrl, new Saml2RedirectBinding(), status, sessionIndex));

            case SamlBindingType.Post:
                return(await LogoutResponseAsync(samlConfig, sequenceData.Id, sequenceData.RelayState, sequenceData.ResponseUrl, new Saml2PostBinding(), status, sessionIndex));

            default:
                throw new NotSupportedException($"SAML binding '{party.LogoutBinding.ResponseBinding}' not supported.");
            }
        }
Beispiel #7
0
        public async Task <IActionResult> LoginResponseAsync(List <Claim> claims)
        {
            logger.ScopeTrace(() => "Up, Login response.");

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <LoginUpSequenceData>();

            logger.SetScopeProperty(Constants.Logs.UpPartyId, sequenceData.UpPartyId);

            logger.ScopeTrace(() => $"Response, Down type {sequenceData.DownPartyLink.Type}.");
            switch (sequenceData.DownPartyLink.Type)
            {
            case PartyTypes.OAuth2:
                throw new NotImplementedException();

            case PartyTypes.Oidc:
                return(await serviceProvider.GetService <OidcAuthDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationResponseAsync(sequenceData.DownPartyLink.Id, claims));

            case PartyTypes.Saml2:
                claims.AddClaim(Constants.JwtClaimTypes.SubFormat, NameIdentifierFormats.Persistent.OriginalString);
                return(await serviceProvider.GetService <SamlAuthnDownLogic>().AuthnResponseAsync(sequenceData.DownPartyLink.Id, jwtClaims: claims));

            default:
                throw new NotSupportedException();
            }
        }
Beispiel #8
0
        public async Task <IActionResult> AuthenticationResponseAsync(string partyId, List <Claim> claims)
        {
            logger.ScopeTrace(() => "Down, OIDC Authentication response.");
            logger.SetScopeProperty(Constants.Logs.DownPartyId, partyId);
            var party = await tenantRepository.GetAsync <TParty>(partyId);

            if (party.Client == null)
            {
                throw new NotSupportedException("Party Client not configured.");
            }

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcDownSequenceData>(false);

            logger.ScopeTrace(() => $"Down, OIDC received JWT claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim);
            claims = await claimTransformLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims);

            logger.ScopeTrace(() => $"Down, OIDC output JWT claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim);

            var nameValueCollection = await CreateAuthenticationAndSessionResponse(party, claims, sequenceData);

            var responseMode = GetResponseMode(sequenceData.ResponseMode, sequenceData.ResponseType);
            await sequenceLogic.RemoveSequenceDataAsync <OidcDownSequenceData>();

            if (party.RestrictFormAction)
            {
                securityHeaderLogic.AddFormAction(sequenceData.RedirectUri);
            }
            else
            {
                securityHeaderLogic.AddFormActionAllowAll();
            }
            switch (responseMode)
            {
            case IdentityConstants.ResponseModes.FormPost:
                return(await nameValueCollection.ToHtmlPostContentResultAsync(sequenceData.RedirectUri));

            case IdentityConstants.ResponseModes.Query:
                return(await nameValueCollection.ToRedirectResultAsync(sequenceData.RedirectUri));

            case IdentityConstants.ResponseModes.Fragment:
                return(await nameValueCollection.ToFragmentResultAsync(sequenceData.RedirectUri));

            default:
                throw new NotSupportedException();
            }
        }
Beispiel #9
0
        public async Task <IActionResult> LoginResponseAsync(LoginUpParty party, User user, long authTime, IEnumerable <string> authMethods, string sessionId)
        {
            logger.ScopeTrace("Up, Login response.");

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <LoginUpSequenceData>();

            logger.SetScopeProperty("upPartyId", sequenceData.UpPartyId);

            var claims = new List <Claim>();

            claims.AddClaim(JwtClaimTypes.Subject, user.UserId);
            claims.AddClaim(JwtClaimTypes.AuthTime, authTime.ToString());
            claims.AddRange(authMethods.Select(am => new Claim(JwtClaimTypes.Amr, am)));
            claims.AddClaim(JwtClaimTypes.SessionId, sessionId);
            claims.AddClaim(JwtClaimTypes.PreferredUsername, user.Email);
            claims.AddClaim(JwtClaimTypes.Email, user.Email);
            if (user.Claims?.Count() > 0)
            {
                claims.AddRange(user.Claims.ToClaimList());
            }

            claims = await claimTransformationsLogic.Transform(party.ClaimTransformations?.ConvertAll(t => (ClaimTransformation)t), claims);

            logger.ScopeTrace($"Response, Down type {sequenceData.DownPartyType}.");
            switch (sequenceData.DownPartyType)
            {
            case PartyTypes.OAuth2:
                throw new NotImplementedException();

            case PartyTypes.Oidc:
                return(await serviceProvider.GetService <OidcAuthDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationResponseAsync(sequenceData.DownPartyId, claims));

            case PartyTypes.Saml2:
                var claimsLogic = serviceProvider.GetService <ClaimsLogic <OAuthDownClient, OAuthDownScope, OAuthDownClaim> >();
                var samlClaims  = await claimsLogic.FromJwtToSamlClaims(claims);

                samlClaims.AddClaim(Saml2ClaimTypes.NameIdFormat, NameIdentifierFormats.Email.OriginalString);
                return(await serviceProvider.GetService <SamlAuthnDownLogic>().AuthnResponseAsync(sequenceData.DownPartyId, claims: samlClaims));

            default:
                throw new NotSupportedException();
            }
        }
Beispiel #10
0
        public async Task <IActionResult> LogoutResponseAsync(string partyId, Saml2StatusCodes status = Saml2StatusCodes.Success, string sessionIndex = null)
        {
            logger.SetScopeProperty("downPartyId", partyId);

            var party = await tenantRepository.GetAsync <SamlDownParty>(partyId);

            ValidatePartyLogoutSupport(party);

            var samlConfig   = saml2ConfigurationLogic.GetSamlDownConfig(party, true);
            var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlDownSequenceData>(false);

            return(await LogoutResponseAsync(party, samlConfig, sequenceData.Id, sequenceData.RelayState, status, sessionIndex));
        }
Beispiel #11
0
        public async Task <IActionResult> AuthnResponseAsync(string partyId, Saml2StatusCodes status = Saml2StatusCodes.Success, IEnumerable <Claim> jwtClaims = null)
        {
            logger.ScopeTrace($"Down, SAML Authn response{(status != Saml2StatusCodes.Success ? " error" : string.Empty )}, Status code '{status}'.");
            logger.SetScopeProperty("downPartyId", partyId);

            var party = await tenantRepository.GetAsync <SamlDownParty>(partyId);

            var samlConfig   = saml2ConfigurationLogic.GetSamlDownConfig(party, true);
            var sequenceData = await sequenceLogic.GetSequenceDataAsync <SamlDownSequenceData>(false);

            var claims = jwtClaims != null ? await claimsDownLogic.FromJwtToSamlClaimsAsync(jwtClaims) : null;

            return(await AuthnResponseAsync(party, samlConfig, sequenceData.Id, sequenceData.RelayState, sequenceData.AcsResponseUrl, status, claims));
        }
Beispiel #12
0
        public async Task <IActionResult> LogoutRequestAsync(string partyId)
        {
            logger.ScopeTrace(() => "Up, SAML Logout request.");
            logger.SetScopeProperty(Constants.Logs.UpPartyId, partyId);
            var samlUpSequenceData = await sequenceLogic.GetSequenceDataAsync <SamlUpSequenceData>(remove : false);

            if (!samlUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal))
            {
                throw new Exception("Invalid up-party id.");
            }

            if (samlUpSequenceData.RequireLogoutConsent)
            {
                throw new NotSupportedException("Require SAML up logout consent not supported.");
            }
            if (!samlUpSequenceData.PostLogoutRedirect)
            {
                throw new NotSupportedException("SAML up post logout redirect required.");
            }

            var party = await tenantRepository.GetAsync <SamlUpParty>(partyId);

            ValidatePartyLogoutSupport(party);

            switch (party.LogoutBinding.RequestBinding)
            {
            case SamlBindingTypes.Redirect:
                return(await LogoutRequestAsync(party, new Saml2RedirectBinding(), samlUpSequenceData));

            case SamlBindingTypes.Post:
                return(await LogoutRequestAsync(party, new Saml2PostBinding(), samlUpSequenceData));

            default:
                throw new NotSupportedException($"SAML binding '{party.LogoutBinding.RequestBinding}' not supported.");
            }
        }
        public async Task <IActionResult> HandleSingleLogoutAsync(SingleLogoutSequenceData sequenceData = null)
        {
            sequenceData = sequenceData ?? await sequenceLogic.GetSequenceDataAsync <SingleLogoutSequenceData>(remove : false);

            if (sequenceData.HostedInIframe && sequenceData.AllowIframeOnDomains?.Count() > 0)
            {
                securityHeaderLogic.AddAllowIframeOnDomains(sequenceData.AllowIframeOnDomains);
            }

            var samlDownPartyId = sequenceData.DownPartyLinks.Where(p => p.Type == PartyTypes.Saml2).Select(p => p.Id).FirstOrDefault();

            var oidcDownPartyIds = sequenceData.DownPartyLinks.Where(p => p.Type == PartyTypes.Oidc).Select(p => p.Id);

            if (oidcDownPartyIds.Count() > 0)
            {
                sequenceData.DownPartyLinks = sequenceData.DownPartyLinks.Where(p => p.Type != PartyTypes.Oidc);
                await sequenceLogic.SaveSequenceDataAsync(sequenceData);

                var doSamlLogoutInIframe = sequenceData.HostedInIframe && samlDownPartyId != null;
                return(await serviceProvider.GetService <OidcFrontChannelLogoutDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().LogoutRequestAsync(oidcDownPartyIds, sequenceData, sequenceData.HostedInIframe, doSamlLogoutInIframe));
            }

            if (samlDownPartyId != null)
            {
                sequenceData.DownPartyLinks = sequenceData.DownPartyLinks.Where(p => p.Id != samlDownPartyId);
                await sequenceLogic.SaveSequenceDataAsync(sequenceData);

                return(await serviceProvider.GetService <SamlLogoutDownLogic>().SingleLogoutRequestAsync(samlDownPartyId, sequenceData));
            }

            await sequenceLogic.RemoveSequenceDataAsync <SingleLogoutSequenceData>();

            logger.ScopeTrace(() => "Successful Single Logout.", triggerEvent: true);

            if (sequenceData.HostedInIframe)
            {
                return(new OkResult());
            }
            else
            {
                return(await ResponseUpPartyAsync(sequenceData.UpPartyName, sequenceData.UpPartyType));
            }
        }
Beispiel #14
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 #15
0
        public async Task <IActionResult> LogoutResponseAsync(string sessionId)
        {
            logger.ScopeTrace("Down, Logout response.");

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <LoginUpSequenceData>();

            logger.SetScopeProperty("upPartyId", sequenceData.UpPartyId);

            logger.ScopeTrace($"Response, Down type {sequenceData.DownPartyType}.");
            switch (sequenceData.DownPartyType)
            {
            case PartyTypes.OAuth2:
                throw new NotImplementedException();

            case PartyTypes.Oidc:
                return(await serviceProvider.GetService <OidcEndSessionDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().EndSessionResponseAsync(sequenceData.DownPartyId));

            case PartyTypes.Saml2:
                return(await serviceProvider.GetService <SamlLogoutDownLogic>().LogoutResponseAsync(sequenceData.DownPartyId, sessionIndex: sessionId));

            default:
                throw new NotSupportedException();
            }
        }
Beispiel #16
0
        public async Task <IActionResult> EndSessionResponseAsync(string partyId)
        {
            logger.ScopeTrace("Down, End session response.");
            logger.SetScopeProperty("downPartyId", partyId);

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcDownSequenceData>(false);

            var endSessionResponse = new EndSessionResponse
            {
                State = sequenceData.State,
            };

            logger.ScopeTrace($"End session response '{endSessionResponse.ToJsonIndented()}'.");
            var nameValueCollection = endSessionResponse.ToDictionary();

            logger.ScopeTrace($"Redirect Uri '{sequenceData.RedirectUri}'.");
            logger.ScopeTrace("Down, OIDC End session response.", triggerEvent: true);

            await sequenceLogic.RemoveSequenceDataAsync <OidcDownSequenceData>();

            await formActionLogic.RemoveFormActionSequenceDataAsync();

            return(await nameValueCollection.ToRedirectResultAsync(sequenceData.RedirectUri));
        }
Beispiel #17
0
        public async Task <IActionResult> AuthenticationRequestAsync(string partyId)
        {
            logger.ScopeTrace(() => "Up, OIDC Authentication request.");
            var oidcUpSequenceData = await sequenceLogic.GetSequenceDataAsync <OidcUpSequenceData>(remove : false);

            if (!oidcUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal))
            {
                throw new Exception("Invalid up-party id.");
            }
            logger.SetScopeProperty(Constants.Logs.UpPartyId, oidcUpSequenceData.UpPartyId);

            var party = await tenantRepository.GetAsync <TParty>(oidcUpSequenceData.UpPartyId);

            logger.SetScopeProperty(Constants.Logs.UpPartyClientId, party.Client.ClientId);

            await oidcDiscoveryReadUpLogic.CheckOidcDiscoveryAndUpdatePartyAsync(party);

            var nonce            = RandomGenerator.GenerateNonce();
            var loginCallBackUrl = HttpContext.GetUpPartyUrl(party.Name, Constants.Routes.OAuthController, Constants.Endpoints.AuthorizationResponse, partyBindingPattern: party.PartyBindingPattern);

            oidcUpSequenceData.ClientId    = !party.Client.SpClientId.IsNullOrWhiteSpace() ? party.Client.SpClientId : party.Client.ClientId;
            oidcUpSequenceData.RedirectUri = loginCallBackUrl;
            oidcUpSequenceData.Nonce       = nonce;
            if (party.Client.EnablePkce)
            {
                var codeVerifier = RandomGenerator.Generate(64);
                oidcUpSequenceData.CodeVerifier = codeVerifier;
            }
            await sequenceLogic.SaveSequenceDataAsync(oidcUpSequenceData);

            var authenticationRequest = new AuthenticationRequest
            {
                ClientId     = oidcUpSequenceData.ClientId,
                ResponseMode = party.Client.ResponseMode,
                ResponseType = party.Client.ResponseType,
                RedirectUri  = loginCallBackUrl,
                Nonce        = nonce,
                State        = await sequenceLogic.CreateExternalSequenceIdAsync()
            };

            switch (oidcUpSequenceData.LoginAction)
            {
            case LoginAction.ReadSession:
                authenticationRequest.Prompt = IdentityConstants.AuthorizationServerPrompt.None;
                break;

            case LoginAction.RequireLogin:
                authenticationRequest.Prompt = IdentityConstants.AuthorizationServerPrompt.Login;
                break;

            default:
                break;
            }

            if (oidcUpSequenceData.MaxAge.HasValue)
            {
                authenticationRequest.MaxAge = oidcUpSequenceData.MaxAge;
            }

            if (!oidcUpSequenceData.UserId.IsNullOrEmpty())
            {
                authenticationRequest.LoginHint = oidcUpSequenceData.UserId;
            }

            authenticationRequest.Scope = new[] { IdentityConstants.DefaultOidcScopes.OpenId }.ConcatOnce(party.Client.Scopes).ToSpaceList();

            //TODO add AcrValues
            //authenticationRequest.AcrValues = "urn:federation:authentication:windows";

            logger.ScopeTrace(() => $"Up, Authentication request '{authenticationRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message);
            var nameValueCollection = authenticationRequest.ToDictionary();

            if (party.Client.EnablePkce)
            {
                var codeChallengeRequest = new CodeChallengeSecret
                {
                    CodeChallenge       = await oidcUpSequenceData.CodeVerifier.Sha256HashBase64urlEncodedAsync(),
                    CodeChallengeMethod = IdentityConstants.CodeChallengeMethods.S256,
                };

                logger.ScopeTrace(() => $"Up, CodeChallengeSecret request '{codeChallengeRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message);
                nameValueCollection = nameValueCollection.AddToDictionary(codeChallengeRequest);
            }

            securityHeaderLogic.AddFormActionAllowAll();

            logger.ScopeTrace(() => $"Up, Authentication request URL '{party.Client.AuthorizeUrl}'.");
            logger.ScopeTrace(() => "Up, Sending OIDC Authentication request.", triggerEvent: true);
            return(await nameValueCollection.ToRedirectResultAsync(party.Client.AuthorizeUrl));
        }
Beispiel #18
0
        public async Task <IActionResult> AuthenticationResponseAsync(string partyId, List <Claim> claims)
        {
            logger.ScopeTrace("Down, OIDC Authentication response.");
            logger.SetScopeProperty("downPartyId", partyId);
            var party = await tenantRepository.GetAsync <TParty>(partyId);

            if (party.Client == null)
            {
                throw new NotSupportedException($"Party Client not configured.");
            }

            var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcDownSequenceData>(false);

            claims = await claimTransformationsLogic.Transform(party.ClaimTransformations?.ConvertAll(t => (ClaimTransformation)t), claims);

            var authenticationResponse = new AuthenticationResponse
            {
                TokenType = IdentityConstants.TokenTypes.Bearer,
                State     = sequenceData.State,
                ExpiresIn = party.Client.AccessTokenLifetime,
            };
            var sessionResponse = new SessionResponse
            {
                SessionState = claims.FindFirstValue(c => c.Type == JwtClaimTypes.SessionId)
            };

            logger.ScopeTrace($"Response type '{sequenceData.ResponseType}'.");
            var responseTypes = sequenceData.ResponseType.ToSpaceList();

            if (responseTypes.Contains(IdentityConstants.ResponseTypes.Code))
            {
                authenticationResponse.Code = await oauthAuthCodeGrantLogic.CreateAuthCodeGrantAsync(party.Client as TClient, claims, sequenceData.RedirectUri, sequenceData.Scope, sequenceData.Nonce, sequenceData.CodeChallenge, sequenceData.CodeChallengeMethod);
            }

            string algorithm = IdentityConstants.Algorithms.Asymmetric.RS256;

            if (responseTypes.Contains(IdentityConstants.ResponseTypes.Token))
            {
                authenticationResponse.AccessToken = await jwtLogic.CreateAccessTokenAsync(party.Client as TClient, claims, sequenceData.Scope?.ToSpaceList(), algorithm);
            }
            if (responseTypes.Contains(IdentityConstants.ResponseTypes.IdToken))
            {
                authenticationResponse.IdToken = await jwtLogic.CreateIdTokenAsync(party.Client as TClient, claims, sequenceData.Scope?.ToSpaceList(), sequenceData.Nonce, responseTypes, authenticationResponse.Code, authenticationResponse.AccessToken, algorithm);
            }

            logger.ScopeTrace($"Authentication response '{authenticationResponse.ToJsonIndented()}'.");
            var nameValueCollection = authenticationResponse.ToDictionary();

            if (!sessionResponse.SessionState.IsNullOrWhiteSpace())
            {
                logger.ScopeTrace($"Session response '{sessionResponse.ToJsonIndented()}'.");
                nameValueCollection = nameValueCollection.AddToDictionary(sessionResponse);
            }

            logger.ScopeTrace($"Redirect Uri '{sequenceData.RedirectUri}'.");
            logger.ScopeTrace("Down, OIDC Authentication response.", triggerEvent: true);

            var responseMode = GetResponseMode(sequenceData.ResponseMode, sequenceData.ResponseType);
            await sequenceLogic.RemoveSequenceDataAsync <OidcDownSequenceData>();

            await formActionLogic.RemoveFormActionSequenceDataAsync();

            switch (responseMode)
            {
            case IdentityConstants.ResponseModes.FormPost:
                return(await nameValueCollection.ToHtmlPostContentResultAsync(sequenceData.RedirectUri));

            case IdentityConstants.ResponseModes.Query:
                return(await nameValueCollection.ToRedirectResultAsync(sequenceData.RedirectUri));

            case IdentityConstants.ResponseModes.Fragment:
                return(await nameValueCollection.ToFragmentResultAsync(sequenceData.RedirectUri));

            default:
                throw new NotSupportedException();
            }
        }
Beispiel #19
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));
            }
        }