Rvy Pandey
Rvy Pandey

Reputation: 1762

Scope/Roles claim not available in the Client Credentials access token

After fetching the access_token (grant_type=client_credentials) from login.microsoftonline.com/common/oauth2/v2.0/token, I get back:

HTTP 403
{
  "error": {
    "code": "AccessDenied",
    "message": "Either scp or roles claim need to be present in the token.",
    "innerError": {
      "request-id": "fa788422-6868-4ab3-9ded-5f076138bda2",
      "date": "2019-04-02T11:24:30"
    }
  }
}

When I decoded the token, it actually does not have an SCP or ROLES key in the body (opposed to what I've seen from the token I read from Graph Explorer)

I read many docs/blogs on this, and all of them pointed to adding "Microsoft Graph's Application permissions", and then getting an admin consent on them. I got the consent after adding required permissions (Files.ReadWrite.All, Files.ReadWrite.AppFolder, etc):

Admin consent on required permissions

Code:

const escapedScopeUri = querystring.escape(
  `https://graph.microsoft.com/.default`
);
const secretKey = querystring.escape(
  azureApplicationConfig.clientSecret
);
const requestBody = `client_id=${azureApplicationConfig.clientID}&client_secret=${secretKey}&scope=${escapedScopeUri}&grant_type=client_credentials`;

const authReqOptions = {
  method: `POST`,
  uri: `https://login.microsoftonline.com/common/oauth2/v2.0/token`,
  body: requestBody,
  headers: {
    "Content-Type": `application/x-www-form-urlencoded`
  }
};

rp(authReqOptions) // rp = request-promise module
  .then(async authRes => {
    console.log(authRes);
  })
  .catch(err => {
    // do something with err
  });

Now, this is not the complete code, but it should give an idea of what is being done.

After authRes is available, I decoded the token information (where the scope is not available). This is the returned info:

{
  "token_type": "Bearer",
  "expires_in": 3600,
  "ext_expires_in": 3600,
  "access_token": "returned token"
}

and the decoded token body info: decoded token body information

Additionally, I have added appRoles in the manifest (not sure if this is the correct format):

{
  //...
  "appRoles": [
    {
      "allowedMemberTypes": ["User"],
      "description": "Trying to make app as Reader",
      "displayName": "Reader",
      "id": "4e76a3f3-86c9-4186-aa1d-c22ccc167326",
      "isEnabled": true,
      "lang": null,
      "origin": "Application",
      "value": "reader"
    },
    {
      "allowedMemberTypes": ["User"],
      "description": "Trying to make app as Admin",
      "displayName": "Admin",
      "id": "f3f3b2f0-3203-45fa-89e0-17a9c9b4ee73",
      "isEnabled": true,
      "lang": null,
      "origin": "Application",
      "value": "admin"
    }
  ]
  //...
}

Still, things have not worked out.

Upvotes: 2

Views: 3267

Answers (1)

Marc LaFleur
Marc LaFleur

Reputation: 33094

You cannot request Client Credentials from the /common tenant. Since you're not providing an email address (as you would with Auth Code or Implicit), there is no way for AAD to discover which tenant you're looking to obtain a token for.

You need to supply either your tenant URI (domain.onmicrosoft.com) or the id (a GUID issued to each tenant when it is created):

const authReqOptions = {
  method: `POST`,
  uri: `https://login.microsoftonline.com/${your-tenant-uri-or-id}/oauth2/v2.0/token`,
  body: requestBody,
  headers: {
    "Content-Type": `application/x-www-form-urlencoded`
  }
};

Upvotes: 2

Related Questions