public async Task AcquireTokenByOboMissMatchUserAssertionsAsync()
        {
            using (var httpManager = new MockHttpManager())
            {
                httpManager.AddInstanceDiscoveryMockHandler();

                AddMockHandlerAadSuccess(httpManager, TestConstants.AuthorityCommonTenant);

                var cca = ConfidentialClientApplicationBuilder
                          .Create(TestConstants.ClientId)
                          .WithClientSecret(TestConstants.ClientSecret)
                          .WithAuthority(TestConstants.AuthorityCommonTenant)
                          .WithHttpManager(httpManager)
                          .BuildConcrete();

                UserAssertion userAssertion = new UserAssertion(TestConstants.DefaultAccessToken);
                var           result        = await cca.AcquireTokenOnBehalfOf(TestConstants.s_scope, userAssertion).ExecuteAsync().ConfigureAwait(false);

                Assert.IsNotNull(result);
                Assert.AreEqual("some-access-token", result.AccessToken);

                //Update user assertions
                TokenCacheHelper.UpdateUserAssertions(cca);

                MockHttpMessageHandler mockTokenRequestHttpHandlerRefresh = AddMockHandlerAadSuccess(httpManager, TestConstants.AuthorityCommonTenant);

                //Access and refresh tokens are have a different user assertion so MSAL should perform OBO.
                result = await cca.AcquireTokenOnBehalfOf(TestConstants.s_scope, userAssertion).ExecuteAsync().ConfigureAwait(false);

                Assert.IsNotNull(result);
                Assert.AreEqual("some-access-token", result.AccessToken);
                Assert.AreEqual(result.AuthenticationResultMetadata.TokenSource, TokenSource.IdentityProvider);
            }
        }
Exemplo n.º 2
0
        //Since this test performs a large number of operations it should not be rerun on other clouds.
        private async Task RunOnBehalfOfTestWithTokenCacheAsync(LabResponse labResponse)
        {
            LabUser user = labResponse.User;
            string  oboHost;
            string  secret;
            string  authority;
            string  publicClientID;
            string  confidentialClientID;

            string[] oboScope;

            oboHost              = PublicCloudHost;
            secret               = _keyVault.GetSecret(TestConstants.MsalOBOKeyVaultUri).Value;
            authority            = TestConstants.AuthorityOrganizationsTenant;
            publicClientID       = PublicCloudPublicClientIDOBO;
            confidentialClientID = PublicCloudConfidentialClientIDOBO;
            oboScope             = s_publicCloudOBOServiceScope;

            //TODO: acquire scenario specific client ids from the lab response

            SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword;
            var          factory        = new HttpSnifferClientFactory();

            var msalPublicClient = PublicClientApplicationBuilder.Create(publicClientID)
                                   .WithAuthority(authority)
                                   .WithRedirectUri(TestConstants.RedirectUri)
                                   .WithTestLogging()
                                   .WithHttpClientFactory(factory)
                                   .Build();

            var authResult = await msalPublicClient.AcquireTokenByUsernamePassword(oboScope, user.Upn, securePassword)
                             .ExecuteAsync()
                             .ConfigureAwait(false);

            var confidentialApp = ConfidentialClientApplicationBuilder
                                  .Create(confidentialClientID)
                                  .WithAuthority(new Uri(oboHost + authResult.TenantId), true)
                                  .WithClientSecret(secret)
                                  .WithTestLogging()
                                  .BuildConcrete();

            var userCacheRecorder = confidentialApp.UserTokenCache.RecordAccess();

            UserAssertion userAssertion = new UserAssertion(authResult.AccessToken);

            string atHash = userAssertion.AssertionHash;

            authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

            MsalAssert.AssertAuthResult(authResult, user);
            Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
            Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource);

            //Run OBO again. Should get token from cache
            authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

            Assert.IsNotNull(authResult);
            Assert.IsNotNull(authResult.AccessToken);
            Assert.IsNotNull(authResult.IdToken);
            Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache);
            Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens);
            Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
            Assert.AreEqual(TokenSource.Cache, authResult.AuthenticationResultMetadata.TokenSource);

            //Expire access tokens
            TokenCacheHelper.ExpireAllAccessTokens(confidentialApp.UserTokenCacheInternal);

            //Run OBO again. Should do OBO flow since the AT is expired and RTs aren't cached for normal OBO flow
            authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

            Assert.IsNotNull(authResult);
            Assert.IsNotNull(authResult.AccessToken);
            Assert.IsNotNull(authResult.IdToken);
            Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache);
            Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens);
            Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
            Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource);
            AssertLastHttpContent("on_behalf_of");

            //creating second app with no refresh tokens
            var atItems          = confidentialApp.UserTokenCacheInternal.Accessor.GetAllAccessTokens();
            var confidentialApp2 = ConfidentialClientApplicationBuilder
                                   .Create(confidentialClientID)
                                   .WithAuthority(new Uri(oboHost + authResult.TenantId), true)
                                   .WithClientSecret(secret)
                                   .WithTestLogging()
                                   .WithHttpClientFactory(factory)
                                   .BuildConcrete();

            TokenCacheHelper.ExpireAccessToken(confidentialApp2.UserTokenCacheInternal, atItems.FirstOrDefault());

            //Should perform OBO flow since the access token is expired and the refresh token does not exist
            authResult = await confidentialApp2.AcquireTokenOnBehalfOf(s_scopes, userAssertion)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

            Assert.IsNotNull(authResult);
            Assert.IsNotNull(authResult.AccessToken);
            Assert.IsNotNull(authResult.IdToken);
            Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache);
            Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens);
            Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
            Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource);
            AssertLastHttpContent("on_behalf_of");

            TokenCacheHelper.ExpireAllAccessTokens(confidentialApp2.UserTokenCacheInternal);
            TokenCacheHelper.UpdateUserAssertions(confidentialApp2);

            //Should perform OBO flow since the access token and the refresh token contains the wrong user assertion hash
            authResult = await confidentialApp2.AcquireTokenOnBehalfOf(s_scopes, userAssertion)
                         .ExecuteAsync(CancellationToken.None)
                         .ConfigureAwait(false);

            Assert.IsNotNull(authResult);
            Assert.IsNotNull(authResult.AccessToken);
            Assert.IsNotNull(authResult.IdToken);
            Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache);
            Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens);
            Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
            Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource);
            AssertLastHttpContent("on_behalf_of");
        }