Hi, I have the following scenario:
- tenant A contains users
- tenant B has Azure resources (e.g. blob storage)
- users from tenant A are invited as guest users in tenant B
- access to the resources in tenant B is setup via RBAC
Say I would like to create a Nextjs app that allows users to sign in and list the files in blob storage. All API calls to Azure APIs are handled server side from the Nextjs app (either via React Server Components or Route Handlers).
If I understand correctly this would need On-behalf-of flow. And I got this working partially.
What happens in the app (based on the info in this @azure/identity sample):
- user signs in via authorization code flow (with PKCE). For this purpose an App Registration has been created in tenant A. This App Registration has the basic Microsoft Graph delegated permissions (email, openid, profile, User.Read). The same App Registration also exposes an API with App ID URI
api://{clientId}
. A single scope has been defined (access_obo_user
) and the client has been configured to be an authorized client of itself. Finally, this scope has also been added as a delegated API permission. Also in the manifest the client id has been added as a knownClientApplication
.
- sign in (via NextAuth) works perfectly fine. The scope used is
openid profile email api://{clientId}/.default
. This results in a valid ID token and access token with the scope defined in the Expose an API blade (access_obo_user
).
- before calling the Azure Storage API (via azure javascript SDK) I exchange this access token for a new one using the
OnBehalfOfCredential
. For this I use the client ID and secret from tenant A (the same as for the initial user sign in).
But then I call the BlobServiceClient.listContainers() function for a storage account in tenant B with the OnBehalfOf credential using azure storage sdk for javascript. This however leads to an error:
invalid_grant: AADSTS65001: The user or administrator has not consented to use the application with ID '<clientId>' named '<App Registration Name>'. Send an interactive authorization request for this user and resource.
As an alternative approach I tried to retrieve the token explicitly (via OnBehalfOfCredential.getToken
with the scope api://<clientId>/.default
) and then pass this retrieved token to the BlobServiceClient using a TokenCredential implementation that simply returns the previously retrieved token.
This leads to a different error:
Server failed to authenticate the request. Please refer to the information in the www-authenticate header.
The www-authenticate
response header contains the following:
Bearer authorization_uri=https://login.microsoftonline.com/<tenant B guid>/oauth2/authorize resource_id=https://storage.azure.com
I was under the impression that no consent would be needed when this has been set up as described above. What am I missing and what is needed to get this scenario working?