public async Task ATS_AccountWithWamId_Async() { // Arrange using (MockHttpAndServiceBundle harness = CreateTestHarness()) { _webAccountProviderFactory.ClearReceivedCalls(); var wamAccountProvider = new WebAccountProvider("id", "*****@*****.**", null); var extraQP = new Dictionary <string, string>() { { "extraQp1", "extraVal1" }, { "instance_aware", "true" } }; var requestParams = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant, extraQueryParameters: extraQP, validateAuthority: true); // AAD requestParams.UserConfiguredAuthority = Authority.CreateAuthority("https://login.microsoftonline.com/organizations"); requestParams.Account = new Account( $"{TestConstants.Uid}.{TestConstants.Utid}", TestConstants.DisplayableId, null, new Dictionary <string, string>() { { TestConstants.ClientId, "wam_id_1" } }); // account has wam_id! var webAccount = new WebAccount(wamAccountProvider, "*****@*****.**", WebAccountState.Connected); var webTokenRequest = new WebTokenRequest(wamAccountProvider); var webTokenResponseWrapper = Substitute.For <IWebTokenRequestResultWrapper>(); webTokenResponseWrapper.ResponseStatus.Returns(WebTokenRequestStatus.Success); var webTokenResponse = new WebTokenResponse(); webTokenResponseWrapper.ResponseData.Returns(new List <WebTokenResponse>() { webTokenResponse }); _webAccountProviderFactory.GetAccountProviderAsync(null).ReturnsForAnyArgs(Task.FromResult(wamAccountProvider)); _wamProxy.FindAccountAsync(Arg.Any <WebAccountProvider>(), "wam_id_1").Returns(Task.FromResult(webAccount)); _aadPlugin.CreateWebTokenRequestAsync( wamAccountProvider, requestParams, isForceLoginPrompt: false, isAccountInWam: true, isInteractive: false) .Returns(Task.FromResult(webTokenRequest)); var atsParams = new AcquireTokenSilentParameters(); _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest). Returns(Task.FromResult(webTokenResponseWrapper)); _aadPlugin.ParseSuccesfullWamResponse(webTokenResponse).Returns(_msalTokenResponse); // Act var result = await _wamBroker.AcquireTokenSilentAsync(requestParams, atsParams).ConfigureAwait(false); // Assert Assert.AreSame(_msalTokenResponse, result); Assert.AreEqual("yes", webTokenRequest.Properties["validateAuthority"]); Assert.AreEqual("extraVal1", webTokenRequest.Properties["extraQp1"]); // Although at the time of writing, MSAL does not support instance aware ... // WAM does support it but the param is different - discovery=home Assert.AreEqual("home", webTokenRequest.Properties["discover"]); Assert.AreEqual("https://login.microsoftonline.com/organizations/", webTokenRequest.Properties["authority"]); } }
public MsalTokenResponse ParseSuccessfullWamResponse( WebTokenResponse webTokenResponse, out Dictionary <string, string> allProperties) { allProperties = new Dictionary <string, string>(8, StringComparer.OrdinalIgnoreCase); if (!webTokenResponse.Properties.TryGetValue("TokenExpiresOn", out string expiresOn)) { _logger.Warning("Result from WAM does not have expiration. Marking access token as expired."); expiresOn = null; } if (!webTokenResponse.Properties.TryGetValue("ExtendedLifetimeToken", out string extendedExpiresOn)) { extendedExpiresOn = null; } if (!webTokenResponse.Properties.TryGetValue("Authority", out string authority)) { _logger.Error("Result from WAM does not have authority."); return(new MsalTokenResponse() { Error = "no_authority_in_wam_response", ErrorDescription = "No authority in WAM response" }); } if (!webTokenResponse.Properties.TryGetValue("correlationId", out string correlationId)) { _logger.Warning("No correlation ID in response"); correlationId = null; } bool hasIdToken = webTokenResponse.Properties.TryGetValue("wamcompat_id_token", out string idToken); _logger.Info("Result from WAM has id token? " + hasIdToken); bool hasClientInfo = webTokenResponse.Properties.TryGetValue("wamcompat_client_info", out string clientInfo); _logger.Info("Result from WAM has client info? " + hasClientInfo); bool hasScopes = webTokenResponse.Properties.TryGetValue("wamcompat_scopes", out string scopes); _logger.InfoPii("Result from WAM scopes: " + scopes, "Result from WAM has scopes? " + hasScopes); foreach (var kvp in webTokenResponse.Properties) { allProperties.Add(kvp.Key, kvp.Value); } MsalTokenResponse msalTokenResponse = new MsalTokenResponse() { AccessToken = webTokenResponse.Token, IdToken = idToken, CorrelationId = correlationId, Scope = scopes, ExpiresIn = CoreHelpers.GetDurationFromWindowsTimestamp(expiresOn, _logger), ExtendedExpiresIn = CoreHelpers.GetDurationFromWindowsTimestamp(extendedExpiresOn, _logger), ClientInfo = clientInfo, TokenType = "Bearer", WamAccountId = webTokenResponse?.WebAccount?.Id, TokenSource = TokenSource.Broker }; return(msalTokenResponse); }
public MsalTokenResponse ParseSuccessfullWamResponse(WebTokenResponse webTokenResponse, out Dictionary <string, string> allProperties) { string msaTokens = webTokenResponse.Token; if (string.IsNullOrEmpty(msaTokens)) { throw new MsalServiceException( MsaErrorCode, "Internal error - bad token format, msaTokens was unexpectedly empty"); } string accessToken = null, idToken = null, clientInfo = null, tokenType = null, scopes = null, correlationId = null; long expiresIn = 0; allProperties = new Dictionary <string, string>(8, StringComparer.OrdinalIgnoreCase); foreach (string keyValuePairString in msaTokens.Split('&')) { string[] keyValuePair = keyValuePairString.Split('='); if (keyValuePair.Length != 2) { throw new MsalClientException( MsaErrorCode, "Internal error - bad token response format, expected '=' separated pair"); } allProperties.Add(keyValuePair[0], keyValuePair[1]); if (string.Equals(keyValuePair[0], "access_token", StringComparison.OrdinalIgnoreCase)) { accessToken = keyValuePair[1]; } else if (string.Equals(keyValuePair[0], "id_token", StringComparison.OrdinalIgnoreCase)) { idToken = keyValuePair[1]; } else if (string.Equals(keyValuePair[0], "token_type", StringComparison.OrdinalIgnoreCase)) { tokenType = keyValuePair[1]; } else if (string.Equals(keyValuePair[0], "scope", StringComparison.OrdinalIgnoreCase)) { scopes = keyValuePair[1]; } else if (string.Equals(keyValuePair[0], "client_info", StringComparison.OrdinalIgnoreCase)) { clientInfo = keyValuePair[1]; } else if (string.Equals(keyValuePair[0], "expires_in", StringComparison.OrdinalIgnoreCase)) { expiresIn = long.Parse(keyValuePair[1], CultureInfo.InvariantCulture); } else if (string.Equals(keyValuePair[0], "correlation", StringComparison.OrdinalIgnoreCase)) { correlationId = keyValuePair[1]; } } if (string.IsNullOrEmpty(tokenType) || string.Equals("bearer", tokenType, System.StringComparison.OrdinalIgnoreCase)) { tokenType = "Bearer"; } var responseScopes = scopes?.Replace("%20", " "); MsalTokenResponse msalTokenResponse = new MsalTokenResponse() { AccessToken = accessToken, IdToken = idToken, CorrelationId = correlationId, Scope = responseScopes, ExpiresIn = expiresIn, ExtendedExpiresIn = 0, // not supported on MSA ClientInfo = clientInfo, TokenType = tokenType, WamAccountId = webTokenResponse.WebAccount.Id, TokenSource = TokenSource.Broker }; return(msalTokenResponse); }
public MsalTokenResponse ParseSuccesfullWamResponse(WebTokenResponse webTokenResponse) { string msaTokens = webTokenResponse.Token; if (string.IsNullOrEmpty(msaTokens)) { throw new MsalServiceException( MsaErrorCode, "Internal error - bad token format, msaTokens was unexpectedly empty"); } string accessToken = null, idToken = null, clientInfo = null, tokenType = null, scopes = null, correlationId = null; long expiresIn = 0; foreach (string keyValuePairString in msaTokens.Split('&')) { string[] keyValuePair = keyValuePairString.Split('='); if (keyValuePair.Length != 2) { throw new MsalClientException( MsaErrorCode, "Internal error - bad token response format, expected '=' separated pair"); } if (keyValuePair[0] == "access_token") { accessToken = keyValuePair[1]; } else if (keyValuePair[0] == "id_token") { idToken = keyValuePair[1]; } else if (keyValuePair[0] == "token_type") { tokenType = keyValuePair[1]; } else if (keyValuePair[0] == "scope") { scopes = keyValuePair[1]; } else if (keyValuePair[0] == "client_info") { clientInfo = keyValuePair[1]; } else if (keyValuePair[0] == "expires_in") { expiresIn = long.Parse(keyValuePair[1], CultureInfo.InvariantCulture); } else if (keyValuePair[0] == "correlation") { correlationId = keyValuePair[1]; } //else //{ // // TODO: C++ code saves the remaining properties, but I did not find a reason why // Debug.WriteLine($"{keyValuePair[0]}={keyValuePair[1]}"); //} } if (string.IsNullOrEmpty(tokenType) || string.Equals("bearer", tokenType, System.StringComparison.OrdinalIgnoreCase)) { tokenType = "Bearer"; } if (string.IsNullOrEmpty(scopes)) { throw new MsalClientException( MsaErrorCode, "Internal error - bad token response format, no scopes"); } var responseScopes = scopes.Replace("%20", " "); MsalTokenResponse msalTokenResponse = new MsalTokenResponse() { AccessToken = accessToken, IdToken = idToken, CorrelationId = correlationId, Scope = responseScopes, ExpiresIn = expiresIn, ExtendedExpiresIn = 0, // not supported on MSA ClientInfo = clientInfo, TokenType = tokenType, WamAccountId = webTokenResponse.WebAccount.Id, TokenSource = TokenSource.Broker }; return(msalTokenResponse); }
async public void acquireTokenAsync( string authority, bool validateAuthority, string resourceUrl, string clientId, string redirectUrl, string userId, string extraQueryParams, IPromise promise) { WebAccountProvider authContext; try { authContext = await getOrCreateContext(authority); } catch (Exception ex) { promise.Reject(ex); return; } RunOnDispatcher(async() => { try { WebAccount account = await TryGetUserAccount(authContext); WebTokenRequest wtr = new WebTokenRequest(authContext, "", clientId, WebTokenRequestPromptType.Default); wtr.Properties.Add("resource", resourceUrl); WebTokenRequestResult wtrr; if (account != null) { wtrr = await WebAuthenticationCoreManager.RequestTokenAsync(wtr, account); } else { wtrr = await WebAuthenticationCoreManager.RequestTokenAsync(wtr); } if (wtrr.ResponseStatus == WebTokenRequestStatus.Success) { WebTokenResponse response = wtrr.ResponseData[0]; account = wtrr.ResponseData[0].WebAccount; if (!string.IsNullOrEmpty(account?.Id)) { // store the user's account id so it can be used in successive requests Windows.Storage.ApplicationData.Current.LocalSettings.Values["CurrentUserId"] = account.Id; } var props = new Dictionary <string, string>(response.Properties); AuthenticationResult result = new AuthenticationResult(response.Token, props); promise.Resolve(result); } else { promise.Reject($"{wtrr.ResponseError.ErrorCode}", new Exception(wtrr.ResponseError.ErrorMessage)); } } catch (Exception ex) { promise.Reject(ex); return; } }); }
// Get an access token for the given context and resourceId. An attempt is first made to // acquire the token silently. If that fails, then we try to acquire the token by prompting the user. private async Task BeginGetAuthTokenAsync() { string token = null; // FindAccountProviderAsync returns the WebAccountProvider of an installed plugin // The Provider and Authority specifies the specific plugin // This scenario only supports Microsoft accounts. // The Microsoft account provider is always present in Windows 10 devices, as is the Azure AD plugin. // If a non-installed plugin or incorect identity is specified, FindAccountProviderAsync will return null accountProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync(MicrosoftAccountProviderId, consumerAuthority); // Check if there's a record of the last account used with the app var userID = _settings.Values["userID"]; if (userID != null) { WebTokenRequest webTokenRequest = new WebTokenRequest(accountProvider, AccountScopeRequested, AccountClientId); // Get an account object for the user userAccount = await WebAuthenticationCoreManager.FindAccountAsync(accountProvider, (string)userID); // Ensure that the saved account works for getting the token we need //WebTokenRequestResult webTokenRequestResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest, userAccount); WebTokenRequestResult webTokenRequestResult = await WebAuthenticationCoreManager.GetTokenSilentlyAsync(webTokenRequest, userAccount); if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success || webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.AccountSwitch) { WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0]; userAccount = webTokenResponse.WebAccount; token = webTokenResponse.Token; } else { // The saved account could not be used for getting a token // Make sure that the UX is ready for a new sign in SignOut(); } } else { // There is no recorded user. // Check if a default MSA account has been set already. accountProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync(MicrosoftAccountProviderId); // Check if the returned authority is "consumers" if (accountProvider?.Authority == consumerAuthority) { // If it is, then there’s a default MSA account present and there’s no need to show account control WebTokenRequest webTokenRequest = new WebTokenRequest(accountProvider, AccountScopeRequested, AccountClientId); // This is where most of the magic happens.The provider you specified takes over, trying to use the currently // signed in user(or any account saved on the system) to obtain the token you requested without prompting the user. //WebTokenRequestResult webTokenRequestResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest); WebTokenRequestResult webTokenRequestResult = await WebAuthenticationCoreManager.GetTokenSilentlyAsync(webTokenRequest); if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success) { WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0]; userAccount = webTokenResponse.WebAccount; token = webTokenResponse.Token; } } else { // There is no default account or the returned authority is not "consumer", so we must show account control // The AccountCommandsRequested event triggers before the Accounts settings pane is displayed AccountsSettingsPane.GetForCurrentView().AccountCommandsRequested += OnAccountCommandsRequested; AccountsSettingsPane.Show(); return; } } // We succeeded in getting a valid user. if (userAccount != null) { // save user ID in local storage _settings.Values["userID"] = userAccount.Id; _settings.Values["userEmail"] = userAccount.UserName; OnAuthenticated?.Invoke(this, new AuthenticatedEventArgs(token)); } // We didn't succeed in getting a valid user. Clear the app settings so that another user can sign in. else { SignOut(); OnAuthenticated?.Invoke(this, new AuthenticatedEventArgs(string.Empty)); } }
public async Task FetchTransferTokenAsync() { // Arrange using (MockHttpAndServiceBundle harness = CreateTestHarness()) { var msaProvider = new WebAccountProvider("id", "*****@*****.**", null); Client.Internal.Requests.AuthenticationRequestParameters requestParams = harness.CreateAuthenticationRequestParameters( TestConstants.AuthorityHomeTenant, validateAuthority: true); requestParams.AppConfig.WindowsBrokerOptions = new WindowsBrokerOptions() { MsaPassthrough = true }; var msaRequest = new WebTokenRequest(msaProvider); // step 1 - msa request _msaPlugin.CreateWebTokenRequestAsync(msaProvider, requestParams, false, true, false) .Returns(Task.FromResult(msaRequest)); var webTokenResponseWrapper = Substitute.For <IWebTokenRequestResultWrapper>(); webTokenResponseWrapper.ResponseStatus.Returns(WebTokenRequestStatus.Success); WebAccount accountFromMsaProvider = new WebAccount(msaProvider, "*****@*****.**", WebAccountState.Connected); var webTokenResponse = new WebTokenResponse("v1_token", accountFromMsaProvider); webTokenResponseWrapper.ResponseData.Returns(new List <WebTokenResponse>() { webTokenResponse }); _wamProxy.RequestTokenForWindowAsync(IntPtr.Zero, msaRequest).Returns(webTokenResponseWrapper); // step 2 - we have v1 token and a WebAccount, now get a transfer token var transferTokenRequest = new WebTokenRequest(msaProvider); _msaPlugin .CreateWebTokenRequestAsync( msaProvider, TestConstants.ClientId, MsaPassthroughHandler.TransferTokenScopes) .Returns(Task.FromResult(transferTokenRequest)); var webTokenResponseWrapper2 = Substitute.For <IWebTokenRequestResultWrapper>(); var transferTokenRequestResult = Substitute.For <IWebTokenRequestResultWrapper>(); transferTokenRequestResult.ResponseStatus.Returns(WebTokenRequestStatus.Success); //WebAccount accountFromMsaProvider = new WebAccount(msaProvider, "*****@*****.**", WebAccountState.Connected); var transferTokenResponse = new WebTokenResponse("transfer_token"); webTokenResponseWrapper2.ResponseData.Returns(new List <WebTokenResponse>() { transferTokenResponse }); _msaPlugin.ParseSuccessfullWamResponse(Arg.Any <WebTokenResponse>(), out Arg.Any <Dictionary <string, string> >()) .Returns(x => { x[1] = new Dictionary <string, string>(); (x[1] as Dictionary <string, string>).Add("code", "actual_transfer_token"); return(new MsalTokenResponse()); }); _wamProxy.RequestTokenForWindowAsync(IntPtr.Zero, transferTokenRequest).Returns(webTokenResponseWrapper2); // Act var transferToken = await _msaPassthroughHandler.TryFetchTransferTokenAsync(requestParams, msaProvider) .ConfigureAwait(false); // Assert Assert.AreEqual("actual_transfer_token", transferToken); } }