public async Task RefreshToken(JwtSecurityToken jwt, TokenBaseAttribute attribute) { if (string.IsNullOrEmpty(attribute.Resource)) { throw new ArgumentException("A resource is required to renew an access token."); } if (string.IsNullOrEmpty(attribute.UserId)) { throw new ArgumentException("A userId is required to renew an access token."); } if (string.IsNullOrEmpty(attribute.IdentityProvider)) { throw new ArgumentException("A provider is necessary to renew an access token."); } string refreshUrl = $"https://{_options.HostName}/.auth/refresh?resource=" + WebUtility.UrlEncode(attribute.Resource); using (var refreshRequest = new HttpRequestMessage(HttpMethod.Get, refreshUrl)) { refreshRequest.Headers.Add("x-zumo-auth", jwt.RawData); _log.LogTrace($"Refreshing ${attribute.IdentityProvider} access token for user ${attribute.UserId} at ${refreshUrl}"); using (HttpResponseMessage response = await _httpClient.SendAsync(refreshRequest)) { _log.LogTrace($"Response from ${refreshUrl}: {response.StatusCode}"); if (!response.IsSuccessStatusCode) { string errorResponse = await response.Content.ReadAsStringAsync(); throw new InvalidOperationException($"Failed to refresh {attribute.UserId} {attribute.IdentityProvider} error={response.StatusCode} {errorResponse}"); } } } }
private JwtSecurityToken CreateTokenForEasyAuthAccess(TokenBaseAttribute attribute) { if (string.IsNullOrEmpty(attribute.UserId)) { throw new ArgumentException("A userId is required to obtain an access token."); } if (string.IsNullOrEmpty(attribute.IdentityProvider)) { throw new ArgumentException("A provider is necessary to obtain an access token."); } var identityClaims = new ClaimsIdentity(attribute.UserId); identityClaims.AddClaim(new Claim(ClaimTypes.NameIdentifier, attribute.UserId)); identityClaims.AddClaim(new Claim("idp", attribute.IdentityProvider)); var baseUrl = $"https://{_options.HostName}/"; var descr = new SecurityTokenDescriptor { Audience = baseUrl, Issuer = baseUrl, Subject = identityClaims, Expires = DateTime.UtcNow.AddMinutes(JwtTokenDurationInMinutes), SigningCredentials = new HmacSigningCredentials(_options.SigningKey), }; return((JwtSecurityToken)JwtHandler.CreateToken(descr)); }
/// <summary> /// Retrieve Easy Auth token based on provider & principal ID /// </summary> /// <param name="attribute">The metadata for the token to grab</param> /// <returns>Task with Token Store entry of the token</returns> public async Task <string> GetEasyAuthAccessTokenAsync(TokenBaseAttribute attribute) { var jwt = CreateTokenForEasyAuthAccess(attribute); EasyAuthTokenStoreEntry tokenStoreEntry = await _client.GetTokenStoreEntry(jwt, attribute); bool isTokenValid = IsTokenValid(tokenStoreEntry.AccessToken); bool isTokenExpired = tokenStoreEntry.ExpiresOn <= DateTime.UtcNow.AddMinutes(GraphTokenBufferInMinutes); bool isRefreshable = IsRefreshableProvider(attribute.IdentityProvider); if (isRefreshable && (isTokenExpired || !isTokenValid)) { await _client.RefreshToken(jwt, attribute); // Now that the refresh has occured, grab the new token tokenStoreEntry = await _client.GetTokenStoreEntry(jwt, attribute); } return(tokenStoreEntry.AccessToken); }
public async Task <string> ConvertAsync(TokenBaseAttribute attribute, CancellationToken cancellationToken) { attribute.CheckValidity(); switch (attribute.Identity) { case TokenIdentityMode.UserFromId: // If the attribute has no identity provider, assume AAD attribute.IdentityProvider = attribute.IdentityProvider ?? "AAD"; var easyAuthTokenManager = new EasyAuthTokenManager(_easyAuthClient, _options); return(await easyAuthTokenManager.GetEasyAuthAccessTokenAsync(attribute)); case TokenIdentityMode.UserFromToken: return(await GetAuthTokenFromUserToken(attribute.UserToken, attribute.Resource)); case TokenIdentityMode.ClientCredentials: return(await _aadManager.GetTokenFromClientCredentials(attribute.Resource)); } throw new InvalidOperationException("Unable to authorize without Principal ID or ID Token."); }
public async Task <EasyAuthTokenStoreEntry> GetTokenStoreEntry(JwtSecurityToken jwt, TokenBaseAttribute attribute) { // Send the token to the local /.auth/me endpoint and return the JSON string meUrl = $"https://{_options.HostName}/.auth/me?provider={attribute.IdentityProvider}"; using (var request = new HttpRequestMessage(HttpMethod.Get, meUrl)) { request.Headers.Add("x-zumo-auth", jwt.RawData); _log.LogTrace($"Fetching user token data from {meUrl}"); using (HttpResponseMessage response = await _httpClient.SendAsync(request)) { _log.LogTrace($"Response from '${meUrl}: {response.StatusCode}"); if (!response.IsSuccessStatusCode) { string errorResponse = await response.Content.ReadAsStringAsync(); throw new InvalidOperationException($"Request to {meUrl} failed. Status Code: {response.StatusCode}; Body: {errorResponse}"); } var responseString = await response.Content.ReadAsStringAsync(); return(JsonConvert.DeserializeObject <EasyAuthTokenStoreEntry>(responseString)); } } }
/// <summary> /// Either retrieve existing GSC or create a new one /// GSCs are cached using a combination of the user's principal ID and the scopes of the token used to authenticate /// </summary> /// <param name="attribute">Token attribute with either principal ID or ID token</param> /// <returns>Authenticated GSC</returns> public virtual async Task <IGraphServiceClient> GetMSGraphClientFromTokenAttributeAsync(TokenBaseAttribute attribute, CancellationToken cancellationToken) { string token = await this._tokenProvider.ConvertAsync(attribute, cancellationToken); string principalId = GetTokenOID(token); var key = string.Concat(principalId, " ", GetTokenOrderedScopes(token)); CachedClient cachedClient = null; // Check to see if there already exists a GSC associated with this principal ID and the token scopes. if (_clients.TryGetValue(key, out cachedClient)) { // Check if token is expired if (cachedClient.expirationDate < DateTimeOffset.Now.ToUnixTimeSeconds()) { // Need to update the client's token & expiration date // $$ todo -- just reset token instead of whole new authentication provider? _clientProvider.UpdateGraphServiceClientAuthToken(cachedClient.client, token); cachedClient.expirationDate = GetTokenExpirationDate(token); } return(cachedClient.client); } else { cachedClient = new CachedClient { client = _clientProvider.CreateNewGraphServiceClient(token), expirationDate = GetTokenExpirationDate(token), }; _clients.TryAdd(key, cachedClient); return(cachedClient.client); } }