Example #1
0
        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"]);
            }
        }
Example #2
0
        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;
                }
            });
        }
Example #6
0
        // 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));
            }
        }
Example #7
0
        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);
            }
        }