public async Task <ResponseValidationResult> ValidateCodeFlowResponseAsync(AuthorizeResponse authorizeResponse, AuthorizeState state)
        {
            Logger.Debug("Validate code flow response");
            var result = new ResponseValidationResult();

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

            // code must be present
            if (authorizeResponse.Code.IsMissing())
            {
                result.Error = "code is missing";
                Logger.Error(result.Error);

                return(result);
            }

            if (!string.Equals(authorizeResponse.State, state.State, StringComparison.Ordinal))
            {
                result.Error = "invalid state";
                Logger.Error(result.Error);

                return(result);
            }

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

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

            if (tokenResponse.IsError || tokenResponse.IsHttpError)
            {
                result.Error = tokenResponse.Error;
                return(result);
            }

            // validate token response
            var tokenResponseValidationResult = await ValidateTokenResponse(tokenResponse);

            if (!tokenResponseValidationResult.Success)
            {
                result.Error = tokenResponseValidationResult.Error;
                return(result);
            }

            return(new ResponseValidationResult
            {
                AuthorizeResponse = authorizeResponse,
                TokenResponse = tokenResponse,
                Claims = tokenResponseValidationResult.IdentityTokenValidationResult.Claims
            });
        }
        private async Task <LoginResult> ProcessClaimsAsync(ResponseValidationResult result)
        {
            Logger.Debug("Processing claims");

            // get profile if enabled
            if (_options.LoadProfile)
            {
                Logger.Debug("load profile");

                var userInfoResult = await GetUserInfoAsync(result.TokenResponse.AccessToken);

                if (!userInfoResult.Success)
                {
                    return(new LoginResult(userInfoResult.Error));
                }

                Logger.Debug("profile claims:");
                Logger.LogClaims(userInfoResult.Claims);

                var primaryClaimTypes = result.Claims.Select(c => c.Type).Distinct();
                foreach (var claim in userInfoResult.Claims.Where(c => !primaryClaimTypes.Contains(c.Type)))
                {
                    result.Claims.Add(claim);
                }
            }
            else
            {
                Logger.Debug("don't load profile");
            }

            // success
            var loginResult = new LoginResult
            {
                Claims                = FilterClaims(result.Claims),
                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(result.TokenResponse.RefreshToken))
            {
                var providerInfo = await _options.GetProviderInformationAsync();

                loginResult.Handler = new RefeshTokenHandler(
                    await TokenClientFactory.CreateAsync(_options),
                    result.TokenResponse.RefreshToken,
                    result.TokenResponse.AccessToken);
            }

            return(loginResult);
        }
        /// <summary>
        /// Validates the response.
        /// </summary>
        /// <param name="data">The response data.</param>
        /// <param name="state">The state.</param>
        /// <returns>Result of the login response validation</returns>
        /// <exception cref="System.InvalidOperationException">Invalid authentication style</exception>
        public async Task <LoginResult> ValidateResponseAsync(string data, AuthorizeState state)
        {
            Logger.Debug("Validate authorize response");

            var response = new AuthorizeResponse(data);

            if (response.IsError)
            {
                Logger.Error(response.Error);

                return(new LoginResult(response.Error));
            }

            if (string.IsNullOrEmpty(response.Code))
            {
                var error = "Missing authorization code";
                Logger.Error(error);

                return(new LoginResult(error));
            }

            if (string.IsNullOrEmpty(response.State))
            {
                var error = "Missing state";
                Logger.Error(error);

                return(new LoginResult(error));
            }

            if (!string.Equals(state.State, response.State, StringComparison.Ordinal))
            {
                var error = "Invalid state";
                Logger.Error(error);

                return(new LoginResult(error));
            }

            ResponseValidationResult validationResult = null;

            if (_options.Style == OidcClientOptions.AuthenticationStyle.AuthorizationCode)
            {
                validationResult = await _validator.ValidateCodeFlowResponseAsync(response, state);
            }
            else if (_options.Style == OidcClientOptions.AuthenticationStyle.Hybrid)
            {
                validationResult = await _validator.ValidateHybridFlowResponseAsync(response, state);
            }
            else
            {
                throw new InvalidOperationException("Invalid authentication style");
            }

            if (!validationResult.Success)
            {
                Logger.Error("Error validating response: " + validationResult.Error);

                return(new LoginResult
                {
                    Error = validationResult.Error
                });
            }

            return(await ProcessClaimsAsync(validationResult));
        }
        public async Task <ResponseValidationResult> ValidateHybridFlowResponseAsync(AuthorizeResponse authorizeResponse, AuthorizeState state)
        {
            Logger.Debug("Validate hybrid flow response");
            var result = new ResponseValidationResult();

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

            // id_token must be present
            if (authorizeResponse.IdentityToken.IsMissing())
            {
                result.Error = "Missing identity token";
                Logger.Error(result.Error);

                return(result);
            }

            // id_token must be valid
            var validationResult = await ValidateIdentityTokenAsync(authorizeResponse.IdentityToken);

            if (!validationResult.Success)
            {
                result.Error = validationResult.Error ?? "Identity token validation error";
                Logger.Error(result.Error);

                return(result);
            }

            // nonce must be valid
            if (!ValidateNonce(state.Nonce, validationResult.Claims))
            {
                result.Error = "Invalid nonce";
                Logger.Error(result.Error);

                return(result);
            }

            // if c_hash is present, it must be valid
            var signingAlgorithmBits = int.Parse(validationResult.SignatureAlgorithm.Substring(2));

            if (!ValidateAuthorizationCodeHash(authorizeResponse.Code, signingAlgorithmBits, validationResult.Claims))
            {
                result.Error = "Invalid c_hash";
                Logger.Error(result.Error);

                return(result);
            }

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

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

            if (tokenResponse.IsError || tokenResponse.IsHttpError)
            {
                Logger.Error(result.Error);
                result.Error = tokenResponse.Error;
                return(result);
            }

            // validate token response
            var tokenResponseValidationResult = await ValidateTokenResponse(tokenResponse);

            if (!tokenResponseValidationResult.Success)
            {
                result.Error = tokenResponseValidationResult.Error;
                return(result);
            }

            return(new ResponseValidationResult
            {
                AuthorizeResponse = authorizeResponse,
                TokenResponse = tokenResponse,
                Claims = tokenResponseValidationResult.IdentityTokenValidationResult.Claims
            });
        }