async Task <Outcome <AuthorizedClient> > GetAuthorizedClientAsync( bool allowCached = true, CancellationToken?cancellationToken = null) { ActorToken token = null; var isTokenCached = false; if (allowCached) { var cachedOutcome = await _cache.GetHelloWorldToken(); token = cachedOutcome ? cachedOutcome.Value : null; isTokenCached = token is {}; } if (token is null) { var tokenOutcome = await _credentialsService.AcquireTokenAsync(cancellationToken ?? new CancellationToken()); if (!tokenOutcome) { return(Outcome <AuthorizedClient> .Fail(tokenOutcome.Exception)); } token = tokenOutcome.Value.Token; } var client = new HttpClient(); client.DefaultRequestHeaders.Add(HeaderNames.Authorization, token); return(Outcome <AuthorizedClient> .Success(new AuthorizedClient(client, isTokenCached))); }
async Task <Outcome <ActorToken> > getCachedIdentityTokenAsync(ActorToken accessToken) { if (AmbientData.Cache is null) { return(Outcome <ActorToken> .Fail(new Exception("Caching is not supported"))); } return(await AmbientData.Cache.GetAsync <ActorToken>(IdentityTokenCache, accessToken)); }
/// <summary> /// Takes the Actor object, validates the properties and then retrieves /// the Authentication values from the repository, turning them in to a /// JWT token /// </summary> /// <param name="actor">The Actor object to validate</param> /// <param name="cancellationToken"></param> /// <returns>An <code>object</code> containing the token and expires_in values</returns> /// <exception cref="ArgumentNullException"></exception> public async Task <ActorToken> GetClaimsIdentityAsync(ActorLogin actor, CancellationToken cancellationToken = default(CancellationToken)) { ActorToken token = null; // Validate the Model, the results will be collected in the ICollection, but if everything is OK then isValid = true ICollection <ValidationResult> validationResults = new List <ValidationResult>(); bool isValid = Validator.TryValidateObject(actor, new ValidationContext(actor), validationResults, true); if (isValid) { Authentication auth = await repository.GetClaimsValuesAsync(actor, cancellationToken); try { if (null == auth) { // Get the generated token and encode it, then set as the return value string encodedJwt = GenerateEncodedToken(auth); token = new ActorToken { Token = encodedJwt, ExpiresIn = (int)jwtOptions.ValidFor.TotalSeconds }; } } catch (ArgumentException argEx) // Catches ArgumentNullException too { // Reset the token response to null, any error is considered a security failure token = null; Trace.WriteLine(argEx.Message); #if DEBUG Trace.WriteLine(argEx.StackTrace); #endif } catch (Microsoft.IdentityModel.Tokens.SecurityTokenCompressionFailedException tokenFailedEx) { // The token may have been valid, but something went wrong so ensure the security fails token = null; Trace.WriteLine(tokenFailedEx.Message); #if DEBUG Trace.WriteLine(tokenFailedEx.StackTrace); #endif } } else { // As each validation error will aggregated, loop over the results foreach (ValidationResult vr in validationResults) { // Throw an exception for the first error, ignoring any others that are present throw new ArgumentNullException(((string[])vr.MemberNames)[0], vr.ErrorMessage); } } // Return the Token object return(token); }
protected override async Task <Outcome <ActorToken> > OnGetAccessTokenAsync(CancellationToken cancellationToken) { try { var accessTokenOutcome = await base.OnGetAccessTokenAsync(cancellationToken); if (!accessTokenOutcome) { return(accessTokenOutcome); } // try getting a cached exchanged token ... var accessToken = accessTokenOutcome.Value; var cachedOutcome = await getCachedIdentityTokenAsync(accessToken); if (cachedOutcome) { return(cachedOutcome); } // exchange token for var clientCredentials = await OnGetClientCredentials(); var credentials = new BasicAuthCredentials(clientCredentials.Identity, clientCredentials.Secret); var bearerToken = accessTokenOutcome.Value as BearerToken; var isBearerToken = bearerToken is { }; var subjectToken = isBearerToken ? bearerToken.Value : accessTokenOutcome.Value.ToString(); var txOutcome = await _tokenExchangeService.ExchangeAccessTokenAsync( credentials, subjectToken, cancellationToken); if (!txOutcome || !ActorToken.TryParse(txOutcome.Value.AccessToken, out var actorToken)) { return(Outcome <ActorToken> .Fail(txOutcome.Exception)); } var exchangedToken = isBearerToken ? new BearerToken(actorToken.Identity, false) : actorToken; // cache exchanged token and return it ... await cacheTokenExchangeAsync(accessToken, exchangedToken); return(Outcome <ActorToken> .Success(exchangedToken)); } catch (Exception ex) { Logger.Error(new Exception($"Claims transformation failure: {ex.Message}", ex)); throw; } }
/// <summary> /// Tries obtaining an access token from the request. /// </summary> /// <returns> /// An <see cref="Outcome{T}"/> instance indicating success/failure. On success the outcome /// holds the access token in its <see cref="Outcome{T}.Value"/> property. On failure the outcome /// declares the problem via its <see cref="Outcome.Exception"/> property. /// </returns> /// <seealso cref="GetAccessToken(Microsoft.AspNetCore.Http.HttpContext, TetraPakAuthConfig)"/> /// <see cref="GetAccessTokenAsync(Microsoft.AspNetCore.Http.HttpRequest, TetraPakAuthConfig)"/> public static Task <Outcome <ActorToken> > GetAccessTokenAsync(this HttpContext self, TetraPakAuthConfig authConfig) { if (self.Items.TryGetValue(AmbientData.Keys.AccessToken, out var o) && o is string s && ActorToken.TryParse(s, out var actorToken)) { return(Task.FromResult(Outcome <ActorToken> .Success(actorToken))); } var headerIdent = authConfig?.AuthorizationHeader ?? HeaderNames.Authorization; s = self.Request.Headers[headerIdent].FirstOrDefault(); if (s is {} && ActorToken.TryParse(s, out actorToken))
/// <summary> /// Provides a new token to replace an old or expiring one. Passing /// Claims to this method will generate a new Token so only call /// form Authenticated Controllers/Endpoints /// </summary> /// <param name="claims">A list of claims required to populate the new Token</param> /// <returns></returns> /// <remarks>Only call from authenticated end points</remarks> public ActorToken RenewClaimsIdentity(ClaimsPrincipal claims) { ActorToken token; try { Authentication auth = new Authentication { Email = claims.FindFirst(ClaimTypes.Email)?.Value, PrimaryId = claims.FindFirst(ClaimTypes.PrimarySid)?.Value, PrimaryGroupId = claims.FindFirst(ClaimTypes.PrimaryGroupSid)?.Value, RoleId = claims.FindFirst(ClaimTypes.Role)?.Value, Alias = claims.FindFirst(ClaimTypes.Actor)?.Value, Hidden = bool.Parse(claims.FindFirst(ClaimTypes.Anonymous)?.Value), }; // Generate and assign the new token values string encodedToken = GenerateEncodedToken(auth); token = new ActorToken { Token = encodedToken, ExpiresIn = (int)jwtOptions.ValidFor.TotalSeconds }; } catch (ArgumentException argEx) // Catches ArgumentNullException { token = null; Trace.WriteLine($"{argEx.ParamName} threw an ArgumentException: {argEx.Message}"); } catch (Microsoft.IdentityModel.Tokens.SecurityTokenCompressionFailedException tokenFailedEx) { token = null; Trace.WriteLine(tokenFailedEx.Message); } catch (FormatException formatEx) { token = null; Trace.WriteLine(formatEx.Message); } return(token); }
async Task cacheTokenExchangeAsync(ActorToken accessToken, ActorToken exchangedToken) { if (AmbientData.Cache is { })
public DetailsModel(IIdentity identity, ActorToken accessToken) : base(identity) { AccessToken = accessToken; }