microsoft-identity-web
microsoft-identity-web copied to clipboard
[Question] RequiredScope doesn't work (for web APIs called by daemon apps)
Which version of Microsoft Identity Web are you using?
1.21.1
Where is the issue?
- Web API
- [ ] Protected web APIs (validating scopes)
Is this a new or an existing app?
This is a new app or an experiment.
Repro
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class SomeController : ControllerBase
{
[RequiredScope("myScopeName")]
public async Task CreateSomething()
{
return Task.CompletedTask();
}
}
Expected behavior
Request finished with 403 code when pass scope myScopeNameM2M.
Actual behavior
Request finishes with 200 code
Additional context / logs / screenshots / link to code
HttpContext.VerifyUserHasAnyAcceptedScope("myScopeName");
Works as expected.
@Anakael I cannot repro:

Can you share what you have in your Startup.cs? Thanks.
I did nothing special for it except the authentication by jwt token. Did I miss something?
I would need more information from your Startup.cs. Do you have the same content as when you do dotnet new webapi --auth SingleOrg?
I've run into this issue as well, repro'd in 2 different projects. What's interesting is it works/doesn't work as follows:
- works: for access_tokens that are on-behalf-of-the-user via the authorization_code flow the RequiredScope attrib is working as expected
- doesn't work: for access_tokens via client_credentials flow the RequiredScope attribute does not work (should return Forbidden when scope is missing), but using "HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);" does work
https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-verification-scope-app-roles?tabs=aspnetcore
So for now i'm using the VerifyUserHasAnyAcceptedScope method but would prefer to use the attribute
Thanks for the explanation, @spacattac. This is by design (and to follow the protocol), but I realize that the design might not be very good, so I'm tempted to start a discussion with the group to simplify things
What happens is:
- Web APIs called on behalf of users need to verify the Scopes.
- Web APIs called on behalf of an app (a daemon app) need to verify app roles
But from the point of view of the client app, in both cases you request scopes (you are exposed to app roles when you do the app registration though).
This is all explained in Protected web API: Verify scopes and app roles
Question for you:
- what behavior would you expect for the API (given the article above)
- how can we make things more discoverable?
Thanks @jmprieur . i had read that msft docs link and i didn't really understand why the daemon app couldn't/wouldn't use the scope the same way and didn't understand what an app role is/was. in reading more about Azure AD last night i did learn about the app registration and that app role designation is part of that registration. that explains why i didn't/don't really know what it is - i'm not actually using Azure AD and instead am using Okta for OIDC+OAuth and the daemon app registration doesn't have an app role. i can use Access Policies on the Okta auth server to grant the scopes to an app using client_credentials just like i can to an app acting on behalf of the user with authorization_code flow.
Thanks for the explanation, @spacattac.
RequiredScopes just checks at the scp or http://schemas.microsoft.com/identity/claims/scope claims. If Okta generates these, you should be good.
Thanks @jmprieur - yes, okta does generate the scope (scp) claim. i don't understand why the call to HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); works for me but [RequiredScopes(scopeRequiredByApi)] does not. It seems to me there should be parity with these.
I guess we'd need to see the structure of the token to understand this question? Are you using app.UseAuthorization? Did you customize the Authorization policies?
@jmprieur i'll grab some code and/or screen snippets to share/show ...
here's a snippet of scope check working as expected via the method but not the attribute.

i am using app.UseAuthorization. and in this project i do have 1 policy added but it's not used in the endpoint/method in this example. also, the other project where i've repro'd this same behavior does not have any additional policies.
services.AddAuthorization(options => { options.AddPolicy("AccessPersonPolicy", policy => policy.Requirements.Add(new IsAnAccessPersonRequirement())); });
and here are the User.Claims and you can see the scope claim has "access_token" but not "documents.read".

@spacattac : in the claims, the scope provided by Okta (item 7) has a value "access_token", whereas you want "documents.read". That's the reason.
@jmprieur - not exactly .... what i'm showing you above is i've intentionally left out the documents.create scope and i'm showing you how the [RequiredScopes("documents.create")] attribute did NOT block the call yet the VerifyUserHasAnyAcceptedScope("documents.create") DID block and returns Forbidden. why is there not parity between these two?
same issue here I have an access_token which I retieve through a Client from AD, then the client calls my WebAPI.
Currently my access_token has a "scp" attribute of "access_api" however when I add an [RequiredScope("access_api_admin")] decorator on a controller action (controller has [Authorize]) and I call the endpoint with the "access_api" scope the action gets executed.
Expected behavior would be an Unauthorized..., yet I am gettin the data which I should not be getting...
The token

The endpoint with requiredScope "access_api_admin"

I feel this should block the request I am making with my access_token since the scope "access_api_admin" is not present in the token...
RequiredScope used to work as expected in this issue until version 1.17.0 of Microsoft.Identityt.Web. It got broken in 1.18.0. See these issues: https://github.com/AzureAD/microsoft-identity-web/issues/1609 https://github.com/AzureAD/microsoft-identity-web/issues/1002.
In my case adding builder.Services.AddRequiredScopeAuthorization() statement to Program.cs made the RequiredScope attribute work.
Closing this one:
- We released RequiredScopeOrAppPermissionAttribute in Microsoft.Identity.Web 1.25.0
- @gurry : you are right, it requires to use app.AddAuthorization(). However, AddRequiredScopeAuthorization is called in AddMicrosoftIdentityWebApi. Did you use that method?
@jmprieur Yes, I peeked inside Microsoft's code and saw 'AddRequiredScopeAuthorization() being used there and simply mimicked that. It may well have been AddMicrosoftIdentityWebApi() that I peeked into (don't exactly remember now).