We use Azure AD tokens as well. We have the following policies in place to do this:
1.Service callout to get token from azure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ServiceCallout async="false" continueOnError="false" enabled="true" name="GetAccessToken"> <DisplayName>GetAccessToken</DisplayName> <Properties/> <Request clearPayload="true" variable="oauthRequest">
<Headers>
<Header name="Content-Type">application/x-www-form-urlencoded</Header>
</Headers>
<FormParams>
<FormParam name="client_id">*********</FormParam>
<FormParam name="client_secret">***************</FormParam>
<FormParam name="grant_type">client_credentials</FormParam>
<FormParam name="scope">https://graph.microsoft.com/.default</FormParam>
</FormParams> <Set> <Verb>POST</Verb> </Set> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> </Request> <Response>calloutResponse</Response> <HTTPTargetConnection> <Properties/> <URL>https://login.microsoftonline.com/XXXX-TENNANTID-XXXX/oauth/v2.0/token</URL> </HTTPTargetConnection> </ServiceCallout>
I get the access token successfully.
I have even tried using "https://login.microsoftonline.com/common/discovery/v2.0/keys"
But it got keys and it failed in validation
2. Retrieve keys from MS:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-RetrieveMicrosoftKeys"> <DisplayName>SC-RetrieveMicrosoftKeys</DisplayName> <Properties/> <Request clearPayload="true" variable="myRequest"> <Set> <Verb>GET</Verb> </Set> <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables> </Request> <Response>msKeys</Response> <HTTPTargetConnection> <Properties/> <URL>https://login.microsoftonline.com/XXXX-TENNANTID-XXXX/discovery/v2.0/keys</URL> </HTTPTargetConnection> </ServiceCallout>
I recieve the keys success fully
2. Extract JWT from header:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ExtractVariables name="Extract-JWT-Assign-Message" enabled="true" async="false" continueOnError="false"> <Source>calloutResponse</Source> <JSONPayload>
<Variable name="access_token">$.access_token</Variable>
</JSONPayload> <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables> </ExtractVariables>
Access_token is extracted successfully
3. Verify JWT:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <VerifyJWT async="false" continueOnError="true" enabled="true" name="VerifyJWT"> <DisplayName>VerifyJWT</DisplayName> <Algorithm>RS256</Algorithm> <Source>authn.jwt</Source> <PublicKey> <JWKS ref="msKeys.content"/> </PublicKey> <Issuer>https://sts.windows.net/XXXX-TENNANTID-XXXX/</Issuer> <Audience ref="aud"/> <AdditionalClaims> <Claim name="roles" ref="active-directory.jwt.roles" type="string" array="true"/> </AdditionalClaims> </VerifyJWT>
Here I get error saying
{ "fault": { "faultstring": "Invalid token: policy(Verify-JWT-1)", "detail": { "errorcode": "steps.jwt.InvalidToken" } } }
Solved! Go to Solution.
I guess you are facing this problem , which is not a problem in Apigee, nor is it a problem in Azure per se, but rather, a misunderstanding of the token Azure creates and hands out to you. In my opinion the misunderstanding is Microsoft's fault, because ... they could have been clearer. But I guess that's irrelevant when you 're just trying to solve the problem.
When you request a token from Entra ID with a scope of https://graph.microsoft.com/.default
, you get a token that looks like a JWT, and can be decoded in the same way a JWT can be decoded, but it cannot be verified like a JWT. Despite appearances, it is not actually a JWT, so VerifyJWT will never work to "verify it". If that seems confusing to you, you are not alone. I find it confusing too. And that's the part I think Microsoft is responsible for. You can read in more detail than you probably want, here.
The solution is one of these two things:
https://graph.microsoft.com/.default
when requesting the token.https://graph.microsoft.com/.default
, but do not try to use VerifyJWT on the subsequently issued token.When you use the special scope of https://graph.microsoft.com/.default
, you're telling Azure AD (Entra ID): I want you to issue a token that will be used for the Graph API. That token looks-like-a-JWT-but-is-not. You should not try to verify it. Any necessary verification will be performed by Microsoft, when you present that token when invoking the graph API.