Exemple #1
0
        internal async Task <TokenResponseValidationResult> ValidateTokenResponseAsync(TokenResponse response, AuthorizeState state, bool requireIdentityToken, CancellationToken cancellationToken = default)
        {
            _logger.LogTrace("ValidateTokenResponse");

            // token response must contain an access token
            if (response.AccessToken.IsMissing())
            {
                return(new TokenResponseValidationResult("Access token is missing on token response."));
            }

            if (requireIdentityToken)
            {
                // token response must contain an identity token (openid scope is mandatory)
                if (response.IdentityToken.IsMissing())
                {
                    return(new TokenResponseValidationResult("Identity token is missing on token response."));
                }
            }

            if (response.IdentityToken.IsPresent())
            {
                // if identity token is present, it must be valid
                var validationResult = await _tokenValidator.ValidateAsync(response.IdentityToken, cancellationToken);

                if (validationResult.IsError)
                {
                    return(new TokenResponseValidationResult(validationResult.Error ?? "Identity token validation error"));
                }

                // validate nonce
                if (state != null)
                {
                    if (!ValidateNonce(state.Nonce, validationResult.User))
                    {
                        return(new TokenResponseValidationResult("Invalid nonce."));
                    }
                }

                // validate at_hash
                var atHash = validationResult.User.FindFirst(JwtClaimTypes.AccessTokenHash);
                if (atHash == null)
                {
                    if (_options.Policy.RequireAccessTokenHash)
                    {
                        return(new TokenResponseValidationResult("at_hash is missing."));
                    }
                }
                else
                {
                    if (!_crypto.ValidateHash(response.AccessToken, atHash.Value, validationResult.SignatureAlgorithm))
                    {
                        return(new TokenResponseValidationResult("Invalid access token hash."));
                    }
                }

                return(new TokenResponseValidationResult(validationResult));
            }

            return(new TokenResponseValidationResult((IdentityTokenValidationResult)null));
        }
        private async Task <ResponseValidationResult> ProcessHybridFlowResponseAsync(AuthorizeResponse authorizeResponse, AuthorizeState state, object extraParameters = null)
        {
            _logger.LogTrace("ProcessHybridFlowResponseAsync");

            //////////////////////////////////////////////////////
            // validate front-channel response
            //////////////////////////////////////////////////////

            // id_token must be present
            if (authorizeResponse.IdentityToken.IsMissing())
            {
                return(new ResponseValidationResult("Missing identity token."));
            }

            // id_token must be valid
            var frontChannelValidationResult = await _tokenValidator.ValidateAsync(authorizeResponse.IdentityToken);

            if (frontChannelValidationResult.IsError)
            {
                return(new ResponseValidationResult(frontChannelValidationResult.Error ?? "Identity token validation error."));
            }

            // nonce must be valid
            if (!ValidateNonce(state.Nonce, frontChannelValidationResult.User))
            {
                return(new ResponseValidationResult("Invalid nonce."));
            }

            // validate c_hash
            var cHash = frontChannelValidationResult.User.FindFirst(JwtClaimTypes.AuthorizationCodeHash);

            if (cHash == null)
            {
                if (_options.Policy.RequireAuthorizationCodeHash)
                {
                    return(new ResponseValidationResult("c_hash is missing."));
                }
            }
            else
            {
                if (!_crypto.ValidateHash(authorizeResponse.Code, cHash.Value, frontChannelValidationResult.SignatureAlgorithm))
                {
                    return(new ResponseValidationResult("Invalid c_hash."));
                }
            }

            //////////////////////////////////////////////////////
            // process back-channel response
            //////////////////////////////////////////////////////

            // redeem code for tokens
            var tokenResponse = await RedeemCodeAsync(authorizeResponse.Code, state, extraParameters);

            if (tokenResponse.IsError)
            {
                return(new ResponseValidationResult(tokenResponse.Error));
            }

            // validate token response
            var tokenResponseValidationResult = await ValidateTokenResponseAsync(tokenResponse, state);

            if (tokenResponseValidationResult.IsError)
            {
                return(new ResponseValidationResult(tokenResponseValidationResult.Error));
            }

            // compare front & back channel subs
            var frontChannelSub = frontChannelValidationResult.User.FindFirst(JwtClaimTypes.Subject).Value;
            var backChannelSub  = tokenResponseValidationResult.IdentityTokenValidationResult.User.FindFirst(JwtClaimTypes.Subject).Value;

            if (!string.Equals(frontChannelSub, backChannelSub, StringComparison.Ordinal))
            {
                return(new ResponseValidationResult($"Subject on front-channel ({frontChannelSub}) does not match subject on back-channel ({backChannelSub})."));
            }

            return(new ResponseValidationResult
            {
                AuthorizeResponse = authorizeResponse,
                TokenResponse = tokenResponse,
                User = tokenResponseValidationResult.IdentityTokenValidationResult.User
            });
        }
        internal async Task <TokenResponseValidationResult> ValidateTokenResponseAsync(TokenResponse response, AuthorizeState state, bool requireIdentityToken, CancellationToken cancellationToken = default)
        {
            _logger.LogTrace("ValidateTokenResponse");

            // token response must contain an access token
            if (response.AccessToken.IsMissing())
            {
                return(new TokenResponseValidationResult("Access token is missing on token response."));
            }

            if (requireIdentityToken)
            {
                // token response must contain an identity token (openid scope is mandatory)
                if (response.IdentityToken.IsMissing())
                {
                    return(new TokenResponseValidationResult("Identity token is missing on token response."));
                }
            }

            if (response.IdentityToken.IsPresent())
            {
                IIdentityTokenValidator validator;
                if (_options.IdentityTokenValidator == null)
                {
                    if (_options.Policy.RequireIdentityTokenSignature == false)
                    {
                        validator = new NoValidationIdentityTokenValidator();
                    }
                    else
                    {
                        throw new InvalidOperationException("No IIdentityTokenValidator is configured. Either explicitly set a validator on the options, or set OidcClientOptions.Policy.RequireIdentityTokenSignature to false to skip validation.");
                    }
                }
                else
                {
                    validator = _options.IdentityTokenValidator;
                }

                var validationResult = await validator.ValidateAsync(response.IdentityToken, _options, cancellationToken);

                if (validationResult.Error == "invalid_signature")
                {
                    await _refreshKeysAsync(cancellationToken);

                    validationResult = await _options.IdentityTokenValidator.ValidateAsync(response.IdentityToken, _options, cancellationToken);
                }

                if (validationResult.IsError)
                {
                    return(new TokenResponseValidationResult(validationResult.Error ?? "Identity token validation error"));
                }

                // validate at_hash
                if (!string.Equals(validationResult.SignatureAlgorithm, "none", StringComparison.OrdinalIgnoreCase))
                {
                    var atHash = validationResult.User.FindFirst(JwtClaimTypes.AccessTokenHash);
                    if (atHash == null)
                    {
                        if (_options.Policy.RequireAccessTokenHash)
                        {
                            return(new TokenResponseValidationResult("at_hash is missing."));
                        }
                    }
                    else
                    {
                        if (!_crypto.ValidateHash(response.AccessToken, atHash.Value, validationResult.SignatureAlgorithm))
                        {
                            return(new TokenResponseValidationResult("Invalid access token hash."));
                        }
                    }
                }

                return(new TokenResponseValidationResult(validationResult));
            }

            return(new TokenResponseValidationResult((IdentityTokenValidationResult)null));
        }