Which Version of MSAL are you using ?
MSAL 4.3.1
Platform
.NET Core 2.2
What authentication flow has the issue?
Other? - please describe;
Is this a new or existing app?
This is a new app or experiment that I'm doing by modifying the following sample project.
https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/2.%20Web%20API%20now%20calls%20Microsoft%20Graph
I would like to get access token OBO for Graph API and Dynamics CRM API by providing scopes together and getting access token back with multiple audiences, so that it can be used for both of them.
Repro
public async Task<string> CallGraphApiOnBehalfOfUser()
{
string[] scopes = { "https://graph.microsoft.com/User.Read", "https://admin.services.crm.dynamics.com/user_impersonation" };
// we use MSAL.NET to get a token to call the API On Behalf Of the current user
try
{
string accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes);
dynamic me = await CallGraphApiOnBehalfOfUser(accessToken);
return me.userPrincipalName;
}
catch (MsalUiRequiredException ex)
{
_tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(scopes, ex);
return string.Empty;
}
}
Expected behavior
Access token should be returned with multiple audiences.
Actual behavior
Access token returned is only for audience for which the scope is mentioned first in the scopes array.
Possible Solution
Additional context/ Logs / Screenshots
I've tried many combinations but none of them work:
"user.read" "user_impersonation"
"user.read user_impersonation"
"https://graph.microsoft.com//User.Read", "https://admin.services.crm.dynamics.com//user_impersonation"
Tried double forward slashes, single slashes, nothing worked.
I've also tried with Postman and it has the same behavior, in that it returns the access token with audience only for the scope that is mentioned first.
@mohsinonxrm : as you have noticed with Postman, Azure AD does not provide scopes for two resources, only the first one. The way to go is to do two calls (one to get the scopes for the first audience, and the second to get the scopes from the second audience), this will work (and the token should accumulate the scope, I'd think. Something like this:
public async Task<string> CallGraphApiOnBehalfOfUser()
{
string[] scopesOfDifferentAudiances= { "https://graph.microsoft.com/User.Read", "https://admin.services.crm.dynamics.com/user_impersonation" };
// we use MSAL.NET to get a token to call the API On Behalf Of the current user
try
{
string accessToken;
foreach(string resource in scopesOfDifferentAudiances)
{
accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes);
}
dynamic me = await CallGraphApiOnBehalfOfUser(accessToken);
return me.userPrincipalName;
}
catch (MsalUiRequiredException ex)
{
_tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(scopes, ex);
return string.Empty;
}
}
@jmprieur , understood. I also reviewed the article you mentioned. Is this something that MSAL will allow in the future? Also, what's the best practice when it comes to scenario like this?
@mohsinonxrm : this is not a limitation of MSAL, but of Azure AD. But I'll inquire if there are plans to change that.
The best practice is to get the tokens with the right scopes just before calling the API.
@jmprieur , something like option # 2 from:
https://www.pingidentity.com/en/company/blog/posts/2019/oauth2-access-token-multiple-resources-usage-strategies.html
Thanks for the link!
In general, multiple audience scenarios are a security risk, which is why we don't currently expose a way of building it. The main concern here, that Ping leaves out, is that you must trust that every API and micro service receiving a multi audience will never be compromised. If they're compromised and get a token that can be used to call another audience, they can - and now your data is at risk, and possibly your business if the token has write permissions.
We are working to propose an update to the JWT standards to build tokens that can safely be issued with multiple audiences - but that adds complexity on the client side.
At this time, the secure (and only) option is to get multiple tokens for multiple audiences. This is handled by the libraries including refreshing and caching.
@hpsin , thanks for the explanation. This helps.
@mohsinonxrm closing the question as Hirsch answered. Feel free to reopen if you disagree.
Most helpful comment
Thanks for the link!
In general, multiple audience scenarios are a security risk, which is why we don't currently expose a way of building it. The main concern here, that Ping leaves out, is that you must trust that every API and micro service receiving a multi audience will never be compromised. If they're compromised and get a token that can be used to call another audience, they can - and now your data is at risk, and possibly your business if the token has write permissions.
We are working to propose an update to the JWT standards to build tokens that can safely be issued with multiple audiences - but that adds complexity on the client side.
At this time, the secure (and only) option is to get multiple tokens for multiple audiences. This is handled by the libraries including refreshing and caching.