Share via


Azure AD, Scope-based authorization

"Hello World!"

Continuing the customization of the basic two tiers scenario introduced in my previous posts, I would like to talk about scopes. OAuth2 defines the concept of scope as a "list of space-delimited, case-sensitive strings" that specifies the scope of the access request. These scopes can be used by a target application to allow or deny the access to its resources. Hence, with the scopes we can fine-tune the API access based on the caller access token.

To make it simple:

  1. The target application (Api) defines which scopes supports (i.e.: "scopeA", "scopeB” ...)
  2. The client application (FrontEnd) specifies which scopes wants during the access token request **
  3. The authentication server (Azure AD) replies with an access token that contains a field (scp) with all the valid scopes
  4. The target application (Api) inspects the access token and takes the proper actions (allow, deny, redirect... etc)

Let's us start from the last step, the target application configuration.

From the point of view of the target application scopes are just strings (or better claims) part of the JWT token:

 "appid": "6fa546d0-c2b8-4902-8415-a2b5bdc07472",
"appidacr": "0",
"e_exp": 262800,
"family_name": "Standard",
"given_name": "User",
"ipaddr": "167.220.197.30",
"name": "user",
"oid": "703edbe8-0fcd-4d2e-8a99-c1ee78df2231",
"scp": "aad.scopeA aad.scopeB user_impersonation",
"sub": "4LSAbYQx-PYbh5LI98jS22dBq4AcFhy47CRmIXIbsAY",
"tid": "f7ad0521-ba5a-4622-bab1-8eae3e485430",
"unique_name": "user@bizspark7demooutlook.onmicrosoft.com",
"upn": "user@bizspark7demooutlook.onmicrosoft.com",
"uti": "VnxNhOlxE06flrDmAp4fAA",
"ver": "1.0"

In Asp.Net Core 2.0 we can easily integrate the scopes control defining authorization policies in the Startup class:

 services.AddAuthorization(options => {
   //custom scopes
   options.AddPolicy("A", policyBuilder => policyBuilder.Requirements.Add(new HasScopeRequirement("aad.scopeA")));
   options.AddPolicy("B", policyBuilder => policyBuilder.Requirements.Add(new HasScopeRequirement("aad.scopeB")));
});
//HasScopeRequirement is a custom class

and then use such policies to control the API access:

 [Authorize(Policy = "A")]
public string GetWithScopeA()
{
   return "Hello from API (Scope A)";
}
 
[Authorize(Policy = "B")]
public string GetWithScopeB()
{
   return "Hello from API (Scope B)";
}

In this manner we are allowing to access to our API only the users that have the right scopes in the access token, otherwise a 403 forbidden message is returned.

So far so good. The next step is to tell Azure AD which are the supported scopes.

Technically speaking the scopes are just a JSON configuration in the application manifest (they are also called OAuth2Permissions):

 {
    "adminConsentDescription": "Allow the application access to scope B APIs",
    "adminConsentDisplayName": "Scope B access",
    "id": "b69ee3c9-c40d-4f2a-ac80-961cd1534e49",
    "isEnabled": true,
    "type": "User",
    "userConsentDescription": "Allow the application access to scope B APIs",
    "userConsentDisplayName": "Scope B access",
    "value": "aad.scopeB"
},
{
    "adminConsentDescription": "Allow the application access to scope A APIs",
    "adminConsentDisplayName": "Scope A access",
    "id": "b69ee3c9-c40d-4f2a-ac80-961cd1534e48",
    "isEnabled": true,
    "type": "User",
    "userConsentDescription": "Allow the application access to scope A APIs",
    "userConsentDisplayName": "Scope A access",
    "value": "aad.scopeA"
}

This is sufficient to create the scopes and exposes them to other applications in the same tenant. The scopes become visible in the "Required Permissions" settings of the other applications.

 

We need to know that the scopes we select here are set as "default", that means every user will get such scopes in the access token, regardless of the client request.

A working example can be found here

** The V1.0 endpoint does not honor the scopes requested by the client; It always fills the "scp" field with the scopes defined in the portal. So, it is a static configuration: the scopes/permissions not selected in this tab cannot be requested by the caller.

*** The V2.0 endpoint honors the scopes in the client request as support dynamic and incremental consent pages

Comments