コード例 #1
0
        public async Task POP_ShrValidation_Async()
        {
            using (var httpManager = new MockHttpManager())
            {
                ConfidentialClientApplication app =
                    ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
                    .WithClientSecret(TestConstants.ClientSecret)
                    .WithHttpManager(httpManager)
                    .WithExperimentalFeatures(true)
                    .BuildConcrete();

                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(ProtectedUrl));
                var popConfig = new PoPAuthenticationConfiguration(request);
                var provider  = PoPProviderFactory.GetOrCreateProvider();

                httpManager.AddInstanceDiscoveryMockHandler();
                httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(tokenType: "pop");

                // Act
                var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
                             .WithAuthority(TestConstants.AuthorityUtidTenant)
                             .WithProofOfPossession(popConfig)
                             .ExecuteAsync()
                             .ConfigureAwait(false);

                // access token parsing can be done with MSAL's id token parsing logic
                var claims = IdToken.Parse(result.AccessToken).ClaimsPrincipal;

                Assert.IsTrue(!string.IsNullOrEmpty(claims.FindAll("nonce").Single().Value));
                AssertSingedHttpRequestClaims(provider, claims);
            }
        }
        public void IdTokenParsing_B2CToken_OneEmail()
        {
            /*
             *    "exp": 1538804860,
             *    "nbf": 1538801260,
             *    "ver": "1.0",
             *    "iss": "https://login.microsoftonline.com/ba6c0d94-a8da-45b2-83ae-33871f9c2dd8/v2.0/",
             *    "sub": "ad020f8e-b1ba-44b2-bd69-c22be86737f5",
             *    "aud": "0a7f52dd-260e-432f-94de-b47828c3f372",
             *    "iat": 1538801260,
             *    "auth_time": 1538801260,
             *    "idp": "live.com",
             *    "name": "MSAL SDK Test",
             *    "oid": "ad020f8e-b1ba-44b2-bd69-c22be86737f5",
             *    "family_name": "SDK Test",
             *    "given_name": "MSAL",
             *    "emails": [
             *      "*****@*****.**",
             *    ],
             *    "tfp": "B2C_1_Signin",
             *    "at_hash": "Q4O3HDClcaLl7y0uU-bJAg"
             *
             */
            var addIdToken  = TestConstants.CreateB2CTestTokenResponse().IdToken;
            var parsedToken = IdToken.Parse(addIdToken);

            Assert.AreEqual("*****@*****.**", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "emails").Value);
            Assert.AreEqual(ClaimValueTypes.String.ToString(), parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "emails").ValueType);
        }
コード例 #3
0
        internal MsalAccountCacheItem(string environment, MsalTokenResponse response, string preferredUsername, string tenantID) : this()
        {
            IdToken idToken = IdToken.Parse(response.IdToken);

            Init(environment, idToken?.ObjectId, response.ClientInfo, idToken.Name, preferredUsername, tenantID,
                 idToken.GivenName, idToken.FamilyName);
        }
コード例 #4
0
        protected async Task <AuthenticationResult> CacheTokenResponseAndCreateAuthenticationResultAsync(MsalTokenResponse msalTokenResponse)
        {
            // developer passed in user object.
            AuthenticationRequestParameters.RequestContext.Logger.Info("Checking client info returned from the server..");

            ClientInfo fromServer = null;

            if (!AuthenticationRequestParameters.IsClientCredentialRequest &&
                !AuthenticationRequestParameters.IsRefreshTokenRequest &&
                AuthenticationRequestParameters.AuthorityInfo.AuthorityType != AuthorityType.Adfs)
            {
                //client_info is not returned from client credential flows because there is no user present.
                fromServer = ClientInfo.CreateFromJson(msalTokenResponse.ClientInfo);
            }

            ValidateAccountIdentifiers(fromServer);

            IdToken idToken = IdToken.Parse(msalTokenResponse.IdToken);

            AuthenticationRequestParameters.TenantUpdatedCanonicalAuthority =
                AuthenticationRequestParameters.Authority.GetTenantedAuthority(idToken?.TenantId);

            AuthenticationRequestParameters.RequestContext.Logger.Info("Saving Token Response to cache..");

            var tuple = await CacheManager.SaveTokenResponseAsync(msalTokenResponse).ConfigureAwait(false);

            return(new AuthenticationResult(tuple.Item1, tuple.Item2, AuthenticationRequestParameters.RequestContext.CorrelationId));
        }
コード例 #5
0
 void OnDeserialized(StreamingContext context)
 {
     if (RawIdToken != null)
     {
         IdToken idToken = IdToken.Parse(RawIdToken);
         TenantId = idToken.TenantId;
         User     = new User(idToken);
     }
 }
        private AccessTokenCacheItem SaveTokenResponseToCache()
        {
            // developer passed in user object.
            string msg = "checking client info returned from the server..";

            AuthenticationRequestParameters.RequestContext.Logger.Info(msg);
            AuthenticationRequestParameters.RequestContext.Logger.InfoPii(msg);

            ClientInfo fromServer = null;

            if (!AuthenticationRequestParameters.IsClientCredentialRequest)
            {
                //client_info is not returned from client credential flows because there is no user present.
                fromServer = ClientInfo.CreateFromJson(Response.ClientInfo);
            }

            if (fromServer != null && AuthenticationRequestParameters.ClientInfo != null)
            {
                if (!fromServer.UniqueIdentifier.Equals(AuthenticationRequestParameters.ClientInfo.UniqueIdentifier) ||
                    !fromServer.UniqueTenantIdentifier.Equals(AuthenticationRequestParameters.ClientInfo
                                                              .UniqueTenantIdentifier))
                {
                    AuthenticationRequestParameters.RequestContext.Logger.ErrorPii(String.Format(
                                                                                       CultureInfo.InvariantCulture,
                                                                                       "Returned user identifiers (uid:{0} utid:{1}) does not meatch the sent user identifier (uid:{2} utid:{3})",
                                                                                       fromServer.UniqueIdentifier, fromServer.UniqueTenantIdentifier,
                                                                                       AuthenticationRequestParameters.ClientInfo.UniqueIdentifier,
                                                                                       AuthenticationRequestParameters.ClientInfo.UniqueTenantIdentifier));

                    AuthenticationRequestParameters.RequestContext.Logger.Error("Returned user identifiers do not match the sent user" +
                                                                                "identifier");

                    throw new MsalServiceException("user_mismatch", "Returned user identifier does not match the sent user identifier");
                }
            }

            IdToken idToken = IdToken.Parse(Response.IdToken);

            AuthenticationRequestParameters.TenantUpdatedCanonicalAuthority = Authority.UpdateTenantId(
                AuthenticationRequestParameters.Authority.CanonicalAuthority, idToken?.TenantId);

            if (StoreToCache)
            {
                msg = "Saving Token Response to cache..";
                AuthenticationRequestParameters.RequestContext.Logger.Info(msg);
                AuthenticationRequestParameters.RequestContext.Logger.InfoPii(msg);

                return(TokenCache.SaveAccessAndRefreshToken(AuthenticationRequestParameters, Response));
            }

            return(new AccessTokenCacheItem(AuthenticationRequestParameters.TenantUpdatedCanonicalAuthority,
                                            AuthenticationRequestParameters.ClientId, Response));
        }
コード例 #7
0
        public void PopulateIdentifiers(TokenResponse response)
        {
            IdToken idToken = IdToken.Parse(response.IdToken);

            RawClientInfo = response.ClientInfo;
            ClientInfo    = ClientInfo.CreateFromJson(RawClientInfo);

            DisplayableId    = idToken.PreferredUsername;
            Name             = idToken.Name;
            IdentityProvider = idToken.Issuer;

            User = new User(GetUserIdentifier(), DisplayableId, Name, IdentityProvider);
        }
コード例 #8
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        public BaseTokenCacheItem(string authority, string clientId, string policy, TokenResponse response)
        {
            if (response.IdToken != null)
            {
                RawIdToken = response.IdToken;
                IdToken idToken = IdToken.Parse(response.IdToken);
                TenantId = idToken.TenantId;
                User     = new User(idToken);
            }

            this.Authority = authority;
            this.ClientId  = clientId;
            this.Policy    = policy;
        }
コード例 #9
0
        private void CreateDerivedProperties()
        {
            ScopeSet = Scope.AsSet();
            IdToken  = IdToken.Parse(RawIdToken);
            if (!string.IsNullOrEmpty(RawClientInfo))
            {
                // this should only happen for client credentials.
                ClientInfo = ClientInfo.CreateFromJson(RawClientInfo);
            }

            if (IdToken != null)
            {
                User = new User(GetUserIdentifier(), IdToken.PreferredUsername, IdToken.Name, IdToken.Issuer);
            }
        }
        public void IdTokenParsing_AADToken()
        {
            /*
             *  "aud": "b6c69a37-df96-4db0-9088-2ab96e1d8215",
             *  "iss": "https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/v2.0",
             *  "iat": 1538538422,
             *  "nbf": 1538538422,
             *  "exp": 1538542322,
             *  "name": "Cloud IDLAB Basic User",
             *  "oid": "9f4880d8-80ba-4c40-97bc-f7a23c703084",
             *  "preferred_username": "******",
             *  "sub": "Y6YkBdHNNLHNmTKel9KhRz8wrasxdLRFiP14BRPWrn4",
             *  "tid": "f645ad92-e38d-4d1a-b510-d1b09a74a8ca",
             *  "uti": "6nciX02SMki9k73-F1sZAA",
             *  "ver": "2.0"
             */
            var addIdToken  = TestConstants.CreateAadTestTokenResponse().IdToken;
            var parsedToken = IdToken.Parse(addIdToken);

            CoreAssert.AreEqual("Cloud IDLAB Basic User", parsedToken.Name, parsedToken.ClaimsPrincipal.FindFirst("name").Value);
            CoreAssert.AreEqual("9f4880d8-80ba-4c40-97bc-f7a23c703084", parsedToken.ObjectId, parsedToken.ClaimsPrincipal.FindFirst("oid").Value);
            CoreAssert.AreEqual("*****@*****.**", parsedToken.PreferredUsername, parsedToken.ClaimsPrincipal.FindFirst("preferred_username").Value);
            CoreAssert.AreEqual("Y6YkBdHNNLHNmTKel9KhRz8wrasxdLRFiP14BRPWrn4", parsedToken.Subject, parsedToken.ClaimsPrincipal.FindFirst("sub").Value);
            CoreAssert.AreEqual("f645ad92-e38d-4d1a-b510-d1b09a74a8ca", parsedToken.TenantId, parsedToken.ClaimsPrincipal.FindFirst("tid").Value);

            Assert.AreEqual("b6c69a37-df96-4db0-9088-2ab96e1d8215", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "aud").Value);
            Assert.AreEqual("https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/v2.0", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "iss").Value);
            Assert.AreEqual("1538538422", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "iat").Value);
            Assert.AreEqual("1538538422", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "nbf").Value);
            Assert.AreEqual("1538542322", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "exp").Value);
            Assert.AreEqual("Cloud IDLAB Basic User", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "name").Value);
            Assert.AreEqual("9f4880d8-80ba-4c40-97bc-f7a23c703084", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "oid").Value);
            Assert.AreEqual("*****@*****.**", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "preferred_username").Value);
            Assert.AreEqual("Y6YkBdHNNLHNmTKel9KhRz8wrasxdLRFiP14BRPWrn4", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "sub").Value);
            Assert.AreEqual("f645ad92-e38d-4d1a-b510-d1b09a74a8ca", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "tid").Value);
            Assert.AreEqual("6nciX02SMki9k73-F1sZAA", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "uti").Value);
            Assert.AreEqual("2.0", parsedToken.ClaimsPrincipal.Claims.Single(c => c.Type == "ver").Value);

            Assert.IsTrue(parsedToken.ClaimsPrincipal.Claims.Where(c => (new[] { "nbf", "iat", "exp" }).Contains(c.Type) == true).All(c => c.ValueType == ClaimValueTypes.Integer.ToString()));
            Assert.IsTrue(parsedToken.ClaimsPrincipal.Claims.Where(c => (new[] { "nbf", "iat", "exp" }).Contains(c.Type) == false).All(c => c.ValueType == ClaimValueTypes.String.ToString()));

            Assert.IsNull(parsedToken.Upn);
            Assert.IsNull(parsedToken.FamilyName);
            Assert.IsNull(parsedToken.GivenName);

            Assert.IsTrue(parsedToken.ClaimsPrincipal.Claims.All(c => c.Issuer == "https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/v2.0"));
            Assert.IsTrue(parsedToken.ClaimsPrincipal.Claims.All(c => c.OriginalIssuer == "https://login.microsoftonline.com/f645ad92-e38d-4d1a-b510-d1b09a74a8ca/v2.0"));
        }
        public void IdTokenParsing_B2CToken_TwoEmails()
        {
            /*
             *   {
             *    "sub": "1234567890",
             *    "emails": ["*****@*****.**", "*****@*****.**"],
             *    "iat": 1516239022
             *      }
             *
             */
            var addIdToken  = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWxzIjpbImFAb3V0bG9vay5jb20iLCJiQGhvdG1haWwuY29tIl0sImlhdCI6MTUxNjIzOTAyMn0.";
            var parsedToken = IdToken.Parse(addIdToken);

            Assert.AreEqual(2, parsedToken.ClaimsPrincipal.Claims.Where(c => c.Type == "emails").Count());
            Assert.IsTrue(parsedToken.ClaimsPrincipal.Claims.Where(c => c.Type == "emails").All(c => (new[] { "*****@*****.**", "*****@*****.**" }).Contains(c.Value)));
        }
コード例 #12
0
        internal MsalAccountCacheItem(
            string preferredCacheEnv,
            MsalTokenResponse response,
            string preferredUsername,
            string tenantId)
            : this()
        {
            var idToken = IdToken.Parse(response.IdToken);

            Init(
                preferredCacheEnv,
                idToken?.ObjectId,
                response.ClientInfo,
                idToken.Name,
                preferredUsername,
                tenantId,
                idToken.GivenName,
                idToken.FamilyName);
        }
コード例 #13
0
        public void GetSubsetScopesMatchedAccessTokenTest()
        {
            TokenCache cache = new TokenCache()
            {
                ClientId = TestConstants.ClientId
            };
            AccessTokenCacheItem atItem = new AccessTokenCacheItem()
            {
                Authority = TestConstants.AuthorityHomeTenant,
                ClientId  = TestConstants.ClientId,
                TokenType = "Bearer",
                ScopeSet  = TestConstants.Scope,
                Scope     = TestConstants.Scope.AsSingleString(),
                ExpiresOnUnixTimestamp = MsalHelpers.DateTimeToUnixTimestamp(DateTime.UtcNow + TimeSpan.FromHours(1)),
                RawIdToken             = MockHelpers.CreateIdToken(TestConstants.UniqueId, TestConstants.DisplayableId),
                RawClientInfo          = MockHelpers.CreateClientInfo(),
            };

            atItem.IdToken    = IdToken.Parse(atItem.RawIdToken);
            atItem.ClientInfo = ClientInfo.CreateFromJson(atItem.RawClientInfo);

            // create key out of access token cache item and then
            // set it as the value of the access token.
            AccessTokenCacheKey atKey = atItem.GetAccessTokenItemKey();

            atItem.AccessToken = atKey.ToString();

            cache.TokenCacheAccessor.AccessTokenCacheDictionary[atKey.ToString()] = JsonHelper.SerializeToJson(atItem);
            var param = new AuthenticationRequestParameters()
            {
                RequestContext = new RequestContext(Guid.Empty, null),
                ClientId       = TestConstants.ClientId,
                Authority      = Authority.CreateAuthority(TestConstants.AuthorityHomeTenant, false),
                Scope          = new SortedSet <string>(),
                User           = TestConstants.User
            };

            param.Scope.Add("r1/scope1");
            AccessTokenCacheItem item = cache.FindAccessToken(param);

            Assert.IsNotNull(item);
            Assert.AreEqual(atKey.ToString(), item.AccessToken);
        }
コード例 #14
0
        public void GetExpiredAccessTokenTest()
        {
            cache = new TokenCache()
            {
                ClientId = TestConstants.ClientId
            };

            AccessTokenCacheItem item = new AccessTokenCacheItem()
            {
                Authority = TestConstants.AuthorityHomeTenant,
                ClientId  = TestConstants.ClientId,
                TokenType = "Bearer",
                ExpiresOnUnixTimestamp = MsalHelpers.DateTimeToUnixTimestamp(DateTime.UtcNow),
                RawIdToken             = MockHelpers.CreateIdToken(TestConstants.UniqueId, TestConstants.DisplayableId),
                RawClientInfo          = MockHelpers.CreateClientInfo(),
                ScopeSet = TestConstants.Scope
            };

            item.IdToken     = IdToken.Parse(item.RawIdToken);
            item.ClientInfo  = ClientInfo.CreateFromJson(item.RawClientInfo);
            item.AccessToken = item.GetAccessTokenItemKey().ToString();
            cache.TokenCacheAccessor.AccessTokenCacheDictionary[item.GetAccessTokenItemKey().ToString()] =
                JsonHelper.SerializeToJson(item);

            cache.TokenCacheAccessor.AccessTokenCacheDictionary[item.GetAccessTokenItemKey().ToString()] =
                JsonHelper.SerializeToJson(item);

            Assert.IsNull(cache.FindAccessToken(new AuthenticationRequestParameters()
            {
                RequestContext = new RequestContext(Guid.Empty, null),
                ClientId       = TestConstants.ClientId,
                Authority      = Authority.CreateAuthority(TestConstants.AuthorityHomeTenant, false),
                Scope          = TestConstants.Scope,
                User           =
                    new User()
                {
                    DisplayableId = TestConstants.DisplayableId,
                    Identifier    = TestConstants.UserIdentifier
                }
            }));
        }
コード例 #15
0
        public async Task POP_NoHttpRequest_Async()
        {
            using (var httpManager = new MockHttpManager())
            {
                ConfidentialClientApplication app =
                    ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId)
                    .WithClientSecret(TestConstants.ClientSecret)
                    .WithHttpManager(httpManager)
                    .WithExperimentalFeatures(true)
                    .BuildConcrete();

                // no HTTP method binding, but custom nonce
                var popConfig = new PoPAuthenticationConfiguration()
                {
                    Nonce = CustomNonce
                };
                var provider = PoPProviderFactory.GetOrCreateProvider();

                httpManager.AddInstanceDiscoveryMockHandler();
                httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(tokenType: "pop");

                // Act
                var result = await app.AcquireTokenForClient(TestConstants.s_scope.ToArray())
                             .WithAuthority(TestConstants.AuthorityUtidTenant)
                             .WithProofOfPossession(popConfig)
                             .ExecuteAsync()
                             .ConfigureAwait(false);

                // access token parsing can be done with MSAL's id token parsing logic
                var claims = IdToken.Parse(result.AccessToken).ClaimsPrincipal;

                Assert.AreEqual(CustomNonce, claims.FindAll("nonce").Single().Value);
                AssertTsAndJwkClaims(provider, claims);

                Assert.IsFalse(claims.FindAll("m").Any());
                Assert.IsFalse(claims.FindAll("u").Any());
                Assert.IsFalse(claims.FindAll("p").Any());
            }
        }
コード例 #16
0
        public static void PopulateCache(TokenCacheAccessor accessor)
        {
            AccessTokenCacheItem item = new AccessTokenCacheItem()
            {
                Authority = TestConstants.AuthorityHomeTenant,
                ClientId  = TestConstants.ClientId,
                TokenType = "Bearer",
                ExpiresOnUnixTimestamp = MsalHelpers.DateTimeToUnixTimestamp(new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(ValidExpiresIn))),
                RawIdToken             = MockHelpers.CreateIdToken(TestConstants.UniqueId, TestConstants.DisplayableId),
                RawClientInfo          = MockHelpers.CreateClientInfo(),
                Scope    = TestConstants.Scope.AsSingleString(),
                ScopeSet = TestConstants.Scope
            };

            item.IdToken     = IdToken.Parse(item.RawIdToken);
            item.ClientInfo  = ClientInfo.CreateFromJson(item.RawClientInfo);
            item.AccessToken = item.GetAccessTokenItemKey().ToString();
            //add access token
            accessor.AccessTokenCacheDictionary[item.GetAccessTokenItemKey().ToString()] = JsonHelper.SerializeToJson(item);

            item = new AccessTokenCacheItem()
            {
                Authority = TestConstants.AuthorityGuestTenant,
                ClientId  = TestConstants.ClientId,
                TokenType = "Bearer",
                ExpiresOnUnixTimestamp = MsalHelpers.DateTimeToUnixTimestamp(new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(ValidExpiresIn))),
                RawIdToken             = MockHelpers.CreateIdToken(TestConstants.UniqueId + "more", TestConstants.DisplayableId),
                RawClientInfo          = MockHelpers.CreateClientInfo(),
                Scope    = TestConstants.ScopeForAnotherResource.AsSingleString(),
                ScopeSet = TestConstants.ScopeForAnotherResource
            };
            item.IdToken     = IdToken.Parse(item.RawIdToken);
            item.ClientInfo  = ClientInfo.CreateFromJson(item.RawClientInfo);
            item.AccessToken = item.GetAccessTokenItemKey().ToString();
            //add another access token
            accessor.AccessTokenCacheDictionary[item.GetAccessTokenItemKey().ToString()] = JsonHelper.SerializeToJson(item);

            AddRefreshTokenToCache(accessor, TestConstants.Uid, TestConstants.Utid, TestConstants.Name);
        }
コード例 #17
0
        async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem, Account> > ITokenCacheInternal.SaveTokenResponseAsync(
            AuthenticationRequestParameters requestParams,
            MsalTokenResponse response)
        {
            response.Log(requestParams.RequestContext.Logger, LogLevel.Verbose);

            MsalAccessTokenCacheItem  msalAccessTokenCacheItem  = null;
            MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null;
            MsalIdTokenCacheItem      msalIdTokenCacheItem      = null;
            MsalAccountCacheItem      msalAccountCacheItem      = null;

            IdToken idToken = IdToken.Parse(response.IdToken);

            if (idToken == null)
            {
                requestParams.RequestContext.Logger.Info("ID Token not present in response. ");
            }

            var tenantId = GetTenantId(idToken, requestParams);

            bool   isAdfsAuthority      = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs;
            string preferredUsername    = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken);
            string username             = isAdfsAuthority ? idToken?.Upn : preferredUsername;
            string homeAccountId        = GetHomeAccountId(requestParams, response, idToken);
            string suggestedWebCacheKey = SuggestedWebCacheKeyFactory.GetKeyFromResponse(requestParams, homeAccountId);

            // Do a full instance discovery when saving tokens (if not cached),
            // so that the PreferredNetwork environment is up to date.
            var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager
                                            .GetMetadataEntryAsync(
                requestParams.Authority.AuthorityInfo,
                requestParams.RequestContext)
                                            .ConfigureAwait(false);

            #region Create Cache Objects
            if (!string.IsNullOrEmpty(response.AccessToken))
            {
                msalAccessTokenCacheItem =
                    new MsalAccessTokenCacheItem(
                        instanceDiscoveryMetadata.PreferredCache,
                        requestParams.AppConfig.ClientId,
                        response,
                        tenantId,
                        homeAccountId,
                        requestParams.AuthenticationScheme.KeyId)
                {
                    UserAssertionHash = requestParams.UserAssertion?.AssertionHash,
                    IsAdfs            = isAdfsAuthority
                };
            }

            if (!string.IsNullOrEmpty(response.RefreshToken))
            {
                msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    requestParams.AppConfig.ClientId,
                    response,
                    homeAccountId);

                if (!_featureFlags.IsFociEnabled)
                {
                    msalRefreshTokenCacheItem.FamilyId = null;
                }
            }

            Dictionary <string, string> wamAccountIds = GetWamAccountIds(requestParams, response);
            Account account;
            if (idToken != null)
            {
                msalIdTokenCacheItem = new MsalIdTokenCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    requestParams.AppConfig.ClientId,
                    response,
                    tenantId,
                    homeAccountId)
                {
                    IsAdfs = isAdfsAuthority
                };

                msalAccountCacheItem = new MsalAccountCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    response.ClientInfo,
                    homeAccountId,
                    idToken,
                    preferredUsername,
                    tenantId,
                    wamAccountIds);
            }

            #endregion

            account = new Account(
                homeAccountId,
                username,
                instanceDiscoveryMetadata.PreferredNetwork,
                wamAccountIds);
            requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Entering token cache semaphore. ");
            await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

            requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Entered token cache semaphore. ");

            try
            {
#pragma warning disable CS0618 // Type or member is obsolete
                HasStateChanged = true;
#pragma warning restore CS0618 // Type or member is obsolete

                try
                {
                    ITokenCacheInternal tokenCacheInternal = this;
                    if (tokenCacheInternal.IsTokenCacheSerialized())
                    {
                        var args = new TokenCacheNotificationArgs(
                            this,
                            ClientId,
                            account,
                            hasStateChanged: true,
                            tokenCacheInternal.IsApplicationCache,
                            hasTokens: tokenCacheInternal.HasTokensNoLocks(),
                            requestParams.RequestContext.UserCancellationToken,
                            suggestedCacheKey: suggestedWebCacheKey);

                        Stopwatch sw = Stopwatch.StartNew();

                        await tokenCacheInternal.OnBeforeAccessAsync(args).ConfigureAwait(false);

                        await tokenCacheInternal.OnBeforeWriteAsync(args).ConfigureAwait(false);

                        requestParams.RequestContext.ApiEvent.DurationInCacheInMs += sw.ElapsedMilliseconds;
                    }

                    if (msalAccessTokenCacheItem != null)
                    {
                        requestParams.RequestContext.Logger.Info("Saving AT in cache and removing overlapping ATs...");

                        DeleteAccessTokensWithIntersectingScopes(
                            requestParams,
                            instanceDiscoveryMetadata.Aliases,
                            tenantId,
                            msalAccessTokenCacheItem.ScopeSet,
                            msalAccessTokenCacheItem.HomeAccountId,
                            msalAccessTokenCacheItem.TokenType);

                        _accessor.SaveAccessToken(msalAccessTokenCacheItem);
                    }

                    if (idToken != null)
                    {
                        requestParams.RequestContext.Logger.Info("Saving Id Token and Account in cache ...");
                        _accessor.SaveIdToken(msalIdTokenCacheItem);
                        MergeWamAccountIds(msalAccountCacheItem);
                        _accessor.SaveAccount(msalAccountCacheItem);
                    }

                    // if server returns the refresh token back, save it in the cache.
                    if (msalRefreshTokenCacheItem != null)
                    {
                        requestParams.RequestContext.Logger.Info("Saving RT in cache...");
                        _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
                    }

                    UpdateAppMetadata(requestParams.AppConfig.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId);

                    // Do not save RT in ADAL cache for client credentials flow or B2C
                    if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled &&
                        !requestParams.IsClientCredentialRequest &&
                        requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C)
                    {
                        var tenatedAuthority            = Authority.CreateAuthorityWithTenant(requestParams.AuthorityInfo, tenantId);
                        var authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment(
                            tenatedAuthority.AuthorityInfo,
                            instanceDiscoveryMetadata.PreferredCache);

                        CacheFallbackOperations.WriteAdalRefreshToken(
                            Logger,
                            LegacyCachePersistence,
                            msalRefreshTokenCacheItem,
                            msalIdTokenCacheItem,
                            authorityWithPreferredCache.AuthorityInfo.CanonicalAuthority,
                            msalIdTokenCacheItem.IdToken.ObjectId,
                            response.Scope);
                    }
                }
                finally
                {
                    ITokenCacheInternal tokenCacheInternal = this;
                    if (tokenCacheInternal.IsTokenCacheSerialized())
                    {
                        var args = new TokenCacheNotificationArgs(
                            this,
                            ClientId,
                            account,
                            hasStateChanged: true,
                            tokenCacheInternal.IsApplicationCache,
                            tokenCacheInternal.HasTokensNoLocks(),
                            requestParams.RequestContext.UserCancellationToken,
                            suggestedCacheKey: suggestedWebCacheKey);

                        Stopwatch sw = Stopwatch.StartNew();
                        await tokenCacheInternal.OnAfterAccessAsync(args).ConfigureAwait(false);

                        requestParams.RequestContext.ApiEvent.DurationInCacheInMs += sw.ElapsedMilliseconds;
                    }
#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = false;
#pragma warning restore CS0618 // Type or member is obsolete
                }

                return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem, account));
            }
            finally
            {
                _semaphoreSlim.Release();
                requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Released token cache semaphore. ");
            }
        }
コード例 #18
0
        public void GetUsersTest()
        {
            PublicClientApplication app   = new PublicClientApplication(TestConstants.ClientId);
            IEnumerable <IUser>     users = app.Users;

            Assert.IsNotNull(users);
            Assert.IsFalse(users.Any());
            cache = new TokenCache()
            {
                ClientId = TestConstants.ClientId
            };

            app.UserTokenCache = cache;
            TokenCacheHelper.PopulateCache(cache.TokenCacheAccessor);
            users = app.Users;
            Assert.IsNotNull(users);
            Assert.AreEqual(1, users.Count());

            AccessTokenCacheItem item = new AccessTokenCacheItem()
            {
                Authority = TestConstants.AuthorityHomeTenant,
                ClientId  = TestConstants.ClientId,
                TokenType = "Bearer",
                ExpiresOnUnixTimestamp =
                    MsalHelpers.DateTimeToUnixTimestamp((DateTime.UtcNow + TimeSpan.FromSeconds(3600))),
                RawIdToken    = MockHelpers.CreateIdToken(TestConstants.UniqueId, TestConstants.DisplayableId),
                RawClientInfo = MockHelpers.CreateClientInfo(),
                ScopeSet      = TestConstants.Scope
            };

            item.IdToken     = IdToken.Parse(item.RawIdToken);
            item.ClientInfo  = ClientInfo.CreateFromJson(item.RawClientInfo);
            item.AccessToken = item.GetAccessTokenItemKey().ToString();
            cache.TokenCacheAccessor.AccessTokenCacheDictionary[item.GetAccessTokenItemKey().ToString()] =
                JsonHelper.SerializeToJson(item);


            // another cache entry for different uid. user count should be 2.
            RefreshTokenCacheItem rtItem = new RefreshTokenCacheItem()
            {
                Environment      = TestConstants.ProductionEnvironment,
                ClientId         = TestConstants.ClientId,
                RefreshToken     = "someRT",
                RawClientInfo    = MockHelpers.CreateClientInfo(TestConstants.Uid + "more", TestConstants.Utid),
                DisplayableId    = TestConstants.DisplayableId,
                IdentityProvider = TestConstants.IdentityProvider,
                Name             = TestConstants.Name
            };

            rtItem.ClientInfo = ClientInfo.CreateFromJson(rtItem.RawClientInfo);
            cache.TokenCacheAccessor.RefreshTokenCacheDictionary[rtItem.GetRefreshTokenItemKey().ToString()] =
                JsonHelper.SerializeToJson(rtItem);
            Assert.AreEqual(2, cache.TokenCacheAccessor.RefreshTokenCacheDictionary.Count);
            users = app.Users;
            Assert.IsNotNull(users);
            Assert.AreEqual(2, users.Count());

            // another cache entry for different environment. user count should still be 2. Sovereign cloud user must not be returned
            rtItem = new RefreshTokenCacheItem()
            {
                Environment      = TestConstants.SovereignEnvironment,
                ClientId         = TestConstants.ClientId,
                RefreshToken     = "someRT",
                RawClientInfo    = MockHelpers.CreateClientInfo(TestConstants.Uid + "more1", TestConstants.Utid),
                DisplayableId    = TestConstants.DisplayableId,
                IdentityProvider = TestConstants.IdentityProvider,
                Name             = TestConstants.Name
            };
            rtItem.ClientInfo = ClientInfo.CreateFromJson(rtItem.RawClientInfo);
            cache.TokenCacheAccessor.RefreshTokenCacheDictionary[rtItem.GetRefreshTokenItemKey().ToString()] =
                JsonHelper.SerializeToJson(rtItem);
            Assert.AreEqual(3, cache.TokenCacheAccessor.RefreshTokenCacheDictionary.Count);
            users = app.Users;
            Assert.IsNotNull(users);
            Assert.AreEqual(2, users.Count());
        }
コード例 #19
0
        async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync(
            AuthenticationRequestParameters requestParams,
            MsalTokenResponse response)
        {
            MsalAccessTokenCacheItem  msalAccessTokenCacheItem  = null;
            MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null;
            MsalIdTokenCacheItem      msalIdTokenCacheItem      = null;
            MsalAccountCacheItem      msalAccountCacheItem      = null;

            IdToken idToken = IdToken.Parse(response.IdToken);

            var tenantId = Authority
                           .CreateAuthority(requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority)
                           .TenantId;

            bool   isAdfsAuthority   = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs;
            string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken);
            string username          = isAdfsAuthority ? idToken?.Upn : preferredUsername;
            string homeAccountId     = GetHomeAccountId(requestParams, response, idToken);

            // Do a full instance discovery when saving tokens (if not cached),
            // so that the PreferredNetwork environment is up to date.
            var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager
                                            .GetMetadataEntryAsync(
                requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority,
                requestParams.RequestContext)
                                            .ConfigureAwait(false);

            #region Create Cache Objects
            if (!string.IsNullOrEmpty(response.AccessToken))
            {
                msalAccessTokenCacheItem =
                    new MsalAccessTokenCacheItem(
                        instanceDiscoveryMetadata.PreferredCache,
                        requestParams.ClientId,
                        response,
                        tenantId,
                        homeAccountId,
                        requestParams.AuthenticationScheme.KeyId)
                {
                    UserAssertionHash = requestParams.UserAssertion?.AssertionHash,
                    IsAdfs            = isAdfsAuthority
                };
            }

            if (!string.IsNullOrEmpty(response.RefreshToken))
            {
                msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    requestParams.ClientId,
                    response,
                    homeAccountId);

                if (!_featureFlags.IsFociEnabled)
                {
                    msalRefreshTokenCacheItem.FamilyId = null;
                }
            }

            if (idToken != null)
            {
                msalIdTokenCacheItem = new MsalIdTokenCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    requestParams.ClientId,
                    response,
                    tenantId,
                    homeAccountId)
                {
                    IsAdfs = isAdfsAuthority
                };

                msalAccountCacheItem = new MsalAccountCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    response.ClientInfo,
                    homeAccountId,
                    idToken,
                    preferredUsername,
                    tenantId);
            }

            #endregion

            Account account = new Account(
                homeAccountId,
                username,
                instanceDiscoveryMetadata.PreferredCache);

            await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

            try
            {
                var args = new TokenCacheNotificationArgs(
                    this,
                    ClientId,
                    account,
                    hasStateChanged: true,
                    (this as ITokenCacheInternal).IsApplicationCache,
                    hasTokens: (this as ITokenCacheInternal).HasTokensNoLocks(),
                    requestParams.SuggestedWebAppCacheKey);

#pragma warning disable CS0618 // Type or member is obsolete
                HasStateChanged = true;
#pragma warning restore CS0618 // Type or member is obsolete

                try
                {
                    await(this as ITokenCacheInternal).OnBeforeAccessAsync(args).ConfigureAwait(false);
                    await(this as ITokenCacheInternal).OnBeforeWriteAsync(args).ConfigureAwait(false);

                    if (msalAccessTokenCacheItem != null)
                    {
                        requestParams.RequestContext.Logger.Info("Saving AT in cache and removing overlapping ATs...");

                        DeleteAccessTokensWithIntersectingScopes(
                            requestParams,
                            instanceDiscoveryMetadata.Aliases,
                            tenantId,
                            msalAccessTokenCacheItem.ScopeSet,
                            msalAccessTokenCacheItem.HomeAccountId,
                            msalAccessTokenCacheItem.TokenType);

                        _accessor.SaveAccessToken(msalAccessTokenCacheItem);
                    }

                    if (idToken != null)
                    {
                        requestParams.RequestContext.Logger.Info("Saving Id Token and Account in cache ...");
                        _accessor.SaveIdToken(msalIdTokenCacheItem);
                        _accessor.SaveAccount(msalAccountCacheItem);
                    }

                    // if server returns the refresh token back, save it in the cache.
                    if (msalRefreshTokenCacheItem != null)
                    {
                        requestParams.RequestContext.Logger.Info("Saving RT in cache...");
                        _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
                    }

                    UpdateAppMetadata(requestParams.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId);

                    // Do not save RT in ADAL cache for confidential client or B2C
                    if (!requestParams.IsClientCredentialRequest &&
                        requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C)
                    {
                        var authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment(
                            requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo,
                            instanceDiscoveryMetadata.PreferredCache);

                        CacheFallbackOperations.WriteAdalRefreshToken(
                            Logger,
                            LegacyCachePersistence,
                            msalRefreshTokenCacheItem,
                            msalIdTokenCacheItem,
                            authorityWithPreferredCache.AuthorityInfo.CanonicalAuthority,
                            msalIdTokenCacheItem.IdToken.ObjectId,
                            response.Scope);
                    }
                }
                finally
                {
                    var args2 = new TokenCacheNotificationArgs(
                        this,
                        ClientId,
                        account,
                        hasStateChanged: true,
                        (this as ITokenCacheInternal).IsApplicationCache,
                        (this as ITokenCacheInternal).HasTokensNoLocks(),
                        requestParams.SuggestedWebAppCacheKey);

                    await(this as ITokenCacheInternal).OnAfterAccessAsync(args2).ConfigureAwait(false);
#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = false;
#pragma warning restore CS0618 // Type or member is obsolete
                }

                return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem));
            }
            finally
            {
                _semaphoreSlim.Release();
            }
        }
コード例 #20
0
        async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync(
            AuthenticationRequestParameters requestParams,
            MsalTokenResponse response)
        {
            var tenantId = Authority
                           .CreateAuthority(ServiceBundle, requestParams.TenantUpdatedCanonicalAuthority)
                           .GetTenantId();

            IdToken idToken = IdToken.Parse(response.IdToken);

            // The preferred_username value cannot be null or empty in order to comply with the ADAL/MSAL Unified cache schema.
            // It will be set to "preferred_username not in idtoken"
            var preferredUsername = !string.IsNullOrWhiteSpace(idToken?.PreferredUsername)
                ? idToken.PreferredUsername
                : NullPreferredUsernameDisplayLabel;

            var instanceDiscoveryMetadataEntry = GetCachedAuthorityMetaData(requestParams.TenantUpdatedCanonicalAuthority);

            var environmentAliases = GetEnvironmentAliases(
                requestParams.TenantUpdatedCanonicalAuthority,
                instanceDiscoveryMetadataEntry);

            var preferredEnvironmentHost = GetPreferredEnvironmentHost(
                requestParams.AuthorityInfo.Host,
                instanceDiscoveryMetadataEntry);

            var msalAccessTokenCacheItem =
                new MsalAccessTokenCacheItem(preferredEnvironmentHost, requestParams.ClientId, response, tenantId)
            {
                UserAssertionHash = requestParams.UserAssertion?.AssertionHash
            };

            MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null;
            MsalIdTokenCacheItem      msalIdTokenCacheItem      = null;

            if (idToken != null)
            {
                msalIdTokenCacheItem = new MsalIdTokenCacheItem(
                    preferredEnvironmentHost,
                    requestParams.ClientId,
                    response,
                    tenantId);
            }

            await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

            try
            {
                try
                {
                    var args = new TokenCacheNotificationArgs
                    {
                        TokenCache = new NoLockTokenCacheProxy(this),
                        ClientId   = ClientId,
                        Account    = msalAccessTokenCacheItem.HomeAccountId != null
                            ? new Account(msalAccessTokenCacheItem.HomeAccountId, preferredUsername, preferredEnvironmentHost)
                            : null,
                        HasStateChanged = true
                    };

#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = true;
#pragma warning restore CS0618 // Type or member is obsolete

                    await OnBeforeAccessAsync(args).ConfigureAwait(false);

                    try
                    {
                        await OnBeforeWriteAsync(args).ConfigureAwait(false);

                        DeleteAccessTokensWithIntersectingScopes(
                            requestParams,
                            environmentAliases,
                            tenantId,
                            msalAccessTokenCacheItem.ScopeSet,
                            msalAccessTokenCacheItem.HomeAccountId);

                        _accessor.SaveAccessToken(msalAccessTokenCacheItem);

                        if (idToken != null)
                        {
                            _accessor.SaveIdToken(msalIdTokenCacheItem);
                            var msalAccountCacheItem = new MsalAccountCacheItem(
                                preferredEnvironmentHost,
                                response,
                                preferredUsername,
                                tenantId);

                            _accessor.SaveAccount(msalAccountCacheItem);
                        }

                        // if server returns the refresh token back, save it in the cache.
                        if (response.RefreshToken != null)
                        {
                            msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(
                                preferredEnvironmentHost,
                                requestParams.ClientId,
                                response);

                            if (!_featureFlags.IsFociEnabled)
                            {
                                msalRefreshTokenCacheItem.FamilyId = null;
                            }

                            requestParams.RequestContext.Logger.Info("Saving RT in cache...");
                            _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
                        }

                        UpdateAppMetadata(requestParams.ClientId, preferredEnvironmentHost, response.FamilyId);

                        // save RT in ADAL cache for public clients
                        // do not save RT in ADAL cache for MSAL B2C scenarios
                        if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AuthorityType.B2C))
                        {
                            CacheFallbackOperations.WriteAdalRefreshToken(
                                Logger,
                                LegacyCachePersistence,
                                msalRefreshTokenCacheItem,
                                msalIdTokenCacheItem,
                                Authority.CreateAuthorityUriWithHost(
                                    requestParams.TenantUpdatedCanonicalAuthority,
                                    preferredEnvironmentHost),
                                msalIdTokenCacheItem.IdToken.ObjectId, response.Scope);
                        }
                    }
                    finally
                    {
                        await OnAfterAccessAsync(args).ConfigureAwait(false);
                    }

                    return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem));
                }
                finally
                {
#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = false;
#pragma warning restore CS0618 // Type or member is obsolete
                }
            }
            finally
            {
                _semaphoreSlim.Release();
            }
        }
コード例 #21
0
        async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync(
            AuthenticationRequestParameters requestParams,
            MsalTokenResponse response)
        {
            var tenantId = Authority
                           .CreateAuthority(ServiceBundle, requestParams.TenantUpdatedCanonicalAuthority)
                           .GetTenantId();

            bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs;

            IdToken idToken = IdToken.Parse(response.IdToken);

            string subject = idToken?.Subject;

            if (idToken != null && string.IsNullOrEmpty(subject))
            {
                requestParams.RequestContext.Logger.Warning("Subject not present in Id token");
            }

            string preferredUsername;

            // The preferred_username value cannot be null or empty in order to comply with the ADAL/MSAL Unified cache schema.
            // It will be set to "preferred_username not in idtoken"
            if (idToken == null)
            {
                preferredUsername = NullPreferredUsernameDisplayLabel;
            }
            else if (string.IsNullOrWhiteSpace(idToken.PreferredUsername))
            {
                if (isAdfsAuthority)
                {
                    //The direct to adfs scenario does not return preferred_username in the id token so it needs to be set to the upn
                    preferredUsername = !string.IsNullOrEmpty(idToken.Upn)
                        ? idToken.Upn
                        : NullPreferredUsernameDisplayLabel;
                }
                else
                {
                    preferredUsername = NullPreferredUsernameDisplayLabel;
                }
            }
            else
            {
                preferredUsername = idToken.PreferredUsername;
            }



            var instanceDiscoveryMetadataEntry = GetCachedAuthorityMetaData(requestParams.TenantUpdatedCanonicalAuthority);

            var environmentAliases = GetEnvironmentAliases(
                requestParams.TenantUpdatedCanonicalAuthority,
                instanceDiscoveryMetadataEntry);

            var preferredEnvironmentHost = GetPreferredEnvironmentHost(
                requestParams.AuthorityInfo.Host,
                instanceDiscoveryMetadataEntry);

            var msalAccessTokenCacheItem =
                new MsalAccessTokenCacheItem(preferredEnvironmentHost, requestParams.ClientId, response, tenantId, subject)
            {
                UserAssertionHash = requestParams.UserAssertion?.AssertionHash,
                IsAdfs            = isAdfsAuthority
            };

            MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null;
            MsalIdTokenCacheItem      msalIdTokenCacheItem      = null;

            if (idToken != null)
            {
                msalIdTokenCacheItem = new MsalIdTokenCacheItem(
                    preferredEnvironmentHost,
                    requestParams.ClientId,
                    response,
                    tenantId,
                    subject)
                {
                    IsAdfs = isAdfsAuthority
                };
            }

            await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

            try
            {
                try
                {
                    Account account  = null;
                    string  username = isAdfsAuthority ? idToken?.Upn : preferredUsername;
                    if (msalAccessTokenCacheItem.HomeAccountId != null)
                    {
                        account = new Account(
                            msalAccessTokenCacheItem.HomeAccountId,
                            username,
                            preferredEnvironmentHost);
                    }
                    var args = new TokenCacheNotificationArgs(this, ClientId, account, true);

#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = true;
#pragma warning restore CS0618 // Type or member is obsolete

                    await OnBeforeAccessAsync(args).ConfigureAwait(false);

                    try
                    {
                        await OnBeforeWriteAsync(args).ConfigureAwait(false);

                        DeleteAccessTokensWithIntersectingScopes(
                            requestParams,
                            environmentAliases,
                            tenantId,
                            msalAccessTokenCacheItem.ScopeSet,
                            msalAccessTokenCacheItem.HomeAccountId);

                        _accessor.SaveAccessToken(msalAccessTokenCacheItem);

                        if (idToken != null)
                        {
                            _accessor.SaveIdToken(msalIdTokenCacheItem);
                            var msalAccountCacheItem = new MsalAccountCacheItem(
                                preferredEnvironmentHost,
                                response,
                                preferredUsername,
                                tenantId);

                            //The ADFS direct scenrio does not return client info so the home account id is acquired from the subject
                            if (isAdfsAuthority && String.IsNullOrEmpty(msalAccountCacheItem.HomeAccountId))
                            {
                                msalAccountCacheItem.HomeAccountId = idToken.Subject;
                            }

                            _accessor.SaveAccount(msalAccountCacheItem);
                        }

                        // if server returns the refresh token back, save it in the cache.
                        if (response.RefreshToken != null)
                        {
                            msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(
                                preferredEnvironmentHost,
                                requestParams.ClientId,
                                response,
                                subject);

                            if (!_featureFlags.IsFociEnabled)
                            {
                                msalRefreshTokenCacheItem.FamilyId = null;
                            }

                            requestParams.RequestContext.Logger.Info("Saving RT in cache...");
                            _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
                        }

                        UpdateAppMetadata(requestParams.ClientId, preferredEnvironmentHost, response.FamilyId);

                        // save RT in ADAL cache for public clients
                        // do not save RT in ADAL cache for MSAL B2C scenarios
                        if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AuthorityType.B2C))
                        {
                            CacheFallbackOperations.WriteAdalRefreshToken(
                                Logger,
                                LegacyCachePersistence,
                                msalRefreshTokenCacheItem,
                                msalIdTokenCacheItem,
                                Authority.CreateAuthorityUriWithHost(
                                    requestParams.TenantUpdatedCanonicalAuthority,
                                    preferredEnvironmentHost),
                                msalIdTokenCacheItem.IdToken.ObjectId, response.Scope);
                        }
                    }
                    finally
                    {
                        await OnAfterAccessAsync(args).ConfigureAwait(false);
                    }

                    return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem));
                }
                finally
                {
#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = false;
#pragma warning restore CS0618 // Type or member is obsolete
                }
            }
            finally
            {
                _semaphoreSlim.Release();
            }
        }
        public void NoCacheLookup()
        {
            Authority authority = Authority.CreateAuthority(TestConstants.AuthorityHomeTenant, false);

            cache = new TokenCache()
            {
                ClientId = TestConstants.ClientId
            };

            AccessTokenCacheItem atItem = new AccessTokenCacheItem()
            {
                Authority              = TestConstants.AuthorityHomeTenant,
                ClientId               = TestConstants.ClientId,
                RawIdToken             = MockHelpers.CreateIdToken(TestConstants.UniqueId, TestConstants.DisplayableId),
                RawClientInfo          = MockHelpers.CreateClientInfo(),
                TokenType              = "Bearer",
                ExpiresOnUnixTimestamp = MsalHelpers.DateTimeToUnixTimestamp(DateTime.UtcNow + TimeSpan.FromSeconds(3599)),
                ScopeSet               = TestConstants.Scope
            };

            atItem.IdToken    = IdToken.Parse(atItem.RawIdToken);
            atItem.ClientInfo = ClientInfo.CreateFromJson(atItem.RawClientInfo);
            AccessTokenCacheKey atKey = atItem.GetAccessTokenItemKey();

            atItem.AccessToken = atKey.ToString();
            cache.TokenCacheAccessor.AccessTokenCacheDictionary[atKey.ToString()] = JsonHelper.SerializeToJson(atItem);

            MockWebUI ui = new MockWebUI()
            {
                MockResult = new AuthorizationResult(AuthorizationStatus.Success,
                                                     TestConstants.AuthorityHomeTenant + "?code=some-code")
            };

            //add mock response for tenant endpoint discovery
            HttpMessageHandlerFactory.AddMockHandler(new MockHttpMessageHandler
            {
                Method          = HttpMethod.Get,
                ResponseMessage = MockHelpers.CreateOpenIdConfigurationResponse(TestConstants.AuthorityHomeTenant)
            });

            MockHttpMessageHandler mockHandler = new MockHttpMessageHandler();

            mockHandler.Method = HttpMethod.Post;

            mockHandler.ResponseMessage = MockHelpers.CreateSuccessTokenResponseMessage();
            HttpMessageHandlerFactory.AddMockHandler(mockHandler);

            AuthenticationRequestParameters parameters = new AuthenticationRequestParameters()
            {
                Authority      = authority,
                ClientId       = TestConstants.ClientId,
                Scope          = TestConstants.Scope,
                TokenCache     = cache,
                RequestContext = new RequestContext(Guid.Empty, null)
            };

            parameters.RedirectUri          = new Uri("some://uri");
            parameters.ExtraQueryParameters = "extra=qp";

            InteractiveRequest request = new InteractiveRequest(parameters,
                                                                TestConstants.ScopeForAnotherResource.ToArray(),
                                                                TestConstants.DisplayableId,
                                                                UIBehavior.SelectAccount, ui);
            Task <AuthenticationResult> task = request.RunAsync();

            task.Wait();
            AuthenticationResult result = task.Result;

            Assert.IsNotNull(result);
            Assert.AreEqual(1, cache.TokenCacheAccessor.RefreshTokenCacheDictionary.Count);
            Assert.AreEqual(2, cache.TokenCacheAccessor.AccessTokenCacheDictionary.Count);
            Assert.AreEqual(result.AccessToken, "some-access-token");

            Assert.IsTrue(HttpMessageHandlerFactory.IsMocksQueueEmpty, "All mocks should have been consumed");

            Assert.IsNotNull(_myReceiver.EventsReceived.Find(anEvent =>  // Expect finding such an event
                                                             anEvent[EventBase.EventNameKey].EndsWith("ui_event") && anEvent[UiEvent.UserCancelledKey] == "false"));
            Assert.IsNotNull(_myReceiver.EventsReceived.Find(anEvent =>  // Expect finding such an event
                                                             anEvent[EventBase.EventNameKey].EndsWith("api_event") && anEvent[ApiEvent.UiBehaviorKey] == "select_account"));
        }
        async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync(
            AuthenticationRequestParameters requestParams,
            MsalTokenResponse response)
        {
            var tenantId = Authority
                           .CreateAuthority(requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority)
                           .GetTenantId();

            bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs;

            IdToken idToken = IdToken.Parse(response.IdToken);

            string subject = idToken?.Subject;

            if (idToken != null && string.IsNullOrEmpty(subject))
            {
                requestParams.RequestContext.Logger.Warning("Subject not present in Id token");
            }

            string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken);

            // Do a full instance discovery when saving tokens (if not cached),
            // so that the PreferredNetwork environment is up to date.
            var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager
                                            .GetMetadataEntryAsync(
                requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority,
                requestParams.RequestContext)
                                            .ConfigureAwait(false);

            var msalAccessTokenCacheItem =
                new MsalAccessTokenCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    requestParams.ClientId,
                    response,
                    tenantId,
                    subject,
                    requestParams.AuthenticationScheme.KeyId)
            {
                UserAssertionHash = requestParams.UserAssertion?.AssertionHash,
                IsAdfs            = isAdfsAuthority
            };

            MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null;
            MsalIdTokenCacheItem      msalIdTokenCacheItem      = null;

            if (idToken != null)
            {
                msalIdTokenCacheItem = new MsalIdTokenCacheItem(
                    instanceDiscoveryMetadata.PreferredCache,
                    requestParams.ClientId,
                    response,
                    tenantId,
                    subject)
                {
                    IsAdfs = isAdfsAuthority
                };
            }

            await _semaphoreSlim.WaitAsync().ConfigureAwait(false);

            try
            {
                try
                {
                    Account account  = null;
                    string  username = isAdfsAuthority ? idToken?.Upn : preferredUsername;
                    if (msalAccessTokenCacheItem.HomeAccountId != null)
                    {
                        account = new Account(
                            msalAccessTokenCacheItem.HomeAccountId,
                            username,
                            instanceDiscoveryMetadata.PreferredCache);
                    }
                    var args = new TokenCacheNotificationArgs(this, ClientId, account, true, (this as ITokenCacheInternal).IsApplicationCache);

#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = true;
#pragma warning restore CS0618 // Type or member is obsolete

                    await(this as ITokenCacheInternal).OnBeforeAccessAsync(args).ConfigureAwait(false);
                    try
                    {
                        await(this as ITokenCacheInternal).OnBeforeWriteAsync(args).ConfigureAwait(false);

                        DeleteAccessTokensWithIntersectingScopes(
                            requestParams,
                            instanceDiscoveryMetadata.Aliases,
                            tenantId,
                            msalAccessTokenCacheItem.ScopeSet,
                            msalAccessTokenCacheItem.HomeAccountId,
                            msalAccessTokenCacheItem.TokenType);

                        _accessor.SaveAccessToken(msalAccessTokenCacheItem);

                        if (idToken != null)
                        {
                            _accessor.SaveIdToken(msalIdTokenCacheItem);
                            var msalAccountCacheItem = new MsalAccountCacheItem(
                                instanceDiscoveryMetadata.PreferredCache,
                                response,
                                preferredUsername,
                                tenantId);

                            //The ADFS direct scenario does not return client info so the home account id is acquired from the subject
                            if (isAdfsAuthority && String.IsNullOrEmpty(msalAccountCacheItem.HomeAccountId))
                            {
                                msalAccountCacheItem.HomeAccountId = idToken.Subject;
                            }

                            _accessor.SaveAccount(msalAccountCacheItem);
                        }

                        // if server returns the refresh token back, save it in the cache.
                        if (response.RefreshToken != null)
                        {
                            msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem(
                                instanceDiscoveryMetadata.PreferredCache,
                                requestParams.ClientId,
                                response,
                                subject);

                            if (!_featureFlags.IsFociEnabled)
                            {
                                msalRefreshTokenCacheItem.FamilyId = null;
                            }

                            requestParams.RequestContext.Logger.Info("Saving RT in cache...");
                            _accessor.SaveRefreshToken(msalRefreshTokenCacheItem);
                        }

                        UpdateAppMetadata(requestParams.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId);

                        // save RT in ADAL cache for public clients
                        // do not save RT in ADAL cache for MSAL B2C scenarios
                        if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AuthorityType.B2C))
                        {
                            var authorityWithPrefferedCache = Authority.CreateAuthorityWithEnvironment(
                                requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo,
                                instanceDiscoveryMetadata.PreferredCache);

                            CacheFallbackOperations.WriteAdalRefreshToken(
                                Logger,
                                LegacyCachePersistence,
                                msalRefreshTokenCacheItem,
                                msalIdTokenCacheItem,
                                authorityWithPrefferedCache.AuthorityInfo.CanonicalAuthority,
                                msalIdTokenCacheItem.IdToken.ObjectId, response.Scope);
                        }
                    }
                    finally
                    {
                        await(this as ITokenCacheInternal).OnAfterAccessAsync(args).ConfigureAwait(false);
                    }

                    return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem));
                }
                finally
                {
#pragma warning disable CS0618 // Type or member is obsolete
                    HasStateChanged = false;
#pragma warning restore CS0618 // Type or member is obsolete
                }
            }
            finally
            {
                _semaphoreSlim.Release();
            }
        }
 internal MsalIdTokenCacheItem()
 {
     CredentialType = StorageJsonValues.CredentialTypeIdToken;
     idTokenLazy    = new Lazy <IdToken>(() => IdToken.Parse(Secret));
 }