Validating Scopes in ASP.NET 4 and 5

OAuth 2.0 scopes are a way to model (API) resources. This allows you to give logical “names” to APIs that clients can use to request tokens for.

You might have very granular scopes like e.g. api1 & api2, or very coarse grained like application.backend. Some people use functional names e.g. contacts.api and customers.api (which might or might not span multiple physical APIs) – some group by criteria like public or internal only. Some even sub-divide a single API – e.g. calendar.read and calendar.readwrite. It is totally up to you (this is how Google uses scopes).

At the end of the day, the access token (be it self-contained or referenced) will be associated with the scopes the client was authorized for (and optionally – the user consented to).

IdentityServer does that by including claims of type scope in the access token – so really any technique that allows checking the claims of the current user will do.

As a side note – there is also a spec that deals with return codes for failed scope validation. In short – this should return a 403 instead of a 401.

We ourselves had some iterations in our thinking how we deal with scopes – here’s a summary and some options.

ASP.NET 4.x

The most common way we do scope checking is via our token validation middleware (source/nuget), which combines token and scope validation into a single step:

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = "https://localhost:44333/core",
        RequiredScopes = new[] { "calendar.read""calendar.readwrite" },
    });

This would validate the token and require that either the calendar.read or calendar.readwrite scope claims are present.

This middleware also emits the right response status code, Www-Authenticate header and respects CORS pre-flight requests.

For finer granularity we also once wrote a Web API authorization attribute – [ScopeAuthorize] that you can put on top of controllers and actions (source).

As mentioned before – you can always check inside your code for scope claims yourself using the claims collection.

ASP.NET 5

We will have the same “all in one” IdentityServer token validation middleware for ASP.NET 5 – but this time split up into separate middleware that can be also used stand-alone (I wrote about the introspection aspect of it in my last post).

The scope validation part (source/nuget) of it looks like this:

app.AllowScopes("calendar.read""calendar.readwrite");

This has the same OR semantics as described above.

You can also use the new ASP.NET 5 authorization API to do scope checks – e.g. as a global policy:

public void ConfigureServices(IServiceCollection services)
{
    var scopePolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .RequireClaim("scope""calendar.read""calendar.readwrite")
        .Build();
 
    services.AddMvc(options =>
    {
        options.Filters.Add(new AuthorizeFilter(scopePolicy));
    });
}

..or as a named policy to decorate individual controllers and actions:

services.AddAuthorization(options =>
{
    options.AddPolicy("read",
        policy => policy.RequireClaim("scope""calendar.read"));
    options.AddPolicy("readwrite",
        policy => policy.RequireClaim("scope""calendar.readwrite"));
});

and use it e.g. like that:

public class CalendarControllerController
{
    [Authorize("read")]
    public IActionFilter Get() { ... }
 
    [Authorize("readwrite")]
    public IActionFilter Put() { ... }
}

One last remark: We get this question a lot – scopes are not used for authorizing users. They are used for modeling resources (and optionally to compose the consent screen as well as to specify which client might have access to these resources).

HTH

This entry was posted in ASP.NET, IdentityModel, IdentityServer, Katana, OAuth, Uncategorized, WebAPI. Bookmark the permalink.

10 Responses to Validating Scopes in ASP.NET 4 and 5

  1. Alexey Aouslender says:

    Regarding your last remark,how the user should be authorized without using scopes?

  2. That’s what you say ;) I don’t agree.

    That scopes are claims is really only an implementation detail. Conceptually they don’t describe the identity of the user – but rather which access the user granted to the client (explicitly or implicitly).

    • Chris Rush says:

      As I understand what is being said, the scopes are the permissions the resource owner has granted to the client. Whether a resource owner can actually access a resource on the resource server, via a client, is the concern of the resource server itself. Thus, the resource server would be responsible for knowing how to retrieve the resource owner’s permissions based on the identity token. For example, it may have some local store that relates resource owner identity to a set of permissions.

      Is this correct?

      • The resource server will not receive an identity token – but an access token. This token will have enough information for the resource server to retrieve permissions. yes.

  3. Alexey Aouslender says:

    So ,each time before protected resource is accessed we need to retrieve user roles according to subject claim for example and decide if particular user is allowed to access the resource?

    • the roles might me in the token itself – the roles might be added via claims transformation (using caching of course) – or the permissions or finer grained authZ data can be evaluated by your authZ logic.

  4. Alexey Aouslender says:

    Thanks a lot for your time and detailed explanation.

  5. arghya says:

    How do you use CORS( Cross Origin) to register a user in ASP.NET 5 account controller? Specifically, the Register Post method with the RegisterViewModel will not allow _userManager.CreateAsync(email, password) to allow CORS request to insert members to the ASP.NET users table

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s