protected override async Task <AuthenticationResultEx> SendTokenRequestAsync() { TimeSpan timeRemaining = deviceCodeResult.ExpiresOn - DateTimeOffset.UtcNow; AuthenticationResultEx resultEx = null; while (timeRemaining.TotalSeconds > 0) { try { resultEx = await base.SendTokenRequestAsync(); break; } catch (AdalServiceException exc) { if (!exc.ErrorCode.Equals(AdalErrorEx.DeviceCodeAuthorizationPendingError)) { throw; } } await Task.Delay(TimeSpan.FromSeconds(deviceCodeResult.Interval)); 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; } }
protected override async Task <AuthenticationResultEx> SendTokenRequestAsync() { AuthenticationResultEx resultEx = await base.SendTokenRequestAsync(); if (resultEx != null) { resultEx.UserAssertionHash = CacheQueryData.AssertionHash; } return(resultEx); }
public async Task <AuthenticationResultEx> AcquireTokenUsingBroker(IDictionary <string, string> brokerPayload) { resultEx = null; readyForResponse = new SemaphoreSlim(0); try { await Task.Run(() => AcquireToken(brokerPayload)); } catch (Exception exc) { PlatformPlugin.Logger.Error(null, exc); throw; } await readyForResponse.WaitAsync(); 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) { PlatformPlugin.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); } PlatformPlugin.Logger.Information(null, string.Format(CultureInfo.CurrentCulture, "Deserialized {0} items to token cache.", count)); } } }
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); }
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); }
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) .Where(p => p.Value.IsMultipleResourceRefreshToken) .ToList(); foreach (KeyValuePair <TokenCacheKey, AuthenticationResultEx> mrrtItem in mrrtItems) { mrrtItem.Value.RefreshToken = result.RefreshToken; } } } }
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); } }
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); } newResultEx = new AuthenticationResultEx { Exception = ex }; } } return(newResultEx); }
public AuthenticationResultEx GetResult(DateTimeOffset expiresOn) { AuthenticationResultEx resultEx; if (this.AccessToken != null) { 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); }
protected virtual void PostTokenRequest(AuthenticationResultEx result) { this.Authenticator.UpdateTenantId(result.Result.TenantId); }
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)) { PlatformPlugin.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 if (!string.IsNullOrEmpty(request.BrokerAccountName) || !string.IsNullOrEmpty(request.UserId)) { PlatformPlugin.Logger.Verbose(null, "User is specified for background token request"); resultEx = mBrokerProxy.GetAuthTokenInBackground(request, platformParams.CallerActivity); } else { PlatformPlugin.Logger.Verbose(null, "User is not specified for background token request"); } if (resultEx != null && resultEx.Result != null && !string.IsNullOrEmpty(resultEx.Result.AccessToken)) { PlatformPlugin.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. PlatformPlugin.Logger.Verbose(null, "Token is not returned from backgroud call"); // Only happens with callback since silent call does not show UI PlatformPlugin.Logger.Verbose(null, "Launch activity for Authenticator"); PlatformPlugin.Logger.Verbose(null, "Starting Authentication Activity"); if (resultEx == null) { PlatformPlugin.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 { PlatformPlugin.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) { PlatformPlugin.Logger.Error(null, e); } } } else { throw new AdalException(AdalErrorAndroidEx.NoBrokerAccountFound, "Add requested account as a Workplace account via Settings->Accounts or set UseBroker=true."); } }
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; 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(cacheQueryData.Resource)) { PlatformPlugin.Logger.Verbose(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 { PlatformPlugin.Logger.Verbose(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); } }
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) { PlatformPlugin.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 PlatformPlugin.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) { PlatformPlugin.Logger.Error(null, e); } catch (AuthenticatorException e) { PlatformPlugin.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);*/ PlatformPlugin.Logger.Error(null, e); } PlatformPlugin.Logger.Verbose(null, "Returning result from Authenticator"); return(authResult); } else { PlatformPlugin.Logger.Verbose(null, "Target account is not found"); } return(null); }