Microsoft Secure Tech Accelerator
Apr 03 2024, 07:00 AM - 11:00 AM (PDT)
Microsoft Tech Community

Microsoft identity platform returns invalid scopes?

MVP

I added a multitenant app registration with below permissions (image api-perm):

api-perm.jpg
I build ASP.NET MVC web app with authorization code flow and MSAL.NET. I use this code as a base - https://github.com/microsoftgraph/msgraph-training-aspnetmvcapp/blob/master/Demos/03-add-msgraph/REA...
I want a user to consent to all static permissions I specified (see image above) on the first sign-in. For that purpose I use .default scope.  So my scope for an authorization code request is https://graph.microsoft.com/.default. When a user signs in for the first time, a user will see consent screen with all permissions (image):

all-perm.jpg

Here is the request from fiddler, /common/oauth2/v2.0/authorize? endpoint (image):

consent-auth-code.jpg

Then I use AcquireTokenByAuthorizationCode to get an access token to read a user profile from MS Graph (code):

 

var idClient = ConfidentialClientApplicationBuilder.Create(appId)
.WithRedirectUri(redirectUri)
.WithClientSecret(appSecret)
.Build();

var signedInUser = new ClaimsPrincipal(notification.AuthenticationTicket.Identity);
var tokenStore = new SessionTokenStore(idClient.UserTokenCache, HttpContext.Current, signedInUser);

try
{
string[] scopes = { "User.Read" };

var result = await idClient.AcquireTokenByAuthorizationCode(
scopes, notification.Code).ExecuteAsync();

var accounts = await idClient.GetAccountsAsync();

// second try to get User.Read scope
var result2 = await idClient.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();

var userDetails = await GraphHelper.GetUserDetailsAsync(result2.AccessToken);

var cachedUser = new CachedUser()
{
DisplayName = userDetails.DisplayName,
Email = string.IsNullOrEmpty(userDetails.Mail) ?
userDetails.UserPrincipalName : userDetails.Mail,
Avatar = string.Empty
};

tokenStore.SaveUserDetails(cachedUser);

public static async Task<User> GetUserDetailsAsync(string accessToken)
{
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
}));

return await graphClient.Me.Request().GetAsync();
}

 


However the token which is returned from /common/oauth2/v2.0/token endpoint doesn't have User.Read scope, despite that I explicitly request it in AcquireTokenByAuthorizationCode method(image):

no-user-auth-code.jpg

I even tried to get the token again with AcquireTokenSilent (refresh token flow), but once again User.Read scope is missing(image):

no-user-refresh.jpg

As a result, I have an error - Authorization_RequestDenied. "Insufficient privileges to complete the operation."

 

If I simply refresh the page and try to sign in with the same user again - no consent screen (because the user already consented), and no errors any more.

 

Do you have any ideas what's happening?

1 Reply

@Sergei Sergeev  Your forensic approach to understanding and identifying are impeccable ! Hey  Have you ever heard of Ansible linux tower system ! I coded 2 play books for there Moscone Frisco Oracle World Kick off ! I work for Tech Data Americas ! I work for Microsoft/IBM Oracle Sun/Linux Systems ! I had to code with PYTHON ! It is not compatible wit Ansible though . That is the beauty of  it ! That is what makes it so stble ! Check It Out ! Thanks   Richardrut