Validating JWT generated from Azure AD

Not applicable

@Dino

I have a Java program which validates Azure generated JWT with following parameters

1. Token

2. App ID

3. Tenant ID

I have looked at your suggested videos for ODIC as well as watched videos

Unfortunately I am not able to do the same using VerifyJWT token policy in Edge.

My need is to ensure the all apis are protected for internal users , however the user store and authentication happens through Azure AD

I keep getting Invalid Token error all the time.

Solved Solved
0 15 7,889
1 ACCEPTED SOLUTION

Hi Raj,

We use Azure AD tokens as well. We have the following policies in place to do this:

1. 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>

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>request</Source>
    <Header name="Authorization">
        <Pattern ignoreCase="false">Bearer {jwt}</Pattern>
    </Header>
    <VariablePrefix>authn</VariablePrefix>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</ExtractVariables>

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>

We use a roles claim to check if the user has the roles claim in their token

We also cache the MS keys but I left that out because it does not impact your question.

Hope this helps!

View solution in original post

15 REPLIES 15

Show me more information:

  • an example token
  • your VerifyJWT policy config
  • the source of your RSA private key
  • The "error" context variable that is set after VerifyJWT fails.

@Dino

Needed to travel for a workshop, so could not respond to you earlier.

The example token is the one coming from AZure AD and it looks like this :

I cannot give actual token as it is corporate one, it will be something similar with valid signature and other details. know this will indicate invalid signature. Token is validated in Java as well as on Jwt.io

eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctODkwYS0yNzRhNzJhNzMwOWUiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83ZmU4MTQ0Ny1kYTU3LTQzODUtYmVjYi02ZGU1N2YyMTQ3N2UvIiwiaWF0IjoxMzg4NDQwODYzLCJuYmYiOjEzODg0NDA4NjMsImV4cCI6MTM4ODQ0NDc2MywidmVyIjoiMS4wIiwidGlkIjoiN2ZlODE0NDctZGE1Ny00Mzg1LWJlY2ItNmRlNTdmMjE0NzdlIiwib2lkIjoiNjgzODlhZTItNjJmYS00YjE4LTkxZmUtNTNkZDEwOWQ3NGY1IiwidXBuIjoiZnJhbmttQGNvbnRvc28uY29tIiwidW5pcXVlX25hbWUiOiJmcmFua21AY29udG9zby5jb20iLCJzdWIiOiJKV3ZZZENXUGhobHBTMVpzZjd5WVV4U2hVd3RVbTV5elBtd18talgzZkhZIiwiZmFtaWx5X25hbWUiOiJNaWxsZXIiLCJnaXZlbl9uYW1lIjoiRnJhbmsifQ.

This is the Verify JWT policy and I am passing all the parameters.

The source is currently hard coded , Plan is to pick it up from Microsoft link later

https://login.microsoftonline.com/common/.well-known/openid-configuration

I have not set any error variable right now.


Rajiv, Id like to help.

I can best help you if you show me your VerifyJWT policy configuration. Are you able to do that?

Also, DESCRIBE the source of the RSA key. From where are you getting the public key? how are you specifying it to the VerifyJWT policy?

I also asked about the "error" context variable. And you wrote

I have not set any error variable right now.

You are misunderstanding. If the VerifyJWT policy fails, it will set the error variable. I want to know what the error is. You need to tell me that. Help me to help you.

@Dino

RSA Key - Currently I have hard coding the RSA key (public key)

Verify JWT policy is shown below:

Though the key is highlighted as private-key , I have assigned it the specific public-key - https://login.windows.net/common/discovery/keys

verifyjwt-config.png

Pls. advice

Note: I am new to Apigee , may be I am missing something in policy configuration

In the future, It's ok for you to cut/paste the XML for the VerifyJWT, rather than attaching a screenshot. It's easier for everyone to see what you're doing.

I can see that you have too many Source elements. And none of them are correct.

See Reiner's answer for an good, working example to follow.

Hi @raj, AFAIK the Source variable in policy is used to find the JWT. The Tenant_ID & App_ID shouldn't go into the Source. You might get a FailedToDecode error.

If Tenant_ID & App_ID needs to be verified as part of claims, it can be done by using AdditionalClaims element in the policy.

You can also remove the Subject claim if you dont want to verify it.

The issuer in the JWT matches the string specified in the Issuer element in the policy.

I hope you are using an encrypted KVM or Assign Message policy before JWT policy to add the Secret Key Value.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWT async="false" continueOnError="false" enabled="true" name="Verify-JWT-1">
    <DisplayName>Verify JWT-1</DisplayName>
    <Algorithm>HS256</Algorithm>
    <Source>request.formparam.jwt</Source>
    <SecretKey>
        <Value ref="private.key"/>
    </SecretKey>
    <!-- <Subject>subject-subject</Subject> -->
    <Issuer>https://sts.windows.net/dd/dd/d//d/d/d</Issuer>
    <AdditionalClaims>
        <Claim name="Tenant_ID" ref='request.formparam.Tenant_ID' type="string" />
        <Claim name="App_ID" ref='request.formparam.App_ID' type="string" />
    </AdditionalClaims>
</VerifyJWT>

As mentioned by @Dino-at-Google, when you invoke the proxy and if the JWT policy fails, it will give an error response. Please mention that to understand the issue,

{
    "fault": {
        "faultstring": "Invalid Claim: policy(Verify-JWT-1)",
        "detail": {
            "errorcode": "steps.jwt.InvalidClaim"
        }
    }
}

Thanks @Siddharth Barahalikar,

Yes I am including the app id as part of the claims for validation of JWT.

Hi Raj,

We use Azure AD tokens as well. We have the following policies in place to do this:

1. 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>

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>request</Source>
    <Header name="Authorization">
        <Pattern ignoreCase="false">Bearer {jwt}</Pattern>
    </Header>
    <VariablePrefix>authn</VariablePrefix>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</ExtractVariables>

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>

We use a roles claim to check if the user has the roles claim in their token

We also cache the MS keys but I left that out because it does not impact your question.

Hope this helps!

Thanks, Reiner, for the detailed instructions.

It is working fine.

One clarification I have is why is service call out with Microsoft with tenant ID is returning null as a result token validation I'd failing. Actual error is "the service callout is returning 404 and it appears to be an error".

Did you come across this error?

Hi Raj,

No I did not. You can check if the endpoint is correct by browsing to it in your browser. This should return a JSON body with the keys in it.

What do you mean "it's working fine" ? If you are getting a 404 from the ServiceCallout, then you have no keys, and surely the VerifyJWT policy will fail without keys. Right?

Which means it's not working fine. Can you browse to the keys URL ? It should be public.

May be I was not specific. I meant to say that if I use common url in policy it works.

https://login.microsoftonline.com/common/discovery/v2.0/keys

If I replace this including Tenant ID it does not work and as this does not exist and it will return 404.

So actual url for validation is https://login.microsoftonline.com/common/discovery/v2.0/keys

If the URL https://login.microsoftonline.com/{tennantId}/discovery/v2.0/keys does not work, this is an issue in your Azure AD configuration, not in Apigee.

Yes. And also, it's possible that it's not an issue at all. If Microsoft is using common keys to sign tokens, then... maybe that's correct.

I completely agree this is not related to Apigee.

To ensure it works I tried with another tenant ID as well. No success, finally I ended up using the common keys to sign tokens.

Good news, is that it is working fine for us, thanks for your help.