diff --git a/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs b/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs index 588a2814db..e844dbdec8 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs @@ -35,8 +35,9 @@ internal sealed class TokenCredentialCache : IDisposable // The token refresh retries half the time. Given default of 1hr it will retry at 30m, 15, 7.5, 3.75, 1.875 // If the background refresh fails with less than a minute then just allow the request to hit the exception. public static readonly TimeSpan MinimumTimeBetweenBackgroundRefreshInterval = TimeSpan.FromMinutes(1); - + private const string ScopeFormat = "https://{0}/.default"; + private readonly TokenRequestContext tokenRequestContext; private readonly TokenCredential tokenCredential; private readonly CancellationTokenSource cancellationTokenSource; @@ -62,13 +63,17 @@ internal TokenCredentialCache( if (accountEndpoint == null) { throw new ArgumentNullException(nameof(accountEndpoint)); - } - - this.tokenRequestContext = new TokenRequestContext(new string[] - { - string.Format(TokenCredentialCache.ScopeFormat, accountEndpoint.Host) - }); - + } + + string? scopeOverride = ConfigurationManager.AADScopeOverrideValue(defaultValue: null); + + this.tokenRequestContext = new TokenRequestContext(new string[] + { + !string.IsNullOrEmpty(scopeOverride) + ? scopeOverride + : string.Format(TokenCredentialCache.ScopeFormat, accountEndpoint.Host) + }); + if (backgroundTokenCredentialRefreshInterval.HasValue) { if (backgroundTokenCredentialRefreshInterval.Value <= TimeSpan.Zero) diff --git a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs index 622f81c81b..f9e8dbe93b 100644 --- a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs +++ b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs @@ -43,7 +43,12 @@ internal static class ConfigurationManager /// /// Environment variable name to enable thin client mode. /// - internal static readonly string ThinClientModeEnabled = "AZURE_COSMOS_THIN_CLIENT_ENABLED"; + internal static readonly string ThinClientModeEnabled = "AZURE_COSMOS_THIN_CLIENT_ENABLED"; + + /// + /// Environment variable to override AAD scope. + /// + internal static readonly string AADScopeOverride = "AZURE_COSMOS_AAD_SCOPE_OVERRIDE"; /// /// A read-only string containing the environment variable name for capturing the consecutive failure count for reads, before triggering per partition @@ -183,6 +188,20 @@ public static bool IsThinClientEnabled( .GetEnvironmentVariable( variable: ConfigurationManager.ThinClientModeEnabled, defaultValue: defaultValue); + } + + /// + /// Gets the AAD scope value to override. + /// + /// Emoty string for AAD scope if no scope value is provided. + /// AAD scope value. + public static string AADScopeOverrideValue( + string defaultValue) + { + return ConfigurationManager + .GetEnvironmentVariable( + variable: ConfigurationManager.AADScopeOverride, + defaultValue: defaultValue); } /// diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs index 01ffcdc674..40e9a27e90 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs @@ -38,7 +38,7 @@ public async Task AadMockTest(ConnectionMode connectionMode) try { (string endpoint, string authKey) = TestCommon.GetAccountInfo(); - LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential(authKey); + LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential(expectedScope: "https://127.0.0.1/.default", masterKey: authKey); CosmosClientOptions clientOptions = new CosmosClientOptions() { ConnectionMode = connectionMode, @@ -140,8 +140,9 @@ void GetAadTokenCallBack( (string endpoint, string authKey) = TestCommon.GetAccountInfo(); LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential( - authKey, - GetAadTokenCallBack); + expectedScope: "https://127.0.0.1/.default", + masterKey: authKey, + getTokenCallback: GetAadTokenCallBack); CosmosClientOptions clientOptions = new CosmosClientOptions() { @@ -191,8 +192,9 @@ void GetAadTokenCallBack( (string endpoint, string authKey) = TestCommon.GetAccountInfo(); LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential( - authKey, - GetAadTokenCallBack); + expectedScope: "https://127.0.0.1/.default", + masterKey: authKey, + getTokenCallback: GetAadTokenCallBack); CosmosClientOptions clientOptions = new CosmosClientOptions() { @@ -232,8 +234,9 @@ void GetAadTokenCallBack( (string endpoint, string authKey) = TestCommon.GetAccountInfo(); LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential( - authKey, - GetAadTokenCallBack); + expectedScope: "https://127.0.0.1/.default", + masterKey: authKey, + getTokenCallback: GetAadTokenCallBack); CosmosClientOptions clientOptions = new CosmosClientOptions() { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs index e71eea6ca0..c40a5e331f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs @@ -18,15 +18,18 @@ public class LocalEmulatorTokenCredential : TokenCredential private readonly DateTime? DefaultDateTime = null; private readonly Action GetTokenCallback; private readonly string masterKey; + private readonly string expectedScope; - internal LocalEmulatorTokenCredential( + internal LocalEmulatorTokenCredential( + string expectedScope, string masterKey = null, Action getTokenCallback = null, DateTime? defaultDateTime = null) { this.masterKey = masterKey; this.GetTokenCallback = getTokenCallback; - this.DefaultDateTime = defaultDateTime; + this.DefaultDateTime = defaultDateTime; + this.expectedScope = expectedScope; } public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) @@ -40,9 +43,8 @@ public override ValueTask GetTokenAsync(TokenRequestContext request } private AccessToken GetAccessToken(TokenRequestContext requestContext, CancellationToken cancellationToken) - { - // Verify that the request context is a valid URI - Assert.AreEqual("https://127.0.0.1/.default", requestContext.Scopes.First()); + { + Assert.AreEqual(this.expectedScope, requestContext.Scopes.First()); this.GetTokenCallback?.Invoke( requestContext, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs index bdf4fcbe29..b98c339402 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs @@ -84,8 +84,9 @@ public async Task ResourceTokenAsync() [TestMethod] public async Task TokenAuthAsync() { - LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential( - "VGhpcyBpcyBhIHNhbXBsZSBzdHJpbmc=", + LocalEmulatorTokenCredential simpleEmulatorTokenCredential = new LocalEmulatorTokenCredential( + expectedScope: "https://127.0.0.1/.default", + masterKey: "VGhpcyBpcyBhIHNhbXBsZSBzdHJpbmc=", defaultDateTime: new DateTime(2030, 9, 21, 9, 9, 9, DateTimeKind.Utc)); using AuthorizationTokenProvider cosmosAuthorization = new AuthorizationTokenProviderTokenCredential( @@ -103,7 +104,7 @@ public async Task TokenAuthAsync() AuthorizationTokenType.PrimaryMasterKey); Assert.AreEqual( - "type%3daad%26ver%3d1.0%26sig%3dew0KICAgICAgICAgICAgICAgICJhbGciOiJSUzI1NiIsDQogICAgICAgICAgICAgICAgImtpZCI6InhfOUtTdXNLVTVZY0hmNCIsDQogICAgICAgICAgICAgICAgInR5cCI6IkpXVCINCiAgICAgICAgICAgIH0.ew0KICAgICAgICAgICAgICAgICJvaWQiOiI5NjMxMzAzNC00NzM5LTQzY2ItOTNjZC03NDE5M2FkYmU1YjYiLA0KICAgICAgICAgICAgICAgICJ0aWQiOiI3YjE5OTlhMS1kZmQ3LTQ0MGUtODIwNC0wMDE3MDk3OWI5ODQiLA0KICAgICAgICAgICAgICAgICJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLA0KICAgICAgICAgICAgICAgICJncm91cHMiOlsNCiAgICAgICAgICAgICAgICAgICAgIjdjZTFkMDAzLTRjYjMtNDg3OS1iN2M1LTc0MDYyYTM1YzY2ZSIsDQogICAgICAgICAgICAgICAgICAgICJlOTlmZjMwYy1jMjI5LTRjNjctYWIyOS0zMGE2YWViYzNlNTgiLA0KICAgICAgICAgICAgICAgICAgICAiNTU0OWJiNjItYzc3Yi00MzA1LWJkYTktOWVjNjZiODVkOWU0IiwNCiAgICAgICAgICAgICAgICAgICAgImM0NGZkNjg1LTVjNTgtNDUyYy1hYWY3LTEzY2U3NTE4NGY2NSIsDQogICAgICAgICAgICAgICAgICAgICJiZTg5NTIxNS1lYWI1LTQzYjctOTUzNi05ZWY4ZmUxMzAzMzAiDQogICAgICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAibmJmIjoxOTE2MjEyMTQ5LA0KICAgICAgICAgICAgICAgICJleHAiOjE5MTYyMTU3NDksDQogICAgICAgICAgICAgICAgImlhdCI6MTU5NjU5MjMzNSwNCiAgICAgICAgICAgICAgICAiaXNzIjoiaHR0cHM6Ly9zdHMuZmFrZS1pc3N1ZXIubmV0LzdiMTk5OWExLWRmZDctNDQwZS04MjA0LTAwMTcwOTc5Yjk4NCIsDQogICAgICAgICAgICAgICAgImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0LmxvY2FsaG9zdCINCiAgICAgICAgICAgIH0.VkdocGN5QnBjeUJoSUhOaGJYQnNaU0J6ZEhKcGJtYz0", + "type%3daad%26ver%3d1.0%26sig%3dewogICAgICAgICAgICAgICAgImFsZyI6IlJTMjU2IiwKICAgICAgICAgICAgICAgICJraWQiOiJ4XzlLU3VzS1U1WWNIZjQiLAogICAgICAgICAgICAgICAgInR5cCI6IkpXVCIKICAgICAgICAgICAgfQ.ewogICAgICAgICAgICAgICAgIm9pZCI6Ijk2MzEzMDM0LTQ3MzktNDNjYi05M2NkLTc0MTkzYWRiZTViNiIsCiAgICAgICAgICAgICAgICAidGlkIjoiN2IxOTk5YTEtZGZkNy00NDBlLTgyMDQtMDAxNzA5NzliOTg0IiwKICAgICAgICAgICAgICAgICJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLAogICAgICAgICAgICAgICAgImdyb3VwcyI6WwogICAgICAgICAgICAgICAgICAgICI3Y2UxZDAwMy00Y2IzLTQ4NzktYjdjNS03NDA2MmEzNWM2NmUiLAogICAgICAgICAgICAgICAgICAgICJlOTlmZjMwYy1jMjI5LTRjNjctYWIyOS0zMGE2YWViYzNlNTgiLAogICAgICAgICAgICAgICAgICAgICI1NTQ5YmI2Mi1jNzdiLTQzMDUtYmRhOS05ZWM2NmI4NWQ5ZTQiLAogICAgICAgICAgICAgICAgICAgICJjNDRmZDY4NS01YzU4LTQ1MmMtYWFmNy0xM2NlNzUxODRmNjUiLAogICAgICAgICAgICAgICAgICAgICJiZTg5NTIxNS1lYWI1LTQzYjctOTUzNi05ZWY4ZmUxMzAzMzAiCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgIm5iZiI6MTkxNjIxMjE0OSwKICAgICAgICAgICAgICAgICJleHAiOjE5MTYyMTU3NDksCiAgICAgICAgICAgICAgICAiaWF0IjoxNTk2NTkyMzM1LAogICAgICAgICAgICAgICAgImlzcyI6Imh0dHBzOi8vc3RzLmZha2UtaXNzdWVyLm5ldC83YjE5OTlhMS1kZmQ3LTQ0MGUtODIwNC0wMDE3MDk3OWI5ODQiLAogICAgICAgICAgICAgICAgImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0LmxvY2FsaG9zdCIKICAgICAgICAgICAgfQ.VkdocGN5QnBjeUJoSUhOaGJYQnNaU0J6ZEhKcGJtYz0", token); Assert.IsNull(payload); } @@ -118,7 +119,7 @@ public async Task TokenAuthAsync() AuthorizationTokenType.PrimaryMasterKey); Assert.AreEqual( - "type%3daad%26ver%3d1.0%26sig%3dew0KICAgICAgICAgICAgICAgICJhbGciOiJSUzI1NiIsDQogICAgICAgICAgICAgICAgImtpZCI6InhfOUtTdXNLVTVZY0hmNCIsDQogICAgICAgICAgICAgICAgInR5cCI6IkpXVCINCiAgICAgICAgICAgIH0.ew0KICAgICAgICAgICAgICAgICJvaWQiOiI5NjMxMzAzNC00NzM5LTQzY2ItOTNjZC03NDE5M2FkYmU1YjYiLA0KICAgICAgICAgICAgICAgICJ0aWQiOiI3YjE5OTlhMS1kZmQ3LTQ0MGUtODIwNC0wMDE3MDk3OWI5ODQiLA0KICAgICAgICAgICAgICAgICJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLA0KICAgICAgICAgICAgICAgICJncm91cHMiOlsNCiAgICAgICAgICAgICAgICAgICAgIjdjZTFkMDAzLTRjYjMtNDg3OS1iN2M1LTc0MDYyYTM1YzY2ZSIsDQogICAgICAgICAgICAgICAgICAgICJlOTlmZjMwYy1jMjI5LTRjNjctYWIyOS0zMGE2YWViYzNlNTgiLA0KICAgICAgICAgICAgICAgICAgICAiNTU0OWJiNjItYzc3Yi00MzA1LWJkYTktOWVjNjZiODVkOWU0IiwNCiAgICAgICAgICAgICAgICAgICAgImM0NGZkNjg1LTVjNTgtNDUyYy1hYWY3LTEzY2U3NTE4NGY2NSIsDQogICAgICAgICAgICAgICAgICAgICJiZTg5NTIxNS1lYWI1LTQzYjctOTUzNi05ZWY4ZmUxMzAzMzAiDQogICAgICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAibmJmIjoxOTE2MjEyMTQ5LA0KICAgICAgICAgICAgICAgICJleHAiOjE5MTYyMTU3NDksDQogICAgICAgICAgICAgICAgImlhdCI6MTU5NjU5MjMzNSwNCiAgICAgICAgICAgICAgICAiaXNzIjoiaHR0cHM6Ly9zdHMuZmFrZS1pc3N1ZXIubmV0LzdiMTk5OWExLWRmZDctNDQwZS04MjA0LTAwMTcwOTc5Yjk4NCIsDQogICAgICAgICAgICAgICAgImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0LmxvY2FsaG9zdCINCiAgICAgICAgICAgIH0.VkdocGN5QnBjeUJoSUhOaGJYQnNaU0J6ZEhKcGJtYz0", token); + "type%3daad%26ver%3d1.0%26sig%3dewogICAgICAgICAgICAgICAgImFsZyI6IlJTMjU2IiwKICAgICAgICAgICAgICAgICJraWQiOiJ4XzlLU3VzS1U1WWNIZjQiLAogICAgICAgICAgICAgICAgInR5cCI6IkpXVCIKICAgICAgICAgICAgfQ.ewogICAgICAgICAgICAgICAgIm9pZCI6Ijk2MzEzMDM0LTQ3MzktNDNjYi05M2NkLTc0MTkzYWRiZTViNiIsCiAgICAgICAgICAgICAgICAidGlkIjoiN2IxOTk5YTEtZGZkNy00NDBlLTgyMDQtMDAxNzA5NzliOTg0IiwKICAgICAgICAgICAgICAgICJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLAogICAgICAgICAgICAgICAgImdyb3VwcyI6WwogICAgICAgICAgICAgICAgICAgICI3Y2UxZDAwMy00Y2IzLTQ4NzktYjdjNS03NDA2MmEzNWM2NmUiLAogICAgICAgICAgICAgICAgICAgICJlOTlmZjMwYy1jMjI5LTRjNjctYWIyOS0zMGE2YWViYzNlNTgiLAogICAgICAgICAgICAgICAgICAgICI1NTQ5YmI2Mi1jNzdiLTQzMDUtYmRhOS05ZWM2NmI4NWQ5ZTQiLAogICAgICAgICAgICAgICAgICAgICJjNDRmZDY4NS01YzU4LTQ1MmMtYWFmNy0xM2NlNzUxODRmNjUiLAogICAgICAgICAgICAgICAgICAgICJiZTg5NTIxNS1lYWI1LTQzYjctOTUzNi05ZWY4ZmUxMzAzMzAiCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgIm5iZiI6MTkxNjIxMjE0OSwKICAgICAgICAgICAgICAgICJleHAiOjE5MTYyMTU3NDksCiAgICAgICAgICAgICAgICAiaWF0IjoxNTk2NTkyMzM1LAogICAgICAgICAgICAgICAgImlzcyI6Imh0dHBzOi8vc3RzLmZha2UtaXNzdWVyLm5ldC83YjE5OTlhMS1kZmQ3LTQ0MGUtODIwNC0wMDE3MDk3OWI5ODQiLAogICAgICAgICAgICAgICAgImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0LmxvY2FsaG9zdCIKICAgICAgICAgICAgfQ.VkdocGN5QnBjeUJoSUhOaGJYQnNaU0J6ZEhKcGJtYz0", token); Assert.IsNull(payload); } @@ -132,10 +133,54 @@ public async Task TokenAuthAsync() AuthorizationTokenType.PrimaryMasterKey); Assert.AreEqual( - "type%3daad%26ver%3d1.0%26sig%3dew0KICAgICAgICAgICAgICAgICJhbGciOiJSUzI1NiIsDQogICAgICAgICAgICAgICAgImtpZCI6InhfOUtTdXNLVTVZY0hmNCIsDQogICAgICAgICAgICAgICAgInR5cCI6IkpXVCINCiAgICAgICAgICAgIH0.ew0KICAgICAgICAgICAgICAgICJvaWQiOiI5NjMxMzAzNC00NzM5LTQzY2ItOTNjZC03NDE5M2FkYmU1YjYiLA0KICAgICAgICAgICAgICAgICJ0aWQiOiI3YjE5OTlhMS1kZmQ3LTQ0MGUtODIwNC0wMDE3MDk3OWI5ODQiLA0KICAgICAgICAgICAgICAgICJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLA0KICAgICAgICAgICAgICAgICJncm91cHMiOlsNCiAgICAgICAgICAgICAgICAgICAgIjdjZTFkMDAzLTRjYjMtNDg3OS1iN2M1LTc0MDYyYTM1YzY2ZSIsDQogICAgICAgICAgICAgICAgICAgICJlOTlmZjMwYy1jMjI5LTRjNjctYWIyOS0zMGE2YWViYzNlNTgiLA0KICAgICAgICAgICAgICAgICAgICAiNTU0OWJiNjItYzc3Yi00MzA1LWJkYTktOWVjNjZiODVkOWU0IiwNCiAgICAgICAgICAgICAgICAgICAgImM0NGZkNjg1LTVjNTgtNDUyYy1hYWY3LTEzY2U3NTE4NGY2NSIsDQogICAgICAgICAgICAgICAgICAgICJiZTg5NTIxNS1lYWI1LTQzYjctOTUzNi05ZWY4ZmUxMzAzMzAiDQogICAgICAgICAgICAgICAgXSwNCiAgICAgICAgICAgICAgICAibmJmIjoxOTE2MjEyMTQ5LA0KICAgICAgICAgICAgICAgICJleHAiOjE5MTYyMTU3NDksDQogICAgICAgICAgICAgICAgImlhdCI6MTU5NjU5MjMzNSwNCiAgICAgICAgICAgICAgICAiaXNzIjoiaHR0cHM6Ly9zdHMuZmFrZS1pc3N1ZXIubmV0LzdiMTk5OWExLWRmZDctNDQwZS04MjA0LTAwMTcwOTc5Yjk4NCIsDQogICAgICAgICAgICAgICAgImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0LmxvY2FsaG9zdCINCiAgICAgICAgICAgIH0.VkdocGN5QnBjeUJoSUhOaGJYQnNaU0J6ZEhKcGJtYz0", token); + "type%3daad%26ver%3d1.0%26sig%3dewogICAgICAgICAgICAgICAgImFsZyI6IlJTMjU2IiwKICAgICAgICAgICAgICAgICJraWQiOiJ4XzlLU3VzS1U1WWNIZjQiLAogICAgICAgICAgICAgICAgInR5cCI6IkpXVCIKICAgICAgICAgICAgfQ.ewogICAgICAgICAgICAgICAgIm9pZCI6Ijk2MzEzMDM0LTQ3MzktNDNjYi05M2NkLTc0MTkzYWRiZTViNiIsCiAgICAgICAgICAgICAgICAidGlkIjoiN2IxOTk5YTEtZGZkNy00NDBlLTgyMDQtMDAxNzA5NzliOTg0IiwKICAgICAgICAgICAgICAgICJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLAogICAgICAgICAgICAgICAgImdyb3VwcyI6WwogICAgICAgICAgICAgICAgICAgICI3Y2UxZDAwMy00Y2IzLTQ4NzktYjdjNS03NDA2MmEzNWM2NmUiLAogICAgICAgICAgICAgICAgICAgICJlOTlmZjMwYy1jMjI5LTRjNjctYWIyOS0zMGE2YWViYzNlNTgiLAogICAgICAgICAgICAgICAgICAgICI1NTQ5YmI2Mi1jNzdiLTQzMDUtYmRhOS05ZWM2NmI4NWQ5ZTQiLAogICAgICAgICAgICAgICAgICAgICJjNDRmZDY4NS01YzU4LTQ1MmMtYWFmNy0xM2NlNzUxODRmNjUiLAogICAgICAgICAgICAgICAgICAgICJiZTg5NTIxNS1lYWI1LTQzYjctOTUzNi05ZWY4ZmUxMzAzMzAiCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgIm5iZiI6MTkxNjIxMjE0OSwKICAgICAgICAgICAgICAgICJleHAiOjE5MTYyMTU3NDksCiAgICAgICAgICAgICAgICAiaWF0IjoxNTk2NTkyMzM1LAogICAgICAgICAgICAgICAgImlzcyI6Imh0dHBzOi8vc3RzLmZha2UtaXNzdWVyLm5ldC83YjE5OTlhMS1kZmQ3LTQ0MGUtODIwNC0wMDE3MDk3OWI5ODQiLAogICAgICAgICAgICAgICAgImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0LmxvY2FsaG9zdCIKICAgICAgICAgICAgfQ.VkdocGN5QnBjeUJoSUhOaGJYQnNaU0J6ZEhKcGJtYz0", token); Assert.IsNull(payload); } - } + } + + [DataTestMethod] + [DataRow("https://env-override/.default", "https://env-override/.default", DisplayName = "EnvVarOverride")] + [DataRow("https://cosmos.azure.com/.default", "https://cosmos.azure.com/.default", DisplayName = "EnvVarOverride_Fabric")] + [DataRow(null, "https://anyhost.documents.azure.com/.default", DisplayName = "NoEnvVar_DefaultScope")] + public async Task TokenCredentialCache_SetsCorrectScope_EnvOverrideOrDefault(string envVarValue, string expectedScope) + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", envVarValue); + + try + { + string anyHost = "anyhost.documents.azure.com"; + Uri anyUri = new Uri($"https://{anyHost}"); + + LocalEmulatorTokenCredential credential = new LocalEmulatorTokenCredential( + masterKey: "testkey", + expectedScope: expectedScope); + + using (AuthorizationTokenProvider authorization = new AuthorizationTokenProviderTokenCredential( + credential, + anyUri, + backgroundTokenCredentialRefreshInterval: TimeSpan.FromSeconds(1))) + { + StoreResponseNameValueCollection headers = new StoreResponseNameValueCollection(); + (string token, string payload) = await authorization.GetUserAuthorizationAsync( + "dbs\\test", + ResourceType.Database.ToResourceTypeString(), + "GET", + headers, + AuthorizationTokenType.PrimaryMasterKey); + + Assert.IsFalse(string.IsNullOrEmpty(token)); + Assert.IsNull(payload); + } + } + catch (Exception ex) + { + Assert.Fail($"Test failed with exception: {ex}"); + } + finally + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", null); + } + } [TestMethod] public void TestTokenCredentialCacheMaxAndMinValues()