コード例 #1
0
        async Task <BoolValue <AuthResult> > buildAuthResultAsync(string responseText)
        {
            var dict = JsonSerializer.Deserialize <Dictionary <string, string> >(responseText);

            if (!dict.TryGetValue("access_token", out var accessToken))
            {
                return(BoolValue <AuthResult> .Fail("Could not get a valid access token."));
            }

            var tokens  = new List <TokenInfo>();
            var expires = dict.TryGetValue("expires_in", out var exp) && int.TryParse(exp, out var seconds)
                ? DateTime.Now.AddSeconds(seconds - 4)
                : (DateTime?)null;

            tokens.Add(new TokenInfo(accessToken, TokenRole.AccessToken, expires, null));

            if (dict.TryGetValue("refresh_token", out var refreshToken))
            {
                tokens.Add(new TokenInfo(refreshToken, TokenRole.RefreshToken, null, null));
            }

            if (!dict.TryGetValue("id_token", out var idToken))
            {
                return(await cacheAuthResultAsync(BoolValue <AuthResult> .Success(new AuthResult(Config, Log, tokens.ToArray()))));
            }

            /* obsolete (since we made validation available we no longer auto-validates id token)
             * var idTokenValidation = await validateIdTokenAsync(idToken);
             * if (!idTokenValidation)
             *  return BoolValue<AuthResult>.Fail(idTokenValidation.Message);
             */

            tokens.Add(new TokenInfo(idToken, TokenRole.IdToken, null, validateIdTokenAsync));
            return(await cacheAuthResultAsync(BoolValue <AuthResult> .Success(new AuthResult(Config, Log, tokens.ToArray()))));
        }
コード例 #2
0
        public async Task <BoolValue <ClaimsPrincipal> > ValidateAsync(string idToken, JwtTokenValidationOptions options = null)
        {
            try
            {
                var discoveryDocument = await DiscoveryDocument.DownloadAsync(idToken);

                if (!discoveryDocument)
                {
                    return(BoolValue <ClaimsPrincipal> .Fail(discoveryDocument.Message, discoveryDocument.Exception));
                }

                var jwks = await JsonWebKeySet.DownloadAsync(discoveryDocument.Value.JwksUri);

                if (!jwks)
                {
                    return(BoolValue <ClaimsPrincipal> .Fail(jwks.Message, jwks.Exception));
                }

                options ??= new JwtTokenValidationOptions();
                var parameters = options.ToTokenValidationParameters(
                    new JwtSecurityToken(idToken),
                    discoveryDocument.Value,
                    jwks.Value);
                var handler = new JwtSecurityTokenHandler();
                handler.InboundClaimTypeMap.Clear();
                var user = handler.ValidateToken(idToken, parameters, out _);
                return(BoolValue <ClaimsPrincipal> .Success(user));
            }
            catch (Exception ex)
            {
                return(BoolValue <ClaimsPrincipal> .Fail(ex.Message, ex));
            }
        }
コード例 #3
0
        public async Task <BoolValue <string[]> > TeyGetUserInfoTypes()
        {
            if (AccessToken is null)
            {
                _log?.Warning("Cannot get user information without a valid access token");
                return(BoolValue <string[]> .Fail());
            }

            try
            {
                if (_userInformation is { })
                {
                    return(BoolValue <string[]> .Success(_userInformation.Types));
                }

                var discoDoc = DiscoveryDocument.Current;
                if (discoDoc is null)
                {
                    var gotDiscoDoc = await DiscoveryDocument.TryDownloadAndSetCurrent(this, true);

                    if (!gotDiscoDoc)
                    {
                        _log?.Debug("ERROR: Failed to retrieve the discovery document. Cannot resolve user information endpoint");
                        return(BoolValue <string[]> .Fail("Failed to retrieve the discovery document. Cannot resolve user information endpoint"));
                    }
                    discoDoc = gotDiscoDoc.Value;
                }
                _userInfoLoader ??= new UserInfoLoader(AccessToken, discoDoc, _log);
                _userInformation = await _userInfoLoader.AwaitDownloadedAsync();

                return(BoolValue <string[]> .Success(_userInformation.Types));
            }
コード例 #4
0
        public static BoolValue <DiscoveryEndpoint> TryParseUrl(string input)
        {
            var success = Uri.TryCreate(input, UriKind.Absolute, out var uri);

            if (success == false)
            {
                var msg = $"Malformed URL: {input}";
                return(BoolValue <DiscoveryEndpoint> .Fail(msg, new FormatException(msg)));
            }

            if (!IsValidScheme(uri))
            {
                var msg = $"Invalid scheme in URL: {input}";
                return(BoolValue <DiscoveryEndpoint> .Fail(msg, new InvalidOperationException(msg)));
            }

            var url       = input.RemoveTrailingSlash();
            var authority = url.EndsWith(WellKnownEndpoint, StringComparison.OrdinalIgnoreCase)
                ? url.Substring(0, url.Length - WellKnownEndpoint.Length - 1)
                : url;

            url = url.EndsWith(WellKnownEndpoint, StringComparison.OrdinalIgnoreCase)
                ? url
                : $"{url.EnsureEndsWith("/")}{WellKnownEndpoint}";


            return(BoolValue <DiscoveryEndpoint> .Success(new DiscoveryEndpoint(authority, url)));

            /* obsolete
             * return url.EndsWith(WellKnownEndpoint, StringComparison.OrdinalIgnoreCase)
             *  ?  new DiscoveryEndpoint(url.Substring(0, url.Length - WellKnownEndpoint.Length - 1), url)
             *  : new DiscoveryEndpoint(url, $"{url.EnsureEndsWith("/")}{WellKnownEndpoint}");
             */
        }
コード例 #5
0
        public static async Task <BoolValue <JsonWebKeySet> > DownloadAsync(string url)
        {
            using var client  = new HttpClient();
            using var message = new HttpRequestMessage(HttpMethod.Get, url);
            message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/jwk-set+json"));
            try
            {
                var response = await client.SendAsync(message);

                if (!response.IsSuccessStatusCode)
                {
                    return(BoolValue <JsonWebKeySet> .Fail($"Error connecting to {url}: {response.ReasonPhrase}"));
                }

                var content = await response.Content.ReadAsStringAsync();

                var jsonWebKeySet = JsonConvert.DeserializeObject <JsonWebKeySet>(content);
                return(BoolValue <JsonWebKeySet> .Success(jsonWebKeySet));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }
        }
コード例 #6
0
        static async Task <BoolValue <string> > validateIdTokenAsync(string idToken)
        {
            var validator = new IdTokenValidator();
            var validated = await validator.ValidateAsync(idToken);

            return(validated
                ? BoolValue <string> .Success(idToken)
                : BoolValue <string> .Fail(validated.Message, validated.Exception));
        }
コード例 #7
0
        async Task <BoolValue <AuthResult> > acquireTokenAsyncUsingNativeWebUI()
        {
            LogDebug("[GET AUTH CODE BEGIN]");
            LogDebug($"Listens for callbacks on {Config.RedirectUri} ...");

            var authAppDelegate = Authorization.GetAuthorizingAppDelegate();

            if (authAppDelegate is null)
            {
                LogDebug("Authorization fails: Could not get an authorization app delegate");
                return(BoolValue <AuthResult> .Fail($"Cannot obtain a {typeof(IAuthorizingAppDelegate)}."));
            }

            var callbackHandler = (TetraPakAuthCallbackHandler)DependencyService.Get <IAuthCallbackHandler>();

            callbackHandler.NotifyUriCallback(onUriCallback);

            // make the call for auth code and await callback from redirect ...
            var authState   = new AuthState(Config.IsStateUsed, Config.IsPkceUsed, Config.ClientId);
            var authRequest = buildAuthRequest(authState);

            LogDebug(authRequest);

            await authAppDelegate.OpenInDefaultBrowserAsync(new Uri(authRequest), Config.RedirectUri);

            var callback = await _authCodeTcs.Task.ConfigureAwait(false);

            LogDebug($"Callback notified with value: {callback.Value}");

            // check the PKCE and get the access code ...
            var authCode = callback.Value.TryGetQueryValue("code").Value;
            var inState  = callback.Value.TryGetQueryValue("state").Value;

            LogDebug("[GET AUTH CODE END]");
            if (authState.IsUsed && inState != authState.State)
            {
                return(BoolValue <AuthResult> .Fail($"Returned state was invalid: \"{inState}\". Expected state: \"{authState.State}\""));
            }

            LogDebug("[GET ACCESS CODE BEGIN]");
            var accessCodeResult = await getAccessCode(authCode, authState);

            LogDebug("[GET ACCESS CODE END]");
            return(onAuthorizationDone(accessCodeResult));

            void onUriCallback(Uri uri, out bool isHandled)
            {
                if (!uri.Scheme.Equals(Config.RedirectUri.Scheme) || !uri.Authority.Equals(Config.RedirectUri.Authority))
                {
                    isHandled = false;
                    return;
                }
                isHandled = true;
                _authCodeTcs.SetResult(BoolValue <Uri> .Success(uri));
            }
        }
コード例 #8
0
        protected override BoolValue <string> OnValidateValue(string value)
        {
            value = value?.Trim();
            if (string.IsNullOrEmpty(value) && IsRequired)
            {
                return(BoolValue <string> .Fail("This value is required"));
            }

            return(BoolValue <string> .Success(value));
        }
コード例 #9
0
        public static Task <BoolValue <AuthResult> > TryGetFromRefreshTokenAsync(this AuthConfig config, string refreshToken)
        {
            if (!s_authResults.TryGetValue(refreshToken, out var authResult))
            {
                return(Task.FromResult <BoolValue <AuthResult> >(BoolValue <AuthResult> .Fail()));
            }

            s_authResults.Remove(refreshToken);
            return(Task.FromResult(BoolValue <AuthResult> .Success(authResult)));
        }
コード例 #10
0
        protected override BoolValue <string> OnValidateValue(string value)
        {
            value = value?.Trim();
            if (string.IsNullOrEmpty(value))
            {
                return(base.OnValidateValue(value));
            }

            return(!Uri.TryCreate(value, UriKind.Absolute, out _)
                ? BoolValue <string> .Fail("Invalid absolute URI")
                : BoolValue <string> .Success(value));
        }
コード例 #11
0
        /// <summary>
        ///   Attempts obtaining user information.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// </remarks>
        public async Task <BoolValue <UserInformation> > TryGetUserInformationAsync()
        {
            _log?.Debug("[GET USER INFORMATION BEGIN]");

            if (AccessToken is null)
            {
                _log?.Warning("Cannot get user information without a valid access token");
                return(BoolValue <UserInformation> .Fail());
            }

            if (_userInformation != null)
            {
                _log?.Debug("User information was cached");
                return(BoolValue <UserInformation> .Success(_userInformation));
            }

            try
            {
                _log?.Debug("Retrieves user information from API ...");
                var discoDoc = DiscoveryDocument.Current;
                if (discoDoc is null)
                {
                    var gotDiscoDoc = await DiscoveryDocument.TryDownloadAndSetCurrent(this, true);

                    if (!gotDiscoDoc)
                    {
                        _log?.Debug("ERROR: Failed to retrieve the discovery document. Cannot resolve user information endpoint");
                        return(BoolValue <UserInformation> .Fail("Failed to retrieve the discovery document. Cannot resolve user information endpoint"));
                    }

                    discoDoc = gotDiscoDoc.Value;
                }

                _userInfoLoader ??= new UserInfoLoader(AccessToken, discoDoc, _log);
                _userInformation = await _userInfoLoader.AwaitDownloadedAsync();

                _log?.Debug("Successfully received user information from API");
                return(BoolValue <UserInformation> .Success(_userInformation));
            }
            catch (Exception ex)
            {
                const string Message = "Failed while retrieving user information from API";
                _log?.Error(ex, Message);
                return(BoolValue <UserInformation> .Fail(Message, ex));
            }
            finally
            {
                _log?.Debug("[GET USER INFORMATION END]");
            }
        }
コード例 #12
0
        public static async Task <BoolValue <AuthResult> > CacheAsync(this AuthConfig config, AuthResult authResult, string cacheKey)
        {
            if (config.IsCaching)
            {
                await config.TokenCache.AddAsync(cacheKey, authResult, true);
            }

            var refreshToken = authResult.Tokens.FirstOrDefault(i => i.Role == TokenRole.RefreshToken);

            if (refreshToken != null)
            {
                s_authResults.Add(refreshToken.TokenValue, authResult);
            }

            return(BoolValue <AuthResult> .Success(authResult));
        }
コード例 #13
0
        /// <summary>
        ///   Attempts fetching all tokens from the cache.
        /// </summary>
        /// <param name="key">
        ///   Identifies the tokens (typically a client/app id).
        /// </param>
        /// <returns>
        ///   An <see cref="AuthResult"/> item.
        /// </returns>
        public override async Task <BoolValue <AuthResult> > TryGetAsync(string key = null)
        {
            key ??= DefaultKey;
            var cached = await base.TryGetAsync(key);

            if (cached || !_isRefreshTokenPersisted)
            {
                return(cached);
            }

            // look for persisted refresh token ...
            var refreshToken = await SecureStorage.GetAsync(key);

            return(refreshToken is null
                ? cached
                : BoolValue <AuthResult> .Success(new AuthResult(_authConfig, _log, new TokenInfo(refreshToken, TokenRole.RefreshToken))));
        }
コード例 #14
0
 static Task <BoolValue <string> > onValidateSimulatedIdToken(string token) => Task.FromResult(BoolValue <string> .Success(token));