/// <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);
        }
Example #2
0
        /// <summary>
        /// Processes the authorize response.
        /// </summary>
        /// <param name="data">The response data.</param>
        /// <param name="state">The state.</param>
        /// <returns>Result of the login response validation</returns>
        public async Task <LoginResult> ProcessResponseAsync(string data, AuthorizeState state)
        {
            _logger.LogTrace("ProcessResponseAsync");
            _logger.LogInformation("Processing response.");

            await EnsureConfigurationAsync();

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

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

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

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

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

            if (_options.LoadProfile)
            {
                var userInfoResult = await GetUserInfoAsync(result.TokenResponse.AccessToken);

                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 (!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 = DateTime.Now.AddSeconds(result.TokenResponse.ExpiresIn),
                IdentityToken         = result.TokenResponse.IdentityToken,
                AuthenticationTime    = DateTime.Now
            };

            if (!string.IsNullOrWhiteSpace(loginResult.RefreshToken))
            {
                loginResult.RefreshTokenHandler = new RefreshTokenHandler(
                    TokenClientFactory.Create(_options),
                    loginResult.RefreshToken,
                    loginResult.AccessToken);
            }

            return(loginResult);
        }