コード例 #1
0
        public void TestCacheKeyForTenantAuthority()
        {
            // Arrange
            const string tenantId        = TestConstants.AadTenantId;
            var          appTokenCache   = new TokenCache(this._serviceBundle, isApplicationTokenCache: true);
            var          requestContext  = new RequestContext(this._serviceBundle, Guid.NewGuid());
            var          tenantAuthority = AuthorityInfo.FromAadAuthority(AzureCloudInstance.AzurePublic, tenant: tenantId, validateAuthority: false);
            var          acquireTokenCommonParameters = new AcquireTokenCommonParameters
            {
                ApiId             = ApiEvent.ApiIds.AcquireTokenForClient,
                AuthorityOverride = tenantAuthority
            };

            var parameters = new AuthenticationRequestParameters(
                this._serviceBundle,
                appTokenCache,
                acquireTokenCommonParameters,
                requestContext,
                Authority.CreateAuthority(tenantAuthority));


            // Act
            var actualKey = SuggestedWebCacheKeyFactory.GetKeyFromRequest(parameters);

            // Assert
            Assert.IsNotNull(actualKey);
            var expectedKey = $"{this._serviceBundle.Config.ClientId}_{tenantId}_AppTokenCache";

            Assert.AreEqual(expectedKey, actualKey);
        }
コード例 #2
0
        public void TestCacheKeyForADFSAuthority()
        {
            // Arrange
            var appTokenCache  = new TokenCache(this._serviceBundle, isApplicationTokenCache: true);
            var requestContext = new RequestContext(this._serviceBundle, Guid.NewGuid());
            var authority      = Authority.CreateAuthority(TestConstants.ADFSAuthority, true);

            requestContext.ServiceBundle.Config.AuthorityInfo = authority.AuthorityInfo;



            var acquireTokenCommonParameters = new AcquireTokenCommonParameters
            {
                ApiId = ApiEvent.ApiIds.AcquireTokenForClient,
            };

            var parameters = new AuthenticationRequestParameters(
                this._serviceBundle,
                appTokenCache,
                acquireTokenCommonParameters,
                requestContext,
                authority);


            // Act
            var actualKey = SuggestedWebCacheKeyFactory.GetKeyFromRequest(parameters);

            // Assert
            Assert.IsNotNull(actualKey);
            var expectedKey = $"{this._serviceBundle.Config.ClientId}__AppTokenCache";

            Assert.AreEqual(expectedKey, actualKey);
        }
コード例 #3
0
        internal static void PopulateDefaultAppTokenCache(
            ConfidentialClientApplication app,
            MsalAccessTokenCacheItem atItem = null)
        {
            if (atItem == null)
            {
                atItem = CreateAccessTokenItem();
            }

            InMemoryTokenCacheAccessor accessor = new InMemoryTokenCacheAccessor(new NullLogger());

            accessor.SaveAccessToken(atItem);

            string key = SuggestedWebCacheKeyFactory.GetClientCredentialKey(atItem.ClientId, atItem.TenantId);

            byte[] bytes = new TokenCacheJsonSerializer(accessor).Serialize(null);
            app.InMemoryPartitionedCacheSerializer.CachePartition[key] = bytes;

            // force a cache read
            var args = new TokenCacheNotificationArgs(
                app.AppTokenCacheInternal,
                app.AppConfig.ClientId,
                null,
                hasStateChanged: false,
                true,
                hasTokens: true,
                suggestedCacheKey: key);

            app.AppTokenCacheInternal.OnBeforeAccessAsync(args).GetAwaiter().GetResult();
        }
        public void TestCacheKeyForRemoveAccount()
        {
            // Arrange
            var appTokenCache   = new TokenCache(_serviceBundle, isApplicationTokenCache: true);
            var requestContext  = new RequestContext(_serviceBundle, Guid.NewGuid());
            var tenantAuthority = AuthorityInfo.FromAadAuthority(AzureCloudInstance.AzurePublic, tenant: TestConstants.AadTenantId, validateAuthority: false);
            var acquireTokenCommonParameters = new AcquireTokenCommonParameters
            {
                ApiId             = ApiEvent.ApiIds.RemoveAccount,
                AuthorityOverride = tenantAuthority
            };

            var parameters = new AuthenticationRequestParameters(
                _serviceBundle,
                appTokenCache,
                acquireTokenCommonParameters,
                requestContext,
                Authority.CreateAuthority(tenantAuthority),
                TestConstants.HomeAccountId)
            {
                Account = new Account(TestConstants.HomeAccountId, TestConstants.Username, TestConstants.ProductionPrefCacheEnvironment)
            };

            // Act
            var actualKey = SuggestedWebCacheKeyFactory.GetKeyFromRequest(parameters);

            // Assert
            Assert.IsNotNull(actualKey);
            Assert.AreEqual(parameters.HomeAccountId, actualKey);
        }
コード例 #5
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. ");
            }
        }
        private void PopulateAppCache(ConfidentialClientApplication cca, TokenDifference tokenDifference, int size)
        {
            Dictionary <string, InMemoryTokenCacheAccessor> accessors = new Dictionary <string, InMemoryTokenCacheAccessor>();
            string key = "";

            for (int i = 0; i < size; i++)
            {
                string tenantId = "tid";
                string scope    = "scope";

                switch (tokenDifference)
                {
                case TokenDifference.ByScope:
                    scope = $"scope_{i}";
                    break;

                case TokenDifference.ByTenant:
                    tenantId = $"tid_{i}";
                    break;

                default:
                    throw new NotImplementedException();
                }

                MsalAccessTokenCacheItem atItem = new MsalAccessTokenCacheItem(
                    TestConstants.ProductionPrefCacheEnvironment,
                    TestConstants.ClientId,
                    scope,
                    tenantId,
                    "",
                    new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(3600)),
                    new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(3600)),
                    null,
                    null);

                key = SuggestedWebCacheKeyFactory.GetClientCredentialKey(atItem.ClientId, atItem.TenantId);
                InMemoryTokenCacheAccessor accessor;
                if (!accessors.TryGetValue(key, out accessor))
                {
                    accessor       = new InMemoryTokenCacheAccessor(new NullLogger());
                    accessors[key] = accessor;
                }

                accessor.SaveAccessToken(atItem);

                if (tokenDifference == TokenDifference.ByTenant || (tokenDifference == TokenDifference.ByScope && i == size - 1))
                {
                    byte[] bytes = new TokenCacheJsonSerializer(accessor).Serialize(null);
                    cca.InMemoryPartitionedCacheSerializer.CachePartition[key] = bytes;
                }
            }

            // force a cache read, otherwise MSAL won't have the tokens in memory
            // force a cache read
            var args = new TokenCacheNotificationArgs(
                cca.AppTokenCacheInternal,
                cca.AppConfig.ClientId,
                null,
                hasStateChanged: false,
                true,
                hasTokens: true,
                cancellationToken: CancellationToken.None,
                suggestedCacheKey: key);

            cca.AppTokenCacheInternal.OnBeforeAccessAsync(args).GetAwaiter().GetResult();
        }
        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);
            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.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(),
                    suggestedCacheKey: suggestedWebCacheKey);

#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(),
                        suggestedCacheKey: suggestedWebCacheKey);

                    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();
            }
        }