Пример #1
0
        private async Task <List <Claim> > ValidateAccessTokenAsync(TParty party, OidcUpSequenceData sequenceData, string accessToken)
        {
            try
            {
                var jwtToken = JwtHandler.ReadToken(accessToken);
                var issuer   = party.Issuers.Where(i => i == jwtToken.Issuer).FirstOrDefault();
                if (string.IsNullOrEmpty(issuer))
                {
                    throw new OAuthRequestException($"{party.Name}|Access token issuer '{jwtToken.Issuer}' is unknown.")
                          {
                              RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidToken
                          };
                }

                var claimsPrincipal = await jwtUpLogic.ValidateAccessTokenAsync(accessToken, issuer, party, sequenceData.ClientId);

                return(claimsPrincipal.Claims.ToList());
            }
            catch (OAuthRequestException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new OAuthRequestException($"{party.Name}|Access token not valid.", ex)
                      {
                          RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidToken
                      };
            }
        }
Пример #2
0
        private async Task <IActionResult> AuthenticationResponseDownAsync(OidcUpSequenceData sequenceData, List <Claim> claims = null, string error = null, string errorDescription = null)
        {
            try
            {
                logger.ScopeTrace(() => $"Response, Down type {sequenceData.DownPartyLink.Type}.");
                switch (sequenceData.DownPartyLink.Type)
                {
                case PartyTypes.OAuth2:
                    throw new NotImplementedException();

                case PartyTypes.Oidc:
                    if (error.IsNullOrEmpty())
                    {
                        return(await serviceProvider.GetService <OidcAuthDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationResponseAsync(sequenceData.DownPartyLink.Id, claims));
                    }
                    else
                    {
                        return(await serviceProvider.GetService <OidcAuthDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().AuthenticationResponseErrorAsync(sequenceData.DownPartyLink.Id, error, errorDescription));
                    }

                case PartyTypes.Saml2:
                    var claimsLogic = serviceProvider.GetService <ClaimsDownLogic <OidcDownClient, OidcDownScope, OidcDownClaim> >();
                    return(await serviceProvider.GetService <SamlAuthnDownLogic>().AuthnResponseAsync(sequenceData.DownPartyLink.Id, ErrorToSamlStatus(error), claims != null ? await claimsLogic.FromJwtToSamlClaimsAsync(claims) : null));

                default:
                    throw new NotSupportedException();
                }
            }
            catch (Exception ex)
            {
                throw new StopSequenceException("Falling authentication response down", ex);
            }
        }
        private async Task <IActionResult> LogoutResponseDownAsync(OidcUpSequenceData sequenceData)
        {
            try
            {
                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 <OidcRpInitiatedLogoutDownLogic <OidcDownParty, OidcDownClient, OidcDownScope, OidcDownClaim> >().EndSessionResponseAsync(sequenceData.DownPartyLink.Id));

                case PartyTypes.Saml2:
                    return(await serviceProvider.GetService <SamlLogoutDownLogic>().LogoutResponseAsync(sequenceData.DownPartyLink.Id, sessionIndex: sequenceData.SessionId));

                default:
                    throw new NotSupportedException();
                }
            }
            catch (Exception ex)
            {
                throw new StopSequenceException("Falling logout response down", ex);
            }
        }
Пример #4
0
        private async Task <List <Claim> > ValidateIdTokenAsync(TParty party, OidcUpSequenceData sequenceData, string idToken, string accessToken, bool authorizationEndpoint)
        {
            try
            {
                var jwtToken = JwtHandler.ReadToken(idToken);
                var issuer   = party.Issuers.Where(i => i == jwtToken.Issuer).FirstOrDefault();
                if (string.IsNullOrEmpty(issuer))
                {
                    throw new OAuthRequestException($"{party.Name}|Id token issuer '{jwtToken.Issuer}' is unknown.")
                          {
                              RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidToken
                          };
                }

                var claimsPrincipal = await jwtUpLogic.ValidateIdTokenAsync(idToken, issuer, party, sequenceData.ClientId);

                var nonce = claimsPrincipal.Claims.FindFirstValue(c => c.Type == JwtClaimTypes.Nonce);
                if (!sequenceData.Nonce.Equals(nonce, StringComparison.Ordinal))
                {
                    throw new OAuthRequestException($"{party.Name}|Id token nonce do not match.")
                          {
                              RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidToken
                          };
                }

                if (authorizationEndpoint && !accessToken.IsNullOrEmpty())
                {
                    var    atHash    = claimsPrincipal.Claims.FindFirstValue(c => c.Type == JwtClaimTypes.AtHash);
                    string algorithm = IdentityConstants.Algorithms.Asymmetric.RS256;
                    if (atHash != await accessToken.LeftMostBase64urlEncodedHashAsync(algorithm))
                    {
                        throw new OAuthRequestException($"{party.Name}|Access Token hash claim in ID token do not match the access token.")
                              {
                                  RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidToken
                              };
                    }
                }

                return(claimsPrincipal.Claims.ToList());
            }
            catch (OAuthRequestException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new OAuthRequestException($"{party.Name}|Id token not valid.", ex)
                      {
                          RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidToken
                      };
            }
        }
Пример #5
0
        public async Task <IActionResult> AuthenticationRequestRedirectAsync(UpPartyLink partyLink, LoginRequest loginRequest)
        {
            logger.ScopeTrace("Up, OIDC Authentication request redirect.");
            var partyId = await UpParty.IdFormatAsync(RouteBinding, partyLink.Name);

            logger.SetScopeProperty("upPartyId", partyId);

            await loginRequest.ValidateObjectAsync();

            var oidcUpSequenceData = new OidcUpSequenceData
            {
                DownPartyLink = loginRequest.DownPartyLink,
                UpPartyId     = partyId,
                LoginAction   = loginRequest.LoginAction,
                UserId        = loginRequest.UserId,
                MaxAge        = loginRequest.MaxAge
            };
            await sequenceLogic.SaveSequenceDataAsync(oidcUpSequenceData);

            return(HttpContext.GetUpPartyUrl(partyLink.Name, Constants.Routes.OAuthUpJumpController, Constants.Endpoints.UpJump.AuthenticationRequest, includeSequence: true).ToRedirectResult());
        }
Пример #6
0
        private async Task <(List <Claim>, string)> ValidateTokensAsync(TParty party, OidcUpSequenceData sequenceData, string idToken, string accessToken, bool authorizationEndpoint)
        {
            var claims = await ValidateIdTokenAsync(party, sequenceData, idToken, accessToken, authorizationEndpoint);

            if (!accessToken.IsNullOrWhiteSpace())
            {
                if (!party.Client.UseIdTokenClaims)
                {
                    var sessionIdClaim = claims.Where(c => c.Type == JwtClaimTypes.SessionId).FirstOrDefault();
                    claims = await ValidateAccessTokenAsync(party, sequenceData, accessToken);

                    if (sessionIdClaim != null && !claims.Where(c => c.Type == JwtClaimTypes.SessionId).Any())
                    {
                        claims.Add(sessionIdClaim);
                    }
                }
                claims.AddClaim(Constants.JwtClaimTypes.AccessToken, $"{party.Name}|{accessToken}");
            }

            var subject = claims.FindFirstValue(c => c.Type == JwtClaimTypes.Subject);

            if (!subject.IsNullOrEmpty())
            {
                claims = claims.Where(c => c.Type != JwtClaimTypes.Subject).ToList();
                claims.Add(new Claim(JwtClaimTypes.Subject, $"{party.Name}|{subject}"));
            }

            return(claims, idToken);
        }
Пример #7
0
        private async Task <TokenResponse> TokenRequestAsync(TClient client, string code, OidcUpSequenceData sequenceData)
        {
            var tokenRequest = new TokenRequest
            {
                GrantType   = IdentityConstants.GrantTypes.AuthorizationCode,
                Code        = code,
                ClientId    = !client.SpClientId.IsNullOrWhiteSpace() ? client.SpClientId : client.ClientId,
                RedirectUri = sequenceData.RedirectUri,
            };

            logger.ScopeTrace(() => $"Up, Token request '{tokenRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message);
            var requestDictionary = tokenRequest.ToDictionary();

            if (!client.ClientSecret.IsNullOrEmpty())
            {
                var clientCredentials = new ClientCredentials
                {
                    ClientSecret = client.ClientSecret,
                };
                logger.ScopeTrace(() => $"Up, Client credentials '{new ClientCredentials { ClientSecret = $"{(clientCredentials.ClientSecret?.Length > 10 ? clientCredentials.ClientSecret.Substring(0, 3) : string.Empty)}..." }.ToJsonIndented()}'.", traceType: TraceTypes.Message);
                requestDictionary = requestDictionary.AddToDictionary(clientCredentials);
            }

            if (client.EnablePkce)
            {
                var codeVerifierSecret = new CodeVerifierSecret
                {
                    CodeVerifier = sequenceData.CodeVerifier,
                };
                logger.ScopeTrace(() => $"Up, Code verifier secret '{new CodeVerifierSecret { CodeVerifier = $"{(codeVerifierSecret.CodeVerifier?.Length > 10 ? codeVerifierSecret.CodeVerifier.Substring(0, 3) : string.Empty)}..." }.ToJsonIndented()}'.", traceType: TraceTypes.Message);
                requestDictionary = requestDictionary.AddToDictionary(codeVerifierSecret);
            }

            logger.ScopeTrace(() => $"Up, OIDC Token request URL '{client.TokenUrl}'.", traceType: TraceTypes.Message);
            var request = new HttpRequestMessage(HttpMethod.Post, client.TokenUrl);

            request.Content = new FormUrlEncodedContent(requestDictionary);

            using var response = await httpClientFactory.CreateClient(nameof (HttpClient)).SendAsync(request);

            switch (response.StatusCode)
            {
            case HttpStatusCode.OK:
                var result = await response.Content.ReadAsStringAsync();

                var tokenResponse = result.ToObject <TokenResponse>();
                logger.ScopeTrace(() => $"Up, Token response '{tokenResponse.ToJsonIndented()}'.", traceType: TraceTypes.Message);
                tokenResponse.Validate(true);
                if (tokenResponse.AccessToken.IsNullOrEmpty())
                {
                    throw new ArgumentNullException(nameof(tokenResponse.AccessToken), tokenResponse.GetTypeName());
                }
                if (tokenResponse.ExpiresIn <= 0)
                {
                    throw new ArgumentNullException(nameof(tokenResponse.ExpiresIn), tokenResponse.GetTypeName());
                }
                return(tokenResponse);

            case HttpStatusCode.BadRequest:
                var resultBadRequest = await response.Content.ReadAsStringAsync();

                var tokenResponseBadRequest = resultBadRequest.ToObject <TokenResponse>();
                logger.ScopeTrace(() => $"Up, Bad token response '{tokenResponseBadRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message);
                try
                {
                    tokenResponseBadRequest.Validate(true);
                }
                catch (ResponseErrorException rex)
                {
                    throw new OAuthRequestException($"External {rex.Message}")
                          {
                              RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidRequest
                          };
                }
                throw new EndpointException($"Bad request. Status code '{response.StatusCode}'. Response '{resultBadRequest}'.")
                      {
                          RouteBinding = RouteBinding
                      };

            default:
                try
                {
                    var resultUnexpectedStatus = await response.Content.ReadAsStringAsync();

                    var tokenResponseUnexpectedStatus = resultUnexpectedStatus.ToObject <TokenResponse>();
                    logger.ScopeTrace(() => $"Up, Unexpected status code response '{tokenResponseUnexpectedStatus.ToJsonIndented()}'.", traceType: TraceTypes.Message);
                    try
                    {
                        tokenResponseUnexpectedStatus.Validate(true);
                    }
                    catch (ResponseErrorException rex)
                    {
                        throw new OAuthRequestException($"External {rex.Message}")
                              {
                                  RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidRequest
                              };
                    }
                }
                catch (OAuthRequestException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    throw new EndpointException($"Unexpected status code. Status code={response.StatusCode}", ex)
                          {
                              RouteBinding = RouteBinding
                          };
                }
                throw new EndpointException($"Unexpected status code. Status code={response.StatusCode}")
                      {
                          RouteBinding = RouteBinding
                      };
            }
        }
Пример #8
0
        private async Task <(List <Claim>, string)> HandleAuthorizationCodeResponseAsync(TParty party, OidcUpSequenceData sequenceData, string code)
        {
            var tokenResponse = await TokenRequestAsync(party.Client, code, sequenceData);

            return(await ValidateTokensAsync(party, sequenceData, tokenResponse.IdToken, tokenResponse.AccessToken, false));
        }
Пример #9
0
        private async Task <(List <Claim>, string)> ValidateTokensAsync(OidcUpParty party, OidcUpSequenceData sequenceData, string idToken, string accessToken, bool authorizationEndpoint)
        {
            var claims = await ValidateIdTokenAsync(party, sequenceData, idToken, accessToken, authorizationEndpoint);

            if (!accessToken.IsNullOrWhiteSpace())
            {
                if (!party.Client.UseIdTokenClaims)
                {
                    // If access token exists, use access token claims instead of ID token claims.
                    claims = ValidateAccessToken(party, sequenceData, accessToken);
                }
                claims.AddClaim(Constants.JwtClaimTypes.AccessToken, $"{party.Name}|{accessToken}");
            }

            var subject = claims.FindFirstValue(c => c.Type == JwtClaimTypes.Subject);

            if (!subject.IsNullOrEmpty())
            {
                claims = claims.Where(c => c.Type != JwtClaimTypes.Subject).ToList();
                claims.Add(new Claim(JwtClaimTypes.Subject, $"{party.Name}|{subject}"));
            }

            return(await Task.FromResult((claims, idToken)));
        }