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())))); }
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)); } }
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)); }
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}"); */ }
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; } }
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)); }
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)); } }
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)); }
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))); }
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)); }
/// <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]"); } }
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)); }
/// <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)))); }
static Task <BoolValue <string> > onValidateSimulatedIdToken(string token) => Task.FromResult(BoolValue <string> .Success(token));