internal static void SetBrokerResult(Intent data, int resultCode) { if (resultCode != BrokerResponseCode.ResponseReceived) { resultEx = new AuthenticationResultEx { Exception = new AdalException(data.GetStringExtra(BrokerConstants.ResponseErrorCode), data.GetStringExtra(BrokerConstants.ResponseErrorMessage)) }; } else { var tokenResponse = new TokenResponse { // Authority = data.GetStringExtra(BrokerConstants.AccountAuthority), AccessToken = data.GetStringExtra(BrokerConstants.AccountAccessToken), IdTokenString = data.GetStringExtra(BrokerConstants.AccountIdToken), TokenType = "Bearer", ExpiresOn = data.GetLongExtra(BrokerConstants.AccountExpireDate, 0) }; resultEx = tokenResponse.GetResult(BrokerProxy.ConvertFromTimeT(tokenResponse.ExpiresOn), BrokerProxy.ConvertFromTimeT(tokenResponse.ExpiresOn)); } readyForResponse.Release(); }
protected override async Task <AuthenticationResultEx> SendTokenRequestAsync() { TimeSpan timeRemaining = deviceCodeResult.ExpiresOn - DateTimeOffset.UtcNow; AuthenticationResultEx resultEx = null; while (timeRemaining.TotalSeconds > 0) { try { resultEx = await base.SendTokenRequestAsync().ConfigureAwait(false); break; } catch (AdalServiceException exc) { if (!exc.ErrorCode.Equals(AdalErrorEx.DeviceCodeAuthorizationPendingError)) { throw; } } await Task.Delay(TimeSpan.FromSeconds(deviceCodeResult.Interval)).ConfigureAwait(false); timeRemaining = deviceCodeResult.ExpiresOn - DateTimeOffset.UtcNow; } return(resultEx); }
internal static void SetBrokerResult(Intent data, int resultCode) { if (resultCode != BrokerResponseCode.ResponseReceived) { resultEx = new AuthenticationResultEx { Exception = new AdalException(data.GetStringExtra(BrokerConstants.ResponseErrorCode), data.GetStringExtra(BrokerConstants.ResponseErrorMessage)) }; } else { string accessToken = data.GetStringExtra(BrokerConstants.AccountAccessToken); DateTimeOffset expiresOn = BrokerProxy.ConvertFromTimeT(data.GetLongExtra(BrokerConstants.AccountExpireDate, 0)); UserInfo userInfo = BrokerProxy.GetUserInfoFromBrokerResult(data.Extras); resultEx = new AuthenticationResultEx { Result = new AuthenticationResult("Bearer", accessToken, expiresOn) { UserInfo = userInfo } }; } readyForResponse.Release(); }
internal void StoreToCache(AuthenticationResultEx result, string authority, string resource, string clientId, TokenSubjectType subjectType, CallState callState) { lock (cacheLock) { PlatformPlugin.Logger.Verbose(callState, "Storing token in the cache..."); string uniqueId = (result.Result.UserInfo != null) ? result.Result.UserInfo.UniqueId : null; string displayableId = (result.Result.UserInfo != null) ? result.Result.UserInfo.DisplayableId : null; this.OnBeforeWrite(new TokenCacheNotificationArgs { Resource = resource, ClientId = clientId, UniqueId = uniqueId, DisplayableId = displayableId }); TokenCacheKey tokenCacheKey = new TokenCacheKey(authority, resource, clientId, subjectType, result.Result.UserInfo); this.tokenCacheDictionary[tokenCacheKey] = result; PlatformPlugin.Logger.Verbose(callState, "An item was stored in the cache"); this.UpdateCachedMrrtRefreshTokens(result, clientId, subjectType); this.HasStateChanged = true; } }
internal async Task StoreToCacheAsync(AuthenticationResultEx result, string authority, string resource, string clientId, TokenSubjectType subjectType, CallState callState) { var metadata = await InstanceDiscovery.GetMetadataEntryAsync(new Uri(authority), false, callState).ConfigureAwait(false); StoreToCacheCommon(result, ReplaceHost(authority, metadata.PreferredCache), resource, clientId, subjectType, callState); }
protected override async Task <AuthenticationResultEx> SendTokenRequestAsync() { AuthenticationResultEx resultEx = await base.SendTokenRequestAsync().ConfigureAwait(false); if (resultEx != null) { resultEx.UserAssertionHash = CacheQueryData.AssertionHash; } return(resultEx); }
/// <summary> /// Deserializes state of the cache. The state should be the blob received earlier by calling the method Serialize. /// </summary> /// <param name="state">State of the cache as a blob</param> public void Deserialize(byte[] state) { lock (cacheLock) { if (state == null) { this.tokenCacheDictionary.Clear(); return; } using (Stream stream = new MemoryStream()) { BinaryWriter writer = new BinaryWriter(stream); writer.Write(state); writer.Flush(); stream.Position = 0; BinaryReader reader = new BinaryReader(stream); int schemaVersion = reader.ReadInt32(); if (schemaVersion != SchemaVersion) { CallState.Default.Logger.Warning(null, "The version of the persistent state of the cache does not match the current schema, so skipping deserialization."); return; } this.tokenCacheDictionary.Clear(); int count = reader.ReadInt32(); for (int n = 0; n < count; n++) { string keyString = reader.ReadString(); string[] kvpElements = keyString.Split(new[] { Delimiter }, StringSplitOptions.None); AuthenticationResultEx resultEx = AuthenticationResultEx.Deserialize(reader.ReadString()); TokenCacheKey key = new TokenCacheKey(kvpElements[0], kvpElements[1], kvpElements[2], (TokenSubjectType)int.Parse(kvpElements[3], CultureInfo.CurrentCulture), resultEx.Result.UserInfo); this.tokenCacheDictionary.Add(key, resultEx); } CallState.Default.Logger.Information(null, string.Format(CultureInfo.CurrentCulture, "Deserialized {0} items to token cache.", count)); } } }
public async Task <AuthenticationResultEx> AcquireTokenUsingBroker(IDictionary <string, string> brokerPayload) { resultEx = null; readyForResponse = new SemaphoreSlim(0); try { await Task.Run(() => AcquireToken(brokerPayload)).ConfigureAwait(false); } catch (Exception exc) { PlatformPlugin.Logger.Error(null, exc); throw; } await readyForResponse.WaitAsync().ConfigureAwait(false); return(resultEx); }
protected override void PostTokenRequest(AuthenticationResultEx resultEx) { base.PostTokenRequest(resultEx); UserInfo userInfo = resultEx.Result.UserInfo; this.UniqueId = (userInfo == null) ? null : userInfo.UniqueId; this.DisplayableId = (userInfo == null) ? null : userInfo.DisplayableId; if (resultEx.ResourceInResponse != null) { this.Resource = resultEx.ResourceInResponse; PlatformPlugin.Logger.Verbose(this.CallState, "Resource value in the token response was used for storing tokens in the cache"); } // If resource is not passed as an argument and is not returned by STS either, // we cannot store the token in the cache with null resource. // TODO: Store refresh token though if STS supports MRRT. this.StoreToCache = this.StoreToCache && (this.Resource != null); }
internal async Task <AuthenticationResultEx> LoadFromCacheAsync(CacheQueryData cacheQueryData, CallState callState) { AuthenticationResultEx resultEx = null; var aliasedHosts = await GetOrderedAliasesAsync(cacheQueryData.Authority, false, callState).ConfigureAwait(false); foreach (var aliasedHost in aliasedHosts) { cacheQueryData.Authority = ReplaceHost(cacheQueryData.Authority, aliasedHost); resultEx = LoadFromCacheCommon(cacheQueryData, callState); if (resultEx?.Result != null) { break; } } return(resultEx); }
protected async Task <AuthenticationResultEx> SendTokenRequestByRefreshTokenAsync(string refreshToken) { var requestParameters = new DictionaryRequestParameters(this.Resource, this.ClientKey); requestParameters[OAuthParameter.GrantType] = OAuthGrantType.RefreshToken; requestParameters[OAuthParameter.RefreshToken] = refreshToken; requestParameters[OAuthParameter.Scope] = OAuthValue.ScopeOpenId; AuthenticationResultEx result = await this.SendHttpMessageAsync(requestParameters); if (result.RefreshToken == null) { result.RefreshToken = refreshToken; PlatformPlugin.Logger.Verbose(this.CallState, "Refresh token was missing from the token refresh response, so the refresh token in the request is returned instead"); } return(result); }
private async Task <AuthenticationResultEx> RefreshAccessTokenAsync(AuthenticationResultEx result) { AuthenticationResultEx newResultEx = null; if (this.Resource != null) { CallState.Logger.Verbose(this.CallState, "Refreshing access token..."); try { newResultEx = await this.SendTokenRequestByRefreshTokenAsync(result.RefreshToken) .ConfigureAwait(false); this.Authenticator.UpdateTenantId(result.Result.TenantId); newResultEx.Result.Authority = Authenticator.Authority; if (newResultEx.Result.IdToken == null) { // If Id token is not returned by token endpoint when refresh token is redeemed, we should copy tenant and user information from the cached token. newResultEx.Result.UpdateTenantAndUserInfo(result.Result.TenantId, result.Result.IdToken, result.Result.UserInfo); } } catch (AdalException ex) { AdalServiceException serviceException = ex as AdalServiceException; if (serviceException != null && serviceException.ErrorCode == "invalid_request") { throw new AdalServiceException( AdalError.FailedToRefreshToken, AdalErrorMessage.FailedToRefreshToken + ". " + serviceException.Message, serviceException.ServiceErrorCodes, serviceException); } newResultEx = new AuthenticationResultEx { Exception = ex }; } } return(newResultEx); }
protected override void PostTokenRequest(AuthenticationResultEx resultEx) { base.PostTokenRequest(resultEx); UserInfo userInfo = resultEx.Result.UserInfo; this.UniqueId = (userInfo == null) ? null : userInfo.UniqueId; this.DisplayableId = (userInfo == null) ? null : userInfo.DisplayableId; if (!ADALScopeHelper.IsNullOrEmpty(resultEx.ScopeInResponse)) { // This sets the Handler's Scope to the most recent loop, the id_token, or scope=openid. // ADAL then tries to return an AuthResult with this scope from the cache. // What's more, ADAL tries to load scope "openid" from the cache and misses b/c the scope was cached as the clientId. //this.Scope = resultEx.ScopeInResponse; //PlatformPlugin.Logger.Verbose(this.CallState, "Resource value in the token response was used for storing tokens in the cache"); } // If resource is not passed as an argument and is not returned by STS either, // we cannot store the token in the cache with null resource. // TODO: Store refresh token though if STS supports MRRT. this.StoreToCache = this.StoreToCache && (!ADALScopeHelper.IsNullOrEmpty(this.Scope)); }
private void UpdateCachedMrrtRefreshTokens(AuthenticationResultEx result, string clientId, TokenSubjectType subjectType) { lock (cacheLock) { if (result.Result.UserInfo != null && result.IsMultipleResourceRefreshToken) { //pass null for authority to update the token for all the tenants List <KeyValuePair <TokenCacheKey, AuthenticationResultEx> > mrrtItems = this.QueryCache(null, clientId, subjectType, result.Result.UserInfo.UniqueId, result.Result.UserInfo.DisplayableId, null) .Where(p => p.Value.IsMultipleResourceRefreshToken) .ToList(); foreach (KeyValuePair <TokenCacheKey, AuthenticationResultEx> mrrtItem in mrrtItems) { mrrtItem.Value.RefreshToken = result.RefreshToken; } } } }
protected async Task <AuthenticationResultEx> SendTokenRequestByRefreshTokenAsync(string refreshToken) { var requestParameters = new DictionaryRequestParameters(this.Resource, this.ClientKey); requestParameters[OAuthParameter.GrantType] = OAuthGrantType.RefreshToken; requestParameters[OAuthParameter.RefreshToken] = refreshToken; // Commented due to a problem with AD not allowing scopes. Please, see http://stackoverflow.com/questions/42208919/refresh-tokens-with-adfs-3-0-adal-web-api-and-xamarin //requestParameters[OAuthParameter.Scope] = OAuthValue.ScopeOpenId; AuthenticationResultEx result = await this.SendHttpMessageAsync(requestParameters).ConfigureAwait(false); if (result.RefreshToken == null) { result.RefreshToken = refreshToken; PlatformPlugin.Logger.Verbose(this.CallState, "Refresh token was missing from the token refresh response, so the refresh token in the request is returned instead"); } return(result); }
protected override void PostTokenRequest(AuthenticationResultEx resultEx) { base.PostTokenRequest(resultEx); if ((this.DisplayableId == null && this.UniqueId == null) || this.UserIdentifierType == UserIdentifierType.OptionalDisplayableId) { return; } string uniqueId = (resultEx.Result.UserInfo != null && resultEx.Result.UserInfo.UniqueId != null) ? resultEx.Result.UserInfo.UniqueId : "NULL"; string displayableId = (resultEx.Result.UserInfo != null) ? resultEx.Result.UserInfo.DisplayableId : "NULL"; if (this.UserIdentifierType == UserIdentifierType.UniqueId && string.Compare(uniqueId, this.UniqueId, StringComparison.Ordinal) != 0) { throw new AdalUserMismatchException(this.UniqueId, uniqueId); } if (this.UserIdentifierType == UserIdentifierType.RequiredDisplayableId && string.Compare(displayableId, this.DisplayableId, StringComparison.OrdinalIgnoreCase) != 0) { throw new AdalUserMismatchException(this.DisplayableId, displayableId); } }
public AuthenticationResultEx GetResult(DateTimeOffset expiresOn, DateTimeOffset extendedExpiresOn) { AuthenticationResultEx resultEx; if (this.AccessToken != null) { var result = new AuthenticationResult(this.TokenType, this.AccessToken, expiresOn, extendedExpiresOn); IdToken idToken = IdToken.Parse(this.IdTokenString); if (idToken != null) { string tenantId = idToken.TenantId; string uniqueId = null; string displayableId = null; if (!string.IsNullOrWhiteSpace(idToken.ObjectId)) { uniqueId = idToken.ObjectId; } else if (!string.IsNullOrWhiteSpace(idToken.Subject)) { uniqueId = idToken.Subject; } if (!string.IsNullOrWhiteSpace(idToken.UPN)) { displayableId = idToken.UPN; } else if (!string.IsNullOrWhiteSpace(idToken.Email)) { displayableId = idToken.Email; } string givenName = idToken.GivenName; string familyName = idToken.FamilyName; string identityProvider = idToken.IdentityProvider ?? idToken.Issuer; DateTimeOffset?passwordExpiresOffest = null; if (idToken.PasswordExpiration > 0) { passwordExpiresOffest = DateTime.UtcNow + TimeSpan.FromSeconds(idToken.PasswordExpiration); } Uri changePasswordUri = null; if (!string.IsNullOrEmpty(idToken.PasswordChangeUrl)) { changePasswordUri = new Uri(idToken.PasswordChangeUrl); } result.UpdateTenantAndUserInfo(tenantId, this.IdTokenString, new UserInfo { UniqueId = uniqueId, DisplayableId = displayableId, GivenName = givenName, FamilyName = familyName, IdentityProvider = identityProvider, PasswordExpiresOn = passwordExpiresOffest, PasswordChangeUrl = changePasswordUri }); result.Authority = Authority; } resultEx = new AuthenticationResultEx { Result = result, RefreshToken = this.RefreshToken, // This is only needed for AcquireTokenByAuthorizationCode in which parameter resource is optional and we need // to get it from the STS response. ResourceInResponse = this.Resource }; } else if (this.Error != null) { throw new AdalServiceException(this.Error, this.ErrorDescription); } else { throw new AdalServiceException(AdalError.Unknown, AdalErrorMessage.Unknown); } return(resultEx); }
public void AcquireToken(IDictionary <string, string> brokerPayload) { if (brokerPayload.ContainsKey("broker_install_url")) { string url = brokerPayload["broker_install_url"]; Uri uri = new Uri(url); string query = uri.Query; if (query.StartsWith("?")) { query = query.Substring(1); } Dictionary <string, string> keyPair = EncodingHelper.ParseKeyValueList(query, '&', true, false, null); PlatformParameters pp = PlatformParameters as PlatformParameters; pp.CallerActivity.StartActivity(new Intent(Intent.ActionView, Android.Net.Uri.Parse(keyPair["app_link"]))); throw new AdalException(AdalErrorAndroidEx.BrokerApplicationRequired, AdalErrorMessageAndroidEx.BrokerApplicationRequired); } Context mContext = Application.Context; AuthenticationRequest request = new AuthenticationRequest(brokerPayload); PlatformParameters platformParams = PlatformParameters as PlatformParameters; // BROKER flow intercepts here // cache and refresh call happens through the authenticator service if (mBrokerProxy.VerifyUser(request.LoginHint, request.UserId)) { CallState.Logger.Verbose(null, "It switched to broker for context: " + mContext.PackageName); request.BrokerAccountName = request.LoginHint; // Don't send background request, if prompt flag is always or // refresh_session bool hasAccountNameOrUserId = !string.IsNullOrEmpty(request.BrokerAccountName) || !string.IsNullOrEmpty(request.UserId); if (string.IsNullOrEmpty(request.Claims) && hasAccountNameOrUserId) { CallState.Logger.Verbose(null, "User is specified for background token request"); resultEx = mBrokerProxy.GetAuthTokenInBackground(request, platformParams.CallerActivity); } else { CallState.Logger.Verbose(null, "User is not specified for background token request"); } if (resultEx != null && resultEx.Result != null && !string.IsNullOrEmpty(resultEx.Result.AccessToken)) { CallState.Logger.Verbose(null, "Token is returned from background call "); readyForResponse.Release(); return; } // Launch broker activity // if cache and refresh request is not handled. // Initial request to authenticator needs to launch activity to // record calling uid for the account. This happens for Prompt auto // or always behavior. CallState.Logger.Verbose(null, "Token is not returned from backgroud call"); // Only happens with callback since silent call does not show UI CallState.Logger.Verbose(null, "Launch activity for Authenticator"); CallState.Logger.Verbose(null, "Starting Authentication Activity"); if (resultEx == null) { CallState.Logger.Verbose(null, "Initial request to authenticator"); // Log the initial request but not force a prompt } if (brokerPayload.ContainsKey("silent_broker_flow")) { throw new AdalSilentTokenAcquisitionException(); } // onActivityResult will receive the response // Activity needs to launch to record calling app for this // account Intent brokerIntent = mBrokerProxy.GetIntentForBrokerActivity(request, platformParams.CallerActivity); if (brokerIntent != null) { try { CallState.Logger.Verbose(null, "Calling activity pid:" + Android.OS.Process.MyPid() + " tid:" + Android.OS.Process.MyTid() + "uid:" + Android.OS.Process.MyUid()); platformParams.CallerActivity.StartActivityForResult(brokerIntent, 1001); } catch (ActivityNotFoundException e) { CallState.Logger.Error(null, e); } } } else { throw new AdalException(AdalErrorAndroidEx.NoBrokerAccountFound, "Add requested account as a Workplace account via Settings->Accounts or set UseBroker=true."); } }
public AuthenticationResultEx GetResult() { AuthenticationResultEx resultEx; if (this.AccessToken != null) { DateTimeOffset expiresOn = DateTime.UtcNow + TimeSpan.FromSeconds(this.ExpiresIn); var result = new AuthenticationResult(this.TokenType, this.AccessToken, expiresOn); IdToken idToken = IdToken.Parse(this.IdTokenString); if (idToken != null) { string tenantId = idToken.TenantId; string uniqueId = null; string displayableId = null; if (!string.IsNullOrWhiteSpace(idToken.ObjectId)) { uniqueId = idToken.ObjectId; } else if (!string.IsNullOrWhiteSpace(idToken.Subject)) { uniqueId = idToken.Subject; } if (!string.IsNullOrWhiteSpace(idToken.UPN)) { displayableId = idToken.UPN; } else if (!string.IsNullOrWhiteSpace(idToken.Email)) { displayableId = idToken.Email; } string givenName = idToken.GivenName; string familyName = idToken.FamilyName; string identityProvider = idToken.IdentityProvider ?? idToken.Issuer; DateTimeOffset? passwordExpiresOffest = null; if (idToken.PasswordExpiration > 0) { passwordExpiresOffest = DateTime.UtcNow + TimeSpan.FromSeconds(idToken.PasswordExpiration); } Uri changePasswordUri = null; if (!string.IsNullOrEmpty(idToken.PasswordChangeUrl)) { changePasswordUri = new Uri(idToken.PasswordChangeUrl); } result.UpdateTenantAndUserInfo(tenantId, this.IdTokenString, new UserInfo { UniqueId = uniqueId, DisplayableId = displayableId, GivenName = givenName, FamilyName = familyName, IdentityProvider = identityProvider, PasswordExpiresOn = passwordExpiresOffest, PasswordChangeUrl = changePasswordUri }); } resultEx = new AuthenticationResultEx { Result = result, RefreshToken = this.RefreshToken, // This is only needed for AcquireTokenByAuthorizationCode in which parameter resource is optional and we need // to get it from the STS response. ResourceInResponse = this.Resource }; } else if (this.Error != null) { throw new AdalServiceException(this.Error, this.ErrorDescription); } else { throw new AdalServiceException(AdalError.Unknown, AdalErrorMessage.Unknown); } return resultEx; }
private async Task<AuthenticationResultEx> RefreshAccessTokenAsync(AuthenticationResultEx result) { AuthenticationResultEx newResultEx = null; if (this.Resource != null) { PlatformPlugin.Logger.Verbose(this.CallState, "Refreshing access token..."); try { newResultEx = await this.SendTokenRequestByRefreshTokenAsync(result.RefreshToken); this.Authenticator.UpdateTenantId(result.Result.TenantId); if (newResultEx.Result.IdToken == null) { // If Id token is not returned by token endpoint when refresh token is redeemed, we should copy tenant and user information from the cached token. newResultEx.Result.UpdateTenantAndUserInfo(result.Result.TenantId, result.Result.IdToken, result.Result.UserInfo); } } catch (AdalException ex) { AdalServiceException serviceException = ex as AdalServiceException; if (serviceException != null && serviceException.ErrorCode == "invalid_request") { throw new AdalServiceException( AdalError.FailedToRefreshToken, AdalErrorMessage.FailedToRefreshToken + ". " + serviceException.Message, serviceException.ServiceErrorCodes, serviceException.InnerException); } newResultEx = null; } } return newResultEx; }
public async Task <AuthenticationResult> RunAsync() { bool notifiedBeforeAccessCache = false; AuthenticationResultEx extendedLifetimeResultEx = null; try { await this.PreRunAsync().ConfigureAwait(false); if (this.LoadFromCache) { CallState.Logger.Verbose(CallState, "Loading from cache."); CacheQueryData.Authority = Authenticator.Authority; CacheQueryData.Resource = this.Resource; CacheQueryData.ClientId = this.ClientKey.ClientId; CacheQueryData.SubjectType = this.TokenSubjectType; CacheQueryData.UniqueId = this.UniqueId; CacheQueryData.DisplayableId = this.DisplayableId; this.NotifyBeforeAccessCache(); notifiedBeforeAccessCache = true; ResultEx = this.tokenCache.LoadFromCache(CacheQueryData, this.CallState); extendedLifetimeResultEx = ResultEx; if (ResultEx?.Result != null && ((ResultEx.Result.AccessToken == null && ResultEx.RefreshToken != null) || (ResultEx.Result.ExtendedLifeTimeToken && ResultEx.RefreshToken != null))) { ResultEx = await this.RefreshAccessTokenAsync(ResultEx).ConfigureAwait(false); if (ResultEx != null && ResultEx.Exception == null) { StoreResultExToCache(ref notifiedBeforeAccessCache); } } } if (ResultEx == null || ResultEx.Exception != null) { if (brokerHelper.CanInvokeBroker) { ResultEx = await brokerHelper.AcquireTokenUsingBroker(brokerParameters).ConfigureAwait(false); } else { await this.PreTokenRequest().ConfigureAwait(false); // check if broker app installation is required for authentication. await CheckAndAcquireTokenUsingBroker().ConfigureAwait(false); } //broker token acquisition failed if (ResultEx != null && ResultEx.Exception != null) { throw ResultEx.Exception; } this.PostTokenRequest(ResultEx); StoreResultExToCache(ref notifiedBeforeAccessCache); } await this.PostRunAsync(ResultEx.Result).ConfigureAwait(false); return(ResultEx.Result); } catch (Exception ex) { CallState.Logger.Error(this.CallState, ex); if (client != null && client.Resiliency && extendedLifetimeResultEx != null) { CallState.Logger.Information(this.CallState, "Refreshing AT failed either due to one of these :- Internal Server Error,Gateway Timeout and Service Unavailable.Hence returning back stale AT"); return(extendedLifetimeResultEx.Result); } throw; } finally { if (notifiedBeforeAccessCache) { this.NotifyAfterAccessCache(); } } }
internal AuthenticationResultEx LoadFromCache(string authority, string resource, string clientId, TokenSubjectType subjectType, string uniqueId, string displayableId, CallState callState) { PlatformPlugin.Logger.Verbose(callState, "Looking up cache for a token..."); AuthenticationResultEx resultEx = null; KeyValuePair<TokenCacheKey, AuthenticationResultEx>? kvp = this.LoadSingleItemFromCache(authority, resource, clientId, subjectType, uniqueId, displayableId, callState); if (kvp.HasValue) { TokenCacheKey cacheKey = kvp.Value.Key; resultEx = kvp.Value.Value; bool tokenNearExpiry = (resultEx.Result.ExpiresOn <= DateTime.UtcNow + TimeSpan.FromMinutes(ExpirationMarginInMinutes)); if (tokenNearExpiry) { resultEx.Result.AccessToken = null; PlatformPlugin.Logger.Verbose(callState, "An expired or near expiry token was found in the cache"); } else if (!cacheKey.ResourceEquals(resource)) { PlatformPlugin.Logger.Verbose(callState, string.Format("Multi resource refresh token for resource '{0}' will be used to acquire token for '{1}'", cacheKey.Resource, resource)); var newResultEx = new AuthenticationResultEx { Result = new AuthenticationResult(null, null, DateTimeOffset.MinValue), RefreshToken = resultEx.RefreshToken, ResourceInResponse = resultEx.ResourceInResponse }; newResultEx.Result.UpdateTenantAndUserInfo(resultEx.Result.TenantId, resultEx.Result.IdToken, resultEx.Result.UserInfo); resultEx = newResultEx; } else { PlatformPlugin.Logger.Verbose(callState, string.Format("{0} minutes left until token in cache expires", (resultEx.Result.ExpiresOn - DateTime.UtcNow).TotalMinutes)); } if (resultEx.Result.AccessToken == null && resultEx.RefreshToken == null) { this.tokenCacheDictionary.Remove(cacheKey); PlatformPlugin.Logger.Information(callState, "An old item was removed from the cache"); this.HasStateChanged = true; resultEx = null; } if (resultEx != null) { PlatformPlugin.Logger.Information(callState, "A matching item (access token or refresh token or both) was found in the cache"); } } else { PlatformPlugin.Logger.Information(callState, "No matching token was found in the cache"); } return resultEx; }
private static void AddToDictionary(TokenCache tokenCache, TokenCacheKey key, AuthenticationResultEx value) { tokenCache.OnBeforeAccess(new TokenCacheNotificationArgs { TokenCache = tokenCache }); tokenCache.OnBeforeWrite(new TokenCacheNotificationArgs { TokenCache = tokenCache }); tokenCache.tokenCacheDictionary.Add(key, value); tokenCache.HasStateChanged = true; tokenCache.OnAfterAccess(new TokenCacheNotificationArgs { TokenCache = tokenCache }); }
public AuthenticationResultEx GetAuthTokenInBackground(AuthenticationRequest request, Activity callerActivity) { AuthenticationResultEx authResult = null; VerifyNotOnMainThread(); // if there is not any user added to account, it returns empty Account targetAccount = null; Account[] accountList = mAcctManager .GetAccountsByType(BrokerConstants.BrokerAccountType); if (!string.IsNullOrEmpty(request.BrokerAccountName)) { targetAccount = FindAccount(request.BrokerAccountName, accountList); } else { try { UserInfo[] users = GetBrokerUsers(); UserInfo matchingUser = FindUserInfo(request.UserId, users); if (matchingUser != null) { targetAccount = FindAccount(matchingUser.DisplayableId, accountList); } } catch (Exception e) { CallState.Logger.Error(null, e); } } if (targetAccount != null) { Bundle brokerOptions = GetBrokerOptions(request); // blocking call to get token from cache or refresh request in // background at Authenticator IAccountManagerFuture result = null; try { // It does not expect activity to be launched. // AuthenticatorService is handling the request at // AccountManager. // result = mAcctManager.GetAuthToken(targetAccount, BrokerConstants.AuthtokenType, brokerOptions, false, null /* * set to null to avoid callback */, new Handler(callerActivity.MainLooper)); // Making blocking request here CallState.Logger.Verbose(null, "Received result from Authenticator"); Bundle bundleResult = (Bundle)result.GetResult(10000, TimeUnit.Milliseconds); // Authenticator should throw OperationCanceledException if // token is not available authResult = GetResultFromBrokerResponse(bundleResult); } catch (OperationCanceledException e) { CallState.Logger.Error(null, e); } catch (AuthenticatorException e) { CallState.Logger.Error(null, e); } catch (Exception e) { // Authenticator gets problem from webrequest or file read/write /* Logger.e(TAG, "Authenticator cancels the request", "", * ADALError.BROKER_AUTHENTICATOR_IO_EXCEPTION);*/ CallState.Logger.Error(null, e); } CallState.Logger.Verbose(null, "Returning result from Authenticator"); return(authResult); } else { CallState.Logger.Verbose(null, "Target account is not found"); } return(null); }
private static bool AreAuthenticationResultExsEqual(AuthenticationResultEx resultEx1, AuthenticationResultEx resultEx2) { return AreAuthenticationResultsEqual(resultEx1.Result, resultEx2.Result) && resultEx1.RefreshToken == resultEx2.RefreshToken && resultEx1.IsMultipleResourceRefreshToken == resultEx2.IsMultipleResourceRefreshToken; }
internal void StoreToCache(AuthenticationResultEx result, string authority, string[] scope, string clientId, TokenSubjectType subjectType, CallState callState) { PlatformPlugin.Logger.Verbose(callState, "Storing token in the cache..."); if (ADALScopeHelper.IsNullOrEmpty(scope) || ADALScopeHelper.CreateSetFromArray(scope).Contains("openid")) { scope = new[] {clientId}; } string uniqueId = (result.Result.UserInfo != null) ? result.Result.UserInfo.UniqueId : null; string displayableId = (result.Result.UserInfo != null) ? result.Result.UserInfo.DisplayableId : null; this.OnBeforeWrite(new TokenCacheNotificationArgs { Scope = scope, ClientId = clientId, UniqueId = uniqueId, DisplayableId = displayableId }); TokenCacheKey tokenCacheKey = new TokenCacheKey(authority, scope, clientId, subjectType, result.Result.UserInfo); // First identify all potential tokens. List<KeyValuePair<TokenCacheKey, AuthenticationResultEx>> items = this.QueryCache(authority, clientId, subjectType, uniqueId, displayableId); List<KeyValuePair<TokenCacheKey, AuthenticationResultEx>> itemsToRemove = items.Where(p => p.Key.ScopeIntersects(scope)).ToList(); if (!itemsToRemove.Any()) { this.tokenCacheDictionary[tokenCacheKey] = result; PlatformPlugin.Logger.Verbose(callState, "An item was stored in the cache"); } else { //remove all intersections foreach (var itemToRemove in itemsToRemove) { this.tokenCacheDictionary.Remove(itemToRemove); } this.tokenCacheDictionary[tokenCacheKey] = result; PlatformPlugin.Logger.Verbose(callState, "An item was updated in the cache"); } this.UpdateCachedMrrtRefreshTokens(result, authority, clientId, subjectType); this.HasStateChanged = true; }
internal static void TokenCacheCapacityTest() { var tokenCache = new TokenCache(); tokenCache.Clear(); const int MaxItemCount = 100; const int MaxFieldSize = 256; TokenCacheKey[] keys = new TokenCacheKey[MaxItemCount]; AuthenticationResultEx[] values = new AuthenticationResultEx[MaxItemCount]; for (int i = 0; i < MaxItemCount; i++) { keys[i] = GenerateRandomTokenCacheKey(MaxFieldSize); values[i] = CreateCacheValue(null, null); AddToDictionary(tokenCache, keys[i], values[i]); } Verify.AreEqual(MaxItemCount, tokenCache.Count); for (int i = 0; i < MaxItemCount; i++) { AuthenticationResultEx cacheValue; int index = MaxItemCount - i - 1; Verify.IsTrue(tokenCache.tokenCacheDictionary.TryGetValue(keys[index], out cacheValue)); Verify.AreEqual(values[index], cacheValue); RemoveFromDictionary(tokenCache, keys[index]); Verify.AreEqual(index, tokenCache.Count); } tokenCache.Clear(); }
internal void StoreToCache(AuthenticationResultEx result, string authority, string resource, string clientId, TokenSubjectType subjectType, CallState callState) { PlatformPlugin.Logger.Verbose(callState, "Storing token in the cache..."); string uniqueId = (result.Result.UserInfo != null) ? result.Result.UserInfo.UniqueId : null; string displayableId = (result.Result.UserInfo != null) ? result.Result.UserInfo.DisplayableId : null; this.OnBeforeWrite(new TokenCacheNotificationArgs { Resource = resource, ClientId = clientId, UniqueId = uniqueId, DisplayableId = displayableId }); TokenCacheKey tokenCacheKey = new TokenCacheKey(authority, resource, clientId, subjectType, result.Result.UserInfo); this.tokenCacheDictionary[tokenCacheKey] = result; PlatformPlugin.Logger.Verbose(callState, "An item was stored in the cache"); this.UpdateCachedMrrtRefreshTokens(result, authority, clientId, subjectType); this.HasStateChanged = true; }
protected virtual void PostTokenRequest(AuthenticationResultEx result) { this.Authenticator.UpdateTenantId(result.Result.TenantId); }
internal AuthenticationResultEx LoadFromCache(string authority, string[] scope, string clientId, TokenSubjectType subjectType, string uniqueId, string displayableId, CallState callState) { PlatformPlugin.Logger.Verbose(callState, "Looking up cache for a token..."); if (ADALScopeHelper.CreateSetFromArray(scope).Contains(clientId)) { PlatformPlugin.Logger.Verbose(callState, "Looking for id token..."); } AuthenticationResultEx resultEx = null; //get either a matching token or an MRRT supported RT KeyValuePair<TokenCacheKey, AuthenticationResultEx>? kvp = this.LoadSingleItemFromCache(authority, scope, clientId, subjectType, uniqueId, displayableId, callState); if (kvp.HasValue) { TokenCacheKey cacheKey = kvp.Value.Key; resultEx = kvp.Value.Value; bool tokenNearExpiry = (resultEx.Result.ExpiresOn <= DateTime.UtcNow + TimeSpan.FromMinutes(ExpirationMarginInMinutes)); if (tokenNearExpiry) { resultEx.Result.Token = null; PlatformPlugin.Logger.Verbose(callState, "An expired or near expiry token was found in the cache"); } else if (!cacheKey.ScopeContains(scope)) { //requested scope are not a subset. PlatformPlugin.Logger.Verbose(callState, string.Format("Refresh token for scope '{0}' will be used to acquire token for '{1}'", ADALScopeHelper.CreateSingleStringFromArray(cacheKey.Scope), ADALScopeHelper.CreateSingleStringFromArray(scope))); var newResultEx = new AuthenticationResultEx { Result = new AuthenticationResult(null, null, DateTimeOffset.MinValue), RefreshToken = resultEx.RefreshToken, ScopeInResponse = resultEx.ScopeInResponse }; newResultEx.Result.UpdateTenantAndUserInfo(resultEx.Result.TenantId, resultEx.Result.ProfileInfo, resultEx.Result.UserInfo); resultEx = newResultEx; } else { PlatformPlugin.Logger.Verbose(callState, string.Format("{0} minutes left until token in cache expires", (resultEx.Result.ExpiresOn - DateTime.UtcNow).TotalMinutes)); } if (resultEx.Result.Token == null && resultEx.RefreshToken == null) { this.tokenCacheDictionary.Remove(cacheKey); PlatformPlugin.Logger.Information(callState, "An old item was removed from the cache"); this.HasStateChanged = true; resultEx = null; } if (resultEx != null) { PlatformPlugin.Logger.Information(callState, "A matching item (access token or refresh token or both) was found in the cache"); } } else { PlatformPlugin.Logger.Information(callState, "No matching token was found in the cache"); } return resultEx; }
internal AuthenticationResultEx LoadFromCache(CacheQueryData cacheQueryData, CallState callState) { lock (cacheLock) { PlatformPlugin.Logger.Verbose(callState, "Looking up cache for a token..."); AuthenticationResultEx resultEx = null; KeyValuePair <TokenCacheKey, AuthenticationResultEx>?kvp = this.LoadSingleItemFromCache(cacheQueryData, callState); if (kvp.HasValue) { TokenCacheKey cacheKey = kvp.Value.Key; resultEx = kvp.Value.Value.Clone(); bool tokenNearExpiry = (resultEx.Result.ExpiresOn <= DateTime.UtcNow + TimeSpan.FromMinutes(ExpirationMarginInMinutes)); bool tokenExtendedLifeTimeExpired = (resultEx.Result.ExtendedExpiresOn <= DateTime.UtcNow); //check for cross-tenant authority if (!cacheKey.Authority.Equals(cacheQueryData.Authority)) { // this is a cross-tenant result. use RT only resultEx.Result.AccessToken = null; PlatformPlugin.Logger.Information(callState, "Cross Tenant refresh token was found in the cache"); } else if (tokenNearExpiry && !cacheQueryData.ExtendedLifeTimeEnabled) { resultEx.Result.AccessToken = null; PlatformPlugin.Logger.Information(callState, "An expired or near expiry token was found in the cache"); } else if (!cacheKey.ResourceEquals(cacheQueryData.Resource)) { PlatformPlugin.Logger.Information(callState, string.Format(CultureInfo.CurrentCulture, "Multi resource refresh token for resource '{0}' will be used to acquire token for '{1}'", cacheKey.Resource, cacheQueryData.Resource)); var newResultEx = new AuthenticationResultEx { Result = new AuthenticationResult(null, null, DateTimeOffset.MinValue), RefreshToken = resultEx.RefreshToken, ResourceInResponse = resultEx.ResourceInResponse }; newResultEx.Result.UpdateTenantAndUserInfo(resultEx.Result.TenantId, resultEx.Result.IdToken, resultEx.Result.UserInfo); resultEx = newResultEx; } else if (!tokenExtendedLifeTimeExpired && cacheQueryData.ExtendedLifeTimeEnabled && tokenNearExpiry) { resultEx.Result.ExtendedLifeTimeToken = true; resultEx.Result.ExpiresOn = resultEx.Result.ExtendedExpiresOn; PlatformPlugin.Logger.Information(callState, "The extendedLifeTime is enabled and a stale AT with extendedLifeTimeEnabled is returned."); } else if (tokenExtendedLifeTimeExpired) { resultEx.Result.AccessToken = null; PlatformPlugin.Logger.Information(callState, "The AT has expired its ExtendedLifeTime"); } else { PlatformPlugin.Logger.Information(callState, string.Format(CultureInfo.CurrentCulture, "{0} minutes left until token in cache expires", (resultEx.Result.ExpiresOn - DateTime.UtcNow).TotalMinutes)); } if (resultEx.Result.AccessToken == null && resultEx.RefreshToken == null) { this.tokenCacheDictionary.Remove(cacheKey); PlatformPlugin.Logger.Information(callState, "An old item was removed from the cache"); this.HasStateChanged = true; resultEx = null; } if (resultEx != null) { PlatformPlugin.Logger.Information(callState, "A matching item (access token or refresh token or both) was found in the cache"); } } else { PlatformPlugin.Logger.Information(callState, "No matching token was found in the cache"); } return(resultEx); } }
private static void VerifyAuthenticationResultExsAreNotEqual(AuthenticationResultEx resultEx1, AuthenticationResultEx resultEx2) { Verify.IsFalse(AreAuthenticationResultExsEqual(resultEx1, resultEx2)); }
private void UpdateCachedMrrtRefreshTokens(AuthenticationResultEx result, string authority, string clientId, TokenSubjectType subjectType) { if (result.Result.UserInfo != null && result.IsMultipleResourceRefreshToken) { List<KeyValuePair<TokenCacheKey, AuthenticationResultEx>> mrrtItems = this.QueryCache(authority, clientId, subjectType, result.Result.UserInfo.UniqueId, result.Result.UserInfo.DisplayableId) .Where(p => p.Value.IsMultipleResourceRefreshToken) .ToList(); foreach (KeyValuePair<TokenCacheKey, AuthenticationResultEx> mrrtItem in mrrtItems) { mrrtItem.Value.RefreshToken = result.RefreshToken; } } }