I'm trying to complete a fairly simple process:
- A web application, authenticated with Azure AD via a personal Microsoft account (i.e. an
@hotmail
account), makes a call to a second microservice. - The microservice is secured via a JWT, obtains a second token using the on-behlaf-of flow, and called the Microsoft Graph API to retrieve calendar events.
I can log into the web frontend OK, and call the second microservice OK. The second microservice can obtain an on-behalf-of (obo) token OK, but the problem I run into is that the obo access token provided to me fails to call the Graph API. The error I receive is this:
{"error":{"code":"NoPermissionsInAccessToken","message":"The token contains no permissions, or permissions can not be
understood.","innerError":{"oAuthEventOperationId":"7499efa7-932b-425d-8ad4-43206630f961","oAuthEventcV":"ed1BzGz2t/H/wK7JnZB6lQ.1.1.1","errorUrl":"https://aka.ms/autherrors#error-InvalidGrant","requestId":"3ca5fa79-423b-460e-9130-b7d1172ec841","date":"2021-09-13T09:24:17"}}}
My problem is similar to the one described here, where my obo JWT does not contains a roles claim. This is the decoded JWT sent to the Graph API:
{
"typ": "JWT",
"nonce": "A3IzBCOGrnE53ukPqb2jHjWYT0grwFbHo_OkzcUhSRc",
"alg": "RS256",
"x5t": "l3sQ-50cCH4xBVZLHTGwnSR7680",
"kid": "l3sQ-50cCH4xBVZLHTGwnSR7680"
}.{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/26375159-666a-4217-adfe-c06427b7798c/",
"iat": 1631522998,
"nbf": 1631522998,
"exp": 1631526898,
"acct": 1,
"acr": "1",
"aio": "AUQAu/8TAAAA1HEurZSE5YS0ADYs3oKeEEy6qhWwyXBZtoHtBPbIS/jo0OD5BTlQptuXZ3ZLtDczWZQw7b0+dUoCNdpN2mY4ew==",
"altsecid": "1:live.com:00014B90C0D373BC",
"amr": [
"pwd"
],
"app_displayname": "SpringBootMicroserviceDemo",
"appid": "5907efc0-f0b5-45db-b4cf-725f655009c3",
"appidacr": "1",
"email": "matthewcasperson@hotmail.com",
"family_name": "Casperson",
"given_name": "Matthew",
"idp": "live.com",
"idtyp": "user",
"ipaddr": "45.132.224.55",
"name": "matthewcasperson",
"oid": "e377a23b-1b88-4d14-9c99-fc6ecd4a41c7",
"platf": "3",
"puid": "1003200180F4FB20",
"rh": "0.AVAAWVE3JmpmF0Kt_sBkJ7d5jMDvB1m18NtFtM9yX2VQCcNQAKM.",
"scp": "Calendars.Read openid profile User.Read email",
"signin_state": [
"kmsi"
],
"sub": "vhbBIoJqEHoEaJLMVsG5sh0C4FjoJiAfAKOFCzrC8hQ",
"tenant_region_scope": "NA",
"tid": "26375159-666a-4217-adfe-c06427b7798c",
"unique_name": "live.com#matthewcasperson@hotmail.com",
"uti": "ufe_nSFv_UuiYCOur72mAQ",
"ver": "1.0",
"wids": [
"13bd1c72-6f4a-4dcf-985f-18d3b80f208a"
],
"xms_st": {
"sub": "PCUskXV6aCsNgHz9Yug42-WhS-iea1gy5GI5trkTZ4E"
},
"xms_tcdt": 1631479794
}.[Signature]
The JWT has Calendars.Read
in the scp
, but no roles
, which appears to be an issue.
The JWT above will complete a call to the "me" endpoint OK (i.e. https://graph.microsoft.com/v1.0/me). Unfortunately, calling the "me" endpoint is as far as any example provided by Microsoft goes.
The sample application here provides a controller that shows calling the "me" endpoint on the Graph API, and if I modify the Graph API client to include the https://graph.microsoft.com/Calendars.Read
scope, it also fails the Graph API request.
The sample application here appears to be a slightly older example in that it builds the token manually rather than injecting something like @RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient client
(which is described in more detail on the Spring blog here). But again the sample only goes so far as to call the "me" endpoint.
As far as I'm aware I have enabled all the correct settings. My Azure AD Application has requested Calendars.Read
both as a delegated and application permission, and the permissions have been granted consent:
My web based application can be found here, and the Calendar API microservice can be found here.
Further reading:
- Azure AD v2 roles not included in Access Token: This described how to add custom roles to the JWT, but I did not see how it could be used to add Graph API roles.
- Graph API access token for calendar read reports No Permissions: This talked about adding application permissions, which did not resolve the issue for me.
- Microsoft Graph API outlook task folder : NoPermissionsInAccessToken: This issue talks about different OAuth flows, but this doesn't appear to apply to a resource server that accepts a JWT.
- How to solve "NoPermissionsInAccessToken" returned by Microsoft Graph when reading calendar or sending mail: The answer here talks about "Adding guest users is meaningless", which makes no sense to me.
- Spring Boot Starter for Azure Active Directory developer's guide: This is a reasonably detailed tutorial that even speaks about resource servers, but I could not find any tips that addressed the
NoPermissionsInAccessToken
error. - OAuth 2.0 Sample for Azure AD Spring Boot Starter client library for Java - This is another reasonably detailed tutorial that talks about accessing a second resource server, but again I didn't find any clues to the
NoPermissionsInAccessToken
error. - List events and Get calendar: This indicates that a Delegated (personal Microsoft account) can use the
Calendars.Read
permission.
The crux of my issue is that using the on-behalf-of flow appears to be the recommended solution for calling the Graph API from a resource server that accepts a JWT from a front end application. Defining the delegated permissions in the Azure AD Application, consenting to them, defining them in an authorization-clients:
in the application.yaml file, and getting a client via @RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient client
are the only consistent instructions from the Microsoft documentation and sample applications. And yet the resulting JWT can only call the "me" endpoint.
Can anyone shed some light on how to call the Graph API with on-behalf-of token in Spring boot?