I have SPA Angular application signing in to Azure AD. I have followed this guide so far.
The application is able to authenticate, generates me a token which is then appended to the requests going back to the .NET API.
Msal module is declared as follows:
MsalModule.forRoot(
new PublicClientApplication({
auth: {
clientId: "obscured9",
authority:
"obscured",
redirectUri: "http://localhost:4200",
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: isIE,
},
}),
{
interactionType: InteractionType.Popup,
authRequest: {
scopes: ["access_as_user"],
},
},
{
interactionType: InteractionType.Popup, // MSAL Interceptor Configuration
protectedResourceMap: new Map([
["Enter_the_Graph_Endpoint_Here/v1.0/me", ["user.read"]],
["https://localhost:7162", ["api://obscured/API"]],
]),
}
),
],
So far this works fine, when I try to authenticate to CosmosClient SDK with the token I'm receiving
The user or administrator has not consented to use the application with
The application registered in the azure ad doesn't have admin consent (which is company policy, the user impersonation should be able to grant enough permissions to access the cosmos DB instance)
On the backend I'm getting the token as follows:
var token = _tokenResolver.GetToken(); //Token from SPA
string[] scopes = { "access_as_user" };
string appKey = "obscured";
string clientId = "obscured";
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(appKey)
.WithTenantId("obscured")
.Build();
UserAssertion userAssertion = new UserAssertion(token,
"urn:ietf:params:oauth:grant-type:jwt-bearer");
var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync().Result;
// base64 encode
var token2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(result.AccessToken));
var credentials = new AzureKeyCredential(Convert.ToBase64String(Encoding.UTF8.GetBytes(token2)));
_cosmosClient = new CosmosClient("obscured",
credentials,
new CosmosClientOptions
{
AllowBulkExecution = true,
ApplicationName = "obscured",
ConnectionMode = ConnectionMode.Direct,
ConsistencyLevel = ConsistencyLevel.Session,
RequestTimeout = new TimeSpan(0,
0,
400),
Serializer = serializer,
MaxRetryAttemptsOnRateLimitedRequests = 10,
MaxRetryWaitTimeOnRateLimitedRequests = new TimeSpan(0,
0,
120)
});
// ensure created
await CreateDatabaseAndContainerIfNotExistsAsync();
_isLoaded = true;
}
This gives me the a new token, however as soon as I try to access the resource I'm getting the error message about not giving the consent. Is there a way to give the user consent either in the BE request or in the SPA for the cosmos DB?
EDIT: After grating the admin consent, the app keeps returning 403, the permissions are added as follows and the token is generated
System.AggregateException: One or more errors occurred. (Response status code does not indicate success: Forbidden (403); Substatus: 5301; ActivityId: bc1f49a1-4398-4610-8e51-539cb9a65fa7; Reason: (Request blocked by Auth upodi : Request is blocked because principal [5c4d3d80-546b-471b-bba3-de92008fc398] does not have required RBAC permissions to perform action [Microsoft.DocumentDB/databaseAccounts/readMetadata] on resource [/]. Learn more: https://aka.ms/cosmos-native-rbac.
ActivityId: bc1f49a1-4398-4610-8e51-539cb9a65fa7, Microsoft.Azure.Documents.Common/2.14.0, Windows/10.0.22621 cosmos-netstandard-sdk/3.30.8);)
---> Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: Forbidden (403); Substatus: 5301; ActivityId: bc1f49a1-4398-4610-8e51-539cb9a65fa7; Reason: (Request blocked by Auth upodi : Request is blocked because principal [5c4d3d80-546b-471b-bba3-de92008fc398] does not have required RBAC permissions to perform action [Microsoft.DocumentDB/databaseAccounts/readMetadata] on resource [/]. Learn more: https://aka.ms/cosmos-native-rbac.
ActivityId: bc1f49a1-4398-4610-8e51-539cb9a65fa7, Microsoft.Azure.Documents.Common/2.14.0, Windows/10.0.22621 cosmos-netstandard-sdk/3.30.8);
at Microsoft.Azure.Cosmos.GatewayStoreClient.ParseResponseAsync(HttpResponseMessage responseMessage, JsonSerializerSettings serializerSettings, DocumentServiceRequest request)
at Microsoft.Azure.Cosmos.GatewayAccountReader.GetDatabaseAccountAsync(Uri serviceEndpoint)
at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetAccountPropertiesHelper.GetAndUpdateAccountPropertiesAsync(Uri endpoint)
at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetAccountPropertiesHelper.GetAccountPropertiesAsync()
at Microsoft.Azure.Cosmos.GatewayAccountReader.InitializeReaderAsync()
at Microsoft.Azure.Cosmos.CosmosAccountServiceConfiguration.InitializeAsync()
at Microsoft.Azure.Cosmos.DocumentClient.InitializeGatewayConfigurationReaderAsync()
at Microsoft.Azure.Cosmos.DocumentClient.GetInitializationTaskAsync(IStoreClientFactory storeClientFactory)
at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync[TParam,TPolicy](Func`1 callbackMethod, Func`3 callbackMethodWithParam, Func`2 callbackMethodWithPolicy, TParam param, IRetryPolicy retryPolicy, IRetryPolicy`1 retryPolicyWithArg, Func`1 inBackoffAlternateCallbackMethod, Func`2 inBackoffAlternateCallbackMethodWithPolicy, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)
at Microsoft.Azure.Documents.ShouldRetryResult.ThrowIfDoneTrying(ExceptionDispatchInfo capturedException)
at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync[TParam,TPolicy](Func`1 callbackMethod, Func`3 callbackMethodWithParam, Func`2 callbackMethodWithPolicy, TParam param, IRetryPolicy retryPolicy, IRetryPolicy`1 retryPolicyWithArg, Func`1 inBackoffAlternateCallbackMethod, Func`2 inBackoffAlternateCallbackMethodWithPolicy, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)
at Microsoft.Azure.Cosmos.AsyncCacheNonBlocking`2.GetAsync(TKey key, Func`2 singleValueInitFunc, Func`2 forceRefresh)
at Microsoft.Azure.Cosmos.AsyncCacheNonBlocking`2.GetAsync(TKey key, Func`2 singleValueInitFunc, Func`2 forceRefresh)
at Microsoft.Azure.Cosmos.DocumentClient.EnsureValidClientAsync(ITrace trace)
The "403 forbidden" error usually occurs if the access token doesn't have sufficient permissions to perform the action.
I created an Azure AD Application and granted API permissions:
For sample, I tried to generate access token via Postman using On-Behalf-Of flow:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
grant_type:authorization_code
scope:api://ClientID/access_as_user
code:code
redirect_uri:https://jwt.ms
client_secret:-ClientSecret
Using the above generated access token, I generated token to access Cosmos DB:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:https://cosmos.azure.com/user_impersonation
grant_type:urn:ietf:params:oauth:grant-type:jwt-bearer
assertion:
requested_token_use:on_behalf_of
When I decoded the access token, the Cosmos DB scope is present like below:
Note that: To access Cosmos DB resource, you have to pass Cosmos DB API permission that is
https://cosmos.azure.com/user_impersonation
while generating the access token.
To resolve the error, check the below:
https://cosmos.azure.com
and scp is user_impersonation
.https://cosmos.azure.com/user_impersonation
while acquiring access token for Cosmos DB like below:string[] scopes = { "https://cosmos.azure.com/user_impersonation" }
If still the issue persists, maybe the resource needs RBAC role. Assign role based like Cosmos DB Account Reader Role
to the Azure AD Application based on the resource you are trying to access: