/// <summary>
        /// Gets the user claims from the userinfo endpoint.
        /// </summary>
        /// <param name="accessToken">The access token.</param>
        /// <returns>User claims</returns>
        public async Task <UserInfoResult> GetUserInfoAsync(string accessToken)
        {
            var providerInfo = await _options.GetProviderInformationAsync();

            if (accessToken.IsMissing())
            {
                throw new ArgumentNullException(nameof(accessToken));
            }
            if (providerInfo.UserInfoEndpoint.IsMissing())
            {
                throw new InvalidOperationException("No userinfo endpoint specified");
            }

            var handler = _options.BackchannelHandler ?? new HttpClientHandler();

            var userInfoClient = new UserInfoClient(new Uri(providerInfo.UserInfoEndpoint), accessToken, handler);

            userInfoClient.Timeout = _options.BackchannelTimeout;

            var userInfoResponse = await userInfoClient.GetAsync();

            if (userInfoResponse.IsError)
            {
                return(new UserInfoResult
                {
                    Error = userInfoResponse.ErrorMessage
                });
            }

            return(new UserInfoResult
            {
                Claims = userInfoResponse.Claims.Select(c => new Claim(c.Item1, c.Item2)).ToClaims()
            });
        }
        /// <summary>
        /// Starts an end_session request using a browser.
        /// </summary>
        /// <param name="identityToken">The identity token.</param>
        /// <param name="trySilent">if set to <c>true</c> try a silent request.</param>
        /// <returns></returns>
        /// <exception cref="System.InvalidOperationException">
        /// No web view defined.
        /// or
        /// no endsession_endpoint defined
        /// </exception>
        public async Task EndSessionAsync(string identityToken = null, bool trySilent = true)
        {
            if (_options.WebView == null)
            {
                throw new InvalidOperationException("No web view defined.");
            }

            string url = (await _options.GetProviderInformationAsync()).EndSessionEndpoint;

            if (url.IsMissing())
            {
                throw new InvalidOperationException("no endsession_endpoint defined");
            }

            if (!string.IsNullOrWhiteSpace(identityToken))
            {
                url += $"?{OidcConstants.EndSessionRequest.IdTokenHint}={identityToken}" +
                       $"&{OidcConstants.EndSessionRequest.PostLogoutRedirectUri}={_options.RedirectUri}";
            }

            var webViewOptions = new InvokeOptions(url, _options.RedirectUri)
            {
                ResponseMode         = ResponseMode.Redirect,
                InvisibleModeTimeout = _options.WebViewTimeout
            };

            if (trySilent)
            {
                webViewOptions.InitialDisplayMode = DisplayMode.Hidden;
            }

            var result = await _options.WebView.InvokeAsync(webViewOptions);
        }
        private async Task <LoginResult> ProcessClaims(AuthorizeResponse response, TokenResponse tokenResult, Claims claims)
        {
            // get profile if enabled
            if (_options.LoadProfile)
            {
                var userInfoResult = await GetUserInfoAsync(tokenResult.AccessToken);

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

                var primaryClaimTypes = claims.Select(c => c.Type).Distinct();
                foreach (var claim in userInfoResult.Claims.Where(c => !primaryClaimTypes.Contains(c.Type)))
                {
                    claims.Add(claim);
                }
            }

            // success
            var loginResult = new LoginResult
            {
                Success               = true,
                Claims                = FilterClaims(claims),
                AccessToken           = tokenResult.AccessToken,
                RefreshToken          = tokenResult.RefreshToken,
                AccessTokenExpiration = DateTime.Now.AddSeconds(tokenResult.ExpiresIn),
                IdentityToken         = response.IdentityToken,
                AuthenticationTime    = DateTime.Now,
            };

            if (!string.IsNullOrWhiteSpace(tokenResult.RefreshToken))
            {
                var providerInfo = await _options.GetProviderInformationAsync();

                loginResult.Handler = new RefeshTokenHandler(
                    providerInfo.TokenEndpoint,
                    _options.ClientId,
                    _options.ClientSecret,
                    tokenResult.RefreshToken,
                    tokenResult.AccessToken);
            }

            return(loginResult);
        }
        private async Task <IdentityTokenValidationResult> ValidateIdentityTokenAsync(string idToken)
        {
            var providerInfo = await _options.GetProviderInformationAsync();

            Logger.Debug("Calling identity token validator: " + _options.IdentityTokenValidator.GetType().FullName);
            var validationResult = await _options.IdentityTokenValidator.ValidateAsync(idToken, _options.ClientId, providerInfo);

            if (validationResult.Success == false)
            {
                return(validationResult);
            }

            var claims = validationResult.Claims;

            Logger.Debug("identity token validation claims:");
            Logger.LogClaims(claims);

            // validate audience
            var audience = claims.FindFirst(JwtClaimTypes.Audience)?.Value ?? "";

            if (!string.Equals(_options.ClientId, audience, StringComparison.Ordinal))
            {
                Logger.Error($"client id ({_options.ClientId}) does not match audience ({audience})");

                return(new IdentityTokenValidationResult
                {
                    Error = "invalid audience"
                });
            }

            // validate issuer
            var issuer = claims.FindFirst(JwtClaimTypes.Issuer)?.Value ?? "";

            if (!string.Equals(providerInfo.IssuerName, issuer, StringComparison.Ordinal))
            {
                Logger.Error($"configured issuer ({providerInfo.IssuerName}) does not match token issuer ({issuer}");

                return(new IdentityTokenValidationResult
                {
                    Error = "invalid issuer"
                });
            }

            return(validationResult);
        }
Exemple #5
0
        public async Task <LoginResult> ValidateResponseAsync(string data, AuthorizeState state)
        {
            var result = new LoginResult {
                Success = false
            };
            var response = new AuthorizeResponse(data);

            if (response.IsError)
            {
                result.Error = response.Error;
                return(result);
            }

            if (string.IsNullOrEmpty(response.Code))
            {
                result.Error = "Missing authorization code";
                return(result);
            }

            if (string.IsNullOrEmpty(response.IdentityToken))
            {
                result.Error = "Missing identity token";
                return(result);
            }

            // validate identity token signature
            var providerInfo = await _options.GetProviderInformationAsync();

            var validationResult = await _options.IdentityTokenValidator.ValidateAsync(response.IdentityToken, _options.ClientId, providerInfo);

            if (validationResult.Success == false)
            {
                return(new LoginResult
                {
                    Success = false,
                    Error = validationResult.Error ?? "identity token validation error"
                });
            }

            var claims = validationResult.Claims;

            // validate audience
            var audience = claims.FindFirst(JwtClaimTypes.Audience)?.Value ?? "";

            if (!string.Equals(_options.ClientId, audience))
            {
                return(new LoginResult
                {
                    Success = false,
                    Error = "invalid audience"
                });
            }

            // validate issuer
            var issuer = claims.FindFirst(JwtClaimTypes.Issuer)?.Value ?? "";

            if (!string.Equals(providerInfo.IssuerName, issuer))
            {
                return(new LoginResult
                {
                    Success = false,
                    Error = "invalid issuer"
                });
            }

            // validate nonce
            var tokenNonce = claims.FindFirst(JwtClaimTypes.Nonce)?.Value ?? "";

            if (!string.Equals(state.Nonce, tokenNonce))
            {
                return(new LoginResult
                {
                    Success = false,
                    Error = "invalid nonce"
                });
            }

            // validate c_hash
            var cHash  = claims.FindFirst(JwtClaimTypes.AuthorizationCodeHash)?.Value ?? "";
            var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);

            var codeHash = sha256.HashData(
                CryptographicBuffer.CreateFromByteArray(
                    Encoding.UTF8.GetBytes(response.Code)));

            byte[] codeHashArray;
            CryptographicBuffer.CopyToByteArray(codeHash, out codeHashArray);

            byte[] leftPart = new byte[16];
            Array.Copy(codeHashArray, leftPart, 16);

            var leftPartB64 = Base64Url.Encode(leftPart);

            if (!leftPartB64.Equals(cHash))
            {
                return(new LoginResult
                {
                    Success = false,
                    Error = "invalid code"
                });
            }

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

            if (tokenResult.IsError || tokenResult.IsHttpError)
            {
                return(new LoginResult
                {
                    Success = false,
                    Error = tokenResult.Error
                });
            }

            // get profile if enabled
            if (_options.LoadProfile)
            {
                var userInfoResult = await GetUserInfoAsync(tokenResult.AccessToken);

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

                var primaryClaimTypes = claims.Select(c => c.Type).Distinct();
                foreach (var claim in userInfoResult.Claims.Where(c => !primaryClaimTypes.Contains(c.Type)))
                {
                    claims.Add(claim);
                }
            }

            // success
            var loginResult = new LoginResult
            {
                Success               = true,
                Claims                = FilterClaims(claims),
                AccessToken           = tokenResult.AccessToken,
                RefreshToken          = tokenResult.RefreshToken,
                AccessTokenExpiration = DateTime.Now.AddSeconds(tokenResult.ExpiresIn),
                IdentityToken         = response.IdentityToken,
                AuthenticationTime    = DateTime.Now,
            };

            if (!string.IsNullOrWhiteSpace(tokenResult.RefreshToken))
            {
                loginResult.Handler = new RefeshTokenHandler(
                    providerInfo.TokenEndpoint,
                    _options.ClientId,
                    _options.ClientSecret,
                    tokenResult.RefreshToken,
                    tokenResult.AccessToken);
            }

            return(loginResult);
        }