예제 #1
0
        public async Task <ResponseValidationResult> ProcessResponseAsync(
            AuthorizeResponse authorizeResponse,
            AuthorizeState state,
            BackChannelParameters backChannelParameters,
            CancellationToken cancellationToken = default)
        {
            _logger.LogTrace("ProcessResponseAsync");

            //////////////////////////////////////////////////////
            // validate common front-channel parameters
            //////////////////////////////////////////////////////

            if (string.IsNullOrEmpty(authorizeResponse.Code))
            {
                return(new ResponseValidationResult("Missing authorization code."));
            }

            if (string.IsNullOrEmpty(authorizeResponse.State))
            {
                return(new ResponseValidationResult("Missing state."));
            }

            if (!string.Equals(state.State, authorizeResponse.State, StringComparison.Ordinal))
            {
                return(new ResponseValidationResult("Invalid state."));
            }

            return(await ProcessCodeFlowResponseAsync(authorizeResponse, state, backChannelParameters, cancellationToken));
        }
        /// <summary>
        /// Refreshes an access token.
        /// </summary>
        /// <param name="refreshToken">The refresh token.</param>
        /// <param name="backChannelParameters">Back-channel parameters</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A token response.
        /// </returns>
        public virtual async Task <RefreshTokenResult> RefreshTokenAsync(string refreshToken, BackChannelParameters backChannelParameters = null, CancellationToken cancellationToken = default)
        {
            _logger.LogTrace("RefreshTokenAsync");

            await EnsureConfigurationAsync(cancellationToken);

            backChannelParameters = backChannelParameters ?? new BackChannelParameters();

            var client = Options.CreateClient();

            var response = await client.RequestRefreshTokenAsync(new RefreshTokenRequest
            {
                Address               = Options.ProviderInformation.TokenEndpoint,
                ClientId              = Options.ClientId,
                ClientSecret          = Options.ClientSecret,
                ClientAssertion       = Options.ClientAssertion,
                ClientCredentialStyle = Options.TokenClientCredentialStyle,
                RefreshToken          = refreshToken,
                Resource              = backChannelParameters.Resource,
                Parameters            = backChannelParameters.Extra
            }, cancellationToken).ConfigureAwait(false);

            if (response.IsError)
            {
                return(new RefreshTokenResult
                {
                    Error = response.Error,
                    ErrorDescription = response.ErrorDescription,
                });
            }

            // validate token response
            var validationResult = await _processor.ValidateTokenResponseAsync(response, null, requireIdentityToken : Options.Policy.RequireIdentityTokenOnRefreshTokenResponse,
                                                                               cancellationToken : cancellationToken);

            if (validationResult.IsError)
            {
                return(new RefreshTokenResult {
                    Error = validationResult.Error
                });
            }

            return(new RefreshTokenResult
            {
                IdentityToken = response.IdentityToken,
                AccessToken = response.AccessToken,
                RefreshToken = response.RefreshToken,
                ExpiresIn = (int)response.ExpiresIn,
                AccessTokenExpiration = DateTime.Now.AddSeconds(response.ExpiresIn)
            });
        }
        /// <summary>
        /// Processes the authorize response.
        /// </summary>
        /// <param name="data">The response data.</param>
        /// <param name="state">The state.</param>
        /// <param name="backChannelParameters">Parameters for back-channel call</param>
        /// <param name="cancellationToken">A token that can be used to cancel the request</param>
        /// <returns>
        /// Result of the login response validation
        /// </returns>
        public virtual async Task <LoginResult> ProcessResponseAsync(
            string data,
            AuthorizeState state,
            BackChannelParameters backChannelParameters = null,
            CancellationToken cancellationToken         = default)
        {
            _logger.LogTrace("ProcessResponseAsync");
            _logger.LogInformation("Processing response.");

            backChannelParameters = backChannelParameters ?? new BackChannelParameters();
            await EnsureConfigurationAsync(cancellationToken);

            _logger.LogDebug("Authorize response: {response}", data);
            var authorizeResponse = new AuthorizeResponse(data);

            if (authorizeResponse.IsError)
            {
                _logger.LogError(authorizeResponse.Error);
                return(new LoginResult(authorizeResponse.Error, authorizeResponse.ErrorDescription));
            }

            var result = await _processor.ProcessResponseAsync(authorizeResponse, state, backChannelParameters, cancellationToken);

            if (result.IsError)
            {
                _logger.LogError(result.Error);
                return(new LoginResult(result.Error, result.ErrorDescription));
            }

            var userInfoClaims = Enumerable.Empty <Claim>();

            if (Options.LoadProfile)
            {
                var userInfoResult = await GetUserInfoAsync(result.TokenResponse.AccessToken, cancellationToken);

                if (userInfoResult.IsError)
                {
                    var error = $"Error contacting userinfo endpoint: {userInfoResult.Error}";
                    _logger.LogError(error);

                    return(new LoginResult(error));
                }

                userInfoClaims = userInfoResult.Claims;

                var userInfoSub = userInfoClaims.FirstOrDefault(c => c.Type == JwtClaimTypes.Subject);
                if (userInfoSub == null)
                {
                    var error = "sub claim is missing from userinfo endpoint";
                    _logger.LogError(error);

                    return(new LoginResult(error));
                }

                if (result.TokenResponse.IdentityToken != null)
                {
                    if (!string.Equals(userInfoSub.Value, result.User.FindFirst(JwtClaimTypes.Subject).Value))
                    {
                        var error = "sub claim from userinfo endpoint is different than sub claim from identity token.";
                        _logger.LogError(error);

                        return(new LoginResult(error));
                    }
                }
            }

            var user = ProcessClaims(result.User, userInfoClaims);

            var loginResult = new LoginResult
            {
                User                  = user,
                AccessToken           = result.TokenResponse.AccessToken,
                RefreshToken          = result.TokenResponse.RefreshToken,
                AccessTokenExpiration = DateTimeOffset.Now.AddSeconds(result.TokenResponse.ExpiresIn),
                IdentityToken         = result.TokenResponse.IdentityToken,
                AuthenticationTime    = DateTimeOffset.Now,
                TokenResponse         = result.TokenResponse // In some cases there is additional custom response data that clients need access to
            };

            if (loginResult.RefreshToken.IsPresent())
            {
                loginResult.RefreshTokenHandler = new RefreshTokenDelegatingHandler(
                    this,
                    loginResult.AccessToken,
                    loginResult.RefreshToken,
                    Options.RefreshTokenInnerHttpHandler);
            }

            return(loginResult);
        }
예제 #4
0
        private async Task <ResponseValidationResult> ProcessCodeFlowResponseAsync(AuthorizeResponse authorizeResponse, AuthorizeState state, BackChannelParameters backChannelParameters, CancellationToken cancellationToken)
        {
            _logger.LogTrace("ProcessCodeFlowResponseAsync");

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

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

            if (tokenResponse.IsError)
            {
                return(new ResponseValidationResult($"Error redeeming code: {tokenResponse.Error ?? "no error code"} / {tokenResponse.ErrorDescription ?? "no description"}"));
            }

            // validate token response
            var tokenResponseValidationResult = await ValidateTokenResponseAsync(tokenResponse, state, requireIdentityToken : false, cancellationToken : cancellationToken);

            if (tokenResponseValidationResult.IsError)
            {
                return(new ResponseValidationResult($"Error validating token response: {tokenResponseValidationResult.Error}"));
            }

            return(new ResponseValidationResult
            {
                AuthorizeResponse = authorizeResponse,
                TokenResponse = tokenResponse,
                User = tokenResponseValidationResult?.IdentityTokenValidationResult?.User ?? Principal.Create(_options.Authority)
            });
        }
예제 #5
0
        private async Task <TokenResponse> RedeemCodeAsync(string code, AuthorizeState state, BackChannelParameters backChannelParameters, CancellationToken cancellationToken)
        {
            _logger.LogTrace("RedeemCodeAsync");

            var client      = _options.CreateClient();
            var tokenResult = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
            {
                Address = _options.ProviderInformation.TokenEndpoint,

                ClientId              = _options.ClientId,
                ClientSecret          = _options.ClientSecret,
                ClientAssertion       = _options.ClientAssertion,
                ClientCredentialStyle = _options.TokenClientCredentialStyle,

                Code         = code,
                RedirectUri  = state.RedirectUri,
                CodeVerifier = state.CodeVerifier,
                Parameters   = backChannelParameters.Extra ?? new Parameters()
            }, cancellationToken).ConfigureAwait(false);

            return(tokenResult);
        }