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); }
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) }); }
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); }