7
votes

I post this request:

 
POST https://login.microsoftonline.com:443/{my-tennant-here}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com 
Content-Type: application/x-www-form-urlencoded

client_id={client id here}
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret={client secret here}
&grant_type=client_credentials
 

This returns:

 
{
  "token_type": "Bearer",
  "expires_in": 3599,
  "ext_expires_in": 0,
  "access_token": "eyJ0eX......
}
 

I have decoded the token using jwt.io and it definitely is not empty. It contains 14 claims. aud, iss, tid etc...

I then use the access token in this request

 
GET https://graph.microsoft.com/v1.0/users
Athorization: Bearer eyJ0eX...
 

I then get a 401 Unauthorized with this body:

 
{
  "error": {
    "code": "InvalidAuthenticationToken",
    "message": "Access token is empty.",
    "innerError": {
      "request-id": "",
      "date": "2018-08-14T15:41:44"
    }
  }
}
 

Expected result was a 200 Ok with a body containing a list of users

Does this simply mean that my app is Unauthorized, and the error message is just misleading (access token is empty)? Or have I done something wrong?

Update: I have noted that the although the token does contain claims it does not have a scope claim which seems a bit weird to me. I would assume that it had the User.Read.All scope. The application (client id/client secret) should have this permission. The claims in the token I receive have the following claims:

 
aud: "https://graph.microsoft.com",
iss: "https://sts.windows.net/my tennant id",
iat: timestamp
nbf: timestamp
exp: timestamp
aio: looks like some kind of signature
app_displayname: "the expected app name"
appid: "the expected appid"
appidacr: "1"
idp: "https://sts.windows.net/...."
oid: "GUID"
sub: "GUID"
tid: "my tennant id"
uti: "value"
ver: 1.0 
5
I can't recall ever seeing 'Access token is empty'. Have you confirmed that you have the correct permissions to make that call: developer.microsoft.com/en-us/graph/docs/concepts/…Michael Mainer
The admin that registered the application for me told me that he gave the application "Read all users" permissions.Olav Nybø

5 Answers

8
votes

The Authorization header was misspelled.

So "Access token is empty" probably actually meant not present or even "No authorization header in request".

3
votes

One URL works while the other doesn't.

This works:

endpoint = "https://graph.microsoft.com/v1.0/reports/getOffice365ActiveUserDetail%28period%3D%27D7%27%29"
headers = {"Authorization": 'Bearer ' + access_token_gmc}
response = requests.get(endpoint, headers=headers)

But this one doesn't:

endpoint = "https://graph.microsoft.com/v1.0//users/myuserid/calendars"
headers = {"Authorization": 'Bearer ' + access_token_gmc}
response = requests.get(endpoint, headers=headers)  

Please make sure the spellings are correct.

0
votes

For User.Read.All scope you can't have a user consent. It must be admin consent. It looks like you may have missed consenting your app using an admin account.

To do this hit:

GET https://login.microsoftonline.com/{tenant}/adminconsent
?client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions

Then get an access token and you should get the scopes the admin has consented users for.

0
votes

For me, my issue was that I had put a linebreak between the request url and the Authorization header, making it the body instead.

Wrong:

GET https://graph.microsoft.com/v1.0/users

Authorization: Bearer {{token}}

Correct:

GET https://graph.microsoft.com/v1.0/users
Authorization: Bearer {{token}}

A stupid mistake, but easy to overlook - if you get to this post you have probably done a silly mistake like OP (typo) or this. Look through your request syntax again!

0
votes

I was getting the same error in my angular application where I use MSAL.

enter image description here

And it was all because of the wrong API scope provided in the MSALAngularConfigFactory. I was having the environment as preceding.

export const environment = {
  production: false,
  clientId: 'clientid',
  redirectUrl: 'http://localhost:4200',
  graphApiUrl: 'https://graph.microsoft.com/v1.0',
  graphGetUserImage: '/me/photo/$value',
  protectedResourceMap: [
    ['http://localhost:65498/api', ['api.access']],
    ['https://graph.microsoft.com/beta', ['user.read']]
  ] as [string, string[]][],
  authority: 'https://login.microsoftonline.com/organizations'
};

As you can see that I have given https://graph.microsoft.com/beta in the protectedResourceMap, which is wrong. Instead, we should give https://graph.microsoft.com/v1.0/. So here is the correct environment.

export const environment = {
  production: false,
  clientId: 'clientid',
  redirectUrl: 'http://localhost:4200',
  graphApiUrl: 'https://graph.microsoft.com/v1.0',
  graphGetUserImage: '/me/photo/$value',
  protectedResourceMap: [
    ['http://localhost:65498/api', ['api.access']],
    ['https://graph.microsoft.com/v1.0/', ['user.read']]
  ] as [string, string[]][],
  authority: 'https://login.microsoftonline.com/organizations'
};

And I use it in the app.module.ts as below.

function MSALAngularConfigFactory(): MsalAngularConfiguration {
  return {
    popUp: false,
    protectedResourceMap: environment.protectedResourceMap,
  };
}