简体   繁体   中英

Microsoft graph return "access token is empty"

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 

The Authorization header was misspelled.

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

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.

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.

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!

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

在此处输入图片说明

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,
  };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM