To fix Generate & VerifyJWTAccessToken Policy with OAuth 2.0

Our new requirement is to create a new JWT OAuth based JWTAccessToken in a API Proxy.

To achieve this we are using JWT OAuth tokens with OAuth 2.0 we have created a API Proxy called 2-legged-Oauth which generate and verify the JWT token.

By referring to the document we are able to generate the JWT token while hitting the (/v2/oauth/token/jwt/access) but when we tried to hit the Verify JWT token we are getting an error (/v2/oauth/token/jwt/verify) as below screenshot.

Kindly find the details which we configured at our end,

GenerateJWTAccessToken Policy:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 name="GetAccessToken">
<Operation>GenerateJWTAccessToken</Operation>
<ExpiresIn>3600000</ExpiresIn>
<SupportedGrantTypes>
<GrantType>client_credentials</GrantType>
</SupportedGrantTypes>
<GenerateResponse enabled="false"/>
<Algorithm>RS256</Algorithm>
<PrivateKey>
<Value>
-----BEGIN RSA PRIVATE KEY-----
aXpkU5XBZVAg2w/+tM8HB/AJVxnS4zrq53ohmlaJmFPX0AbKm+rORTFWyyf4kw3G
xs3YC995hmFwqmSXvfjG7rve+d68/DcAt/2G4J+elz5fG0+8d7jBAnwPvRk8oeaY
-----END RSA PRIVATE KEY-----
</Value>
</PrivateKey>
</OAuthV2>

VerifyJWTAccessToken Policy:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="VerifyAccessToken">
<DisplayName>VerifyAccessToken</DisplayName>
<ExternalAuthorization>false</ExternalAuthorization>
<Operation>VerifyJWTAccessToken</Operation>
<SupportedGrantTypes/>
<GenerateResponse enabled="false"/>
<Tokens/>
<Algorithm>RS256</Algorithm>
<PublicKey>
<Value> xTbXKgSXNg28by1eWRko1tJpAs31x8i46JUjJaVc7QyhJDe0TgwC5Qh9wJxq9W+xY53pClekEvjvrZfWi97M2z0pf8yRjv5TEcNO4zBdmnTL7bIw
</Value>
</PublicKey>
</OAuthV2>

Could any ine kindly assits us on what is missed over here to fix the issue?

Screenshot 2024-01-04 025139.png

Kindly assists us to resolve this query.

1 13 372
13 REPLIES 13

Happy New Year.

Did you review the error?  It is clearly stating about missing grant_type parameter. You're missing <SupportedGrantTypes> information.

Please carefully review the information provided during the token generation which in your case you are using client_credentials, so please add necessary elements & re-test.

 

 

 

Hi,

As metioned we have added the supportType as well even tho we are getting the same error.
Tried scenarios,
1. we included Supported grantype,
<SupportedGrantTypes>
<GrantType>client_credentials</GrantType>
</SupportedGrantTypes>
2.included Response type
<ResponseType>token</ResponseType>
3. included both support type and response we are getting unexpected error.

We reffred to this Document but facing the same error: https://cloud.google.com/apigee/docs/api-platform/security/oauth/using-jwt-oauth

According to the OAuth model, there are two phases: Dispensing the token, and verifying the token. In Apigee, you use the OAuthV2 policy for both, but two very different configurations of the policy.

From the screenshot, it seems like the problem you are seeing is in the 2nd phase - verifying the token.   

Specifically for the verifying phase, you have multiple problems. At least ONE of those problems is that your public key is misformatted. It should be PEM format, and should look something like this: 

 

-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BiUL0/iMFhtr1U9RjrDF...GNtQ0\ngwIDAQAB\n-----END PUBLIC KEY-----\n

 

The other problem, I think, is that you are using unnecessary elements in that policy: Tokens, SupportedGrantTypes, ExternalAuthorization, GenerateResponse.  None of those are relevant to VerifyJWTAccessToken. The OAuthV2 policy does lots of things, and some of the elements it uses do not apply to some of the operations. SupportedGrantTypes applies when Operation= GenerateAccessToken or GenerateJWTAccessToken.  It is not clear from the screenshot which policy you have configured for the particular request.  In any case,  I suggest you remove those unnecessary elements, in the verify policy.

If a value in the PEM format I showed above is stored in a variable called rsa-1-public , then your VerifyAccessToken policy can be as simple as this: 

 

<OAuthV2 name="OAuthV2-VerifyJWT-AT">
  <Operation>VerifyJWTAccessToken</Operation>
  <Algorithm>RS256</Algorithm>
  <PublicKey>
    <Value ref="rsa-1-public"/>
  </PublicKey>
</OAuthV2>

 

Here's a full working example, that does dispensing and verifying.  It has a README:

https://github.com/DinoChiesa/Apigee-Sample-OAuth2-JWT

Hi,

Thanks for your response.

We have tried by removing the unwanted elements as you suggested on the verifyJWTAccessToken still getting the same error.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="VerifyAccessToken">
<Operation>VerifyJWTAccessToken</Operation>
<Algorithm>RS256</Algorithm>
<PublicKey>
<Value>
-----BEGIN PUBLIC KEY-----
AAAAB3NzaC1yc2EAAAADAQABAAACAQC2bKC7GtQ1DuKUz1/+zj1Y6su+uU0LkFJ6pt2dEl..
-----END PUBLIC KEY-----
</Value>
</PublicKey>
</OAuthV2>

Any hint or idea that why we are still facing the same issue? 

I don't know what "same issue" is, at this point.  

But Yes - I have a guess. I guess  that your proxy does not have the policies attache din the places they need to be attached. 

Or, you are sending in a request and invoking a flow in your proxy, that you do not intend to invoke. 

Or you are sending in an invalid request, that lacks all required parameters.

It could be any of those three things, or a combination of 2, or all three. There's a lot that you need to get right.  

I put together a working sample, it has all the policies attached in the right places. There's a readme that describes how to use it.  I suggest you start there, with a working example, and see it work.  Then see if you can compare that example to what you've got, and make adjustments to your own proxy to match. 

 

Hi,

I tried by replicating the samples at my end even tho im getting the same response on the verifyJWTAccessToken.

{
    "fault": {
        "faultstring""Missing required parameter: response_type or grant_type",
        "detail": {
            "errorcode""steps.oauth.v2.MissingParameter"
        }
    }
}
--> 500 Internal server error 

The error message is telling you that the policy is looking for a form parameter , one of either grant_type or response_type, and that form parameter is missing in the request. These form parameters are normally used when requesting a token, eg, with OAuthV2/GenerateAccessToken or OauthV2/GenerateJWTAccessToken.  I suspect the policy that has failed is  Operation=GenerateJWTAccessToken. 

But from your report, you think you are invoking OAuthV2/VerifyJWTAccessToken . That seems wrong. TheOAuthV2 policy with Operation=VerifyJWTAccessToken will not generate the error message you showed.  So there is some confusion.  

Something is not right in your flow. This is what I meant when I said earlier, "your proxy does not have the policies attached in the places they need to be attached. Or, you are sending in a request and invoking a flow in your proxy, that you do not intend to invoke."  

It's not enough to configure the policies you have in your proxy, to be internally the same as the policies in my sample.  You need to attach the policies in the right places in the proxy. and THEN, you need to invoke the right flows with your request. 

The working example I offered to you, does that.  Have you gotten that working?  You said you tried "replicating the samples".  My question to you is:  did you get the sample that I gave to you, working?  

If so, look closely at where the policies are attached, specifically the OAuthV2 policies. Compare the attachment points in my sample to the attachment points you are using in your proxy.  You should be able to figure this out, but you'll need to look closely. 

If you cannot figure it out, Maybe you can show your proxyendpoint configuration? What I mean is the part. in your proxy configuration that is analogous to this: https://github.com/DinoChiesa/Apigee-Sample-OAuth2-JWT/blob/main/apiproxy/proxies/verifier.xml 

And also provide the specific request you are making: the verb, the headers, the payload if any, the URL path.  All details.

Hi dchiesa,

We have configured our 2-legged-OAuth proxy to generate and verify the JWT Token by refreing to the example https://github.com/DinoChiesa/Apigee-Sample-OAuth2-JWT
where we are getting the token as below format,
{
"token_type": "Bearer",
"access_token": "m7luGDxG5O44gZef4llqZ36qedMv",
"grant_type": "client_credentials",
"issued_at": 1705472110,
"expires_in": 3599
}
And while hitting the verify endpoint we are getting success message for all the token which is not valid.
Could you let us know how to upload our proxy bundel file over here?

 

For me there is an icon in the editor here on the community site that allows me to attach a zip file.  

If that's available to you, you can bundle your proxy and attach it here. If that doesn't work then maybe you can create a public repo on github or some other source code system that we can access. 

Hi,

I can't able to find option using my account, kindly find the public repo of our bundle https://github.com/Mustaribegum/2-legged-OAuth-JWT.git

Kindly Let us know how to fix the behaviour.

Thanks in advance!

Hi

I have to tell you, I am a little confused here. Two weeks ago, I provided to you an example of EXACTLY what you are trying to do. As I understand it, You want an endpoint that dispenses OAuth tokens in JWT format, and you also want an endpoint that verifies tokens in JWT format. I provided that, with a readme, and an explanation. I spent time on that.  I did that for you.  Did you try it?

You wrote, after I provided that example, "We have configured our 2-legged-OAuth proxy to generate and verify the JWT Token by refreing to the example..." but that is not the same as actually trying my example. Referring to the example is good, but did you actually try it? It's not a sample, it works. Deploy it. Try it. It does EXACTLY what you say you want to do.

I also looked at your proxy, and.... I have some feedback.

  1. your AssignMessage policies are incorrect. In each of them, you have Remove Copy Set and Add elements. Like this:

    <AssignMessage async="false" continueOnError="false" enabled="true" name="Set-JWTAccessToken-Response">
        <DisplayName>Set JWTAccessToken Response</DisplayName>
        <Properties/>
        <Copy source="request">
            <Headers/>
            <QueryParams/>
            <FormParams/>
            <Payload/>
            <Verb/>
            <StatusCode/>
            <ReasonPhrase/>
            <Path/>
        </Copy>
        <Remove>
            <Headers>
                <Header name="h1"/>
            </Headers>
            <QueryParams>
                <QueryParam name="q1"/>
            </QueryParams>
            <FormParams>
                <FormParam name="f1"/>
            </FormParams>
            <Payload/>
        </Remove>
        <Add>
            <Headers/>
            <QueryParams/>
            <FormParams/>
        </Add>
        <Set>
            <Headers/>
            <QueryParams/>
            <FormParams/>
            <!-- <Verb>GET</Verb> -->
            <Payload contentType="application/json">
            {
            "access_token":"{oauthv2accesstoken.GetAccessToken.access_token}",
            "expires_in":"{oauthv2accesstoken.GetAccessToken.expires_in}"
            }
            </Payload>
            <Path/>
        </Set>
       ...

    That's almost always wrong. I think you may be getting this policy boilerplate from the UI. When you use the Apigee UI to add an AssignMessage policy, it provides a starting policy configuration with all of those things. Unfortunately, that's misleading. You will want to use SOME of those things, not all of them. Your policy configuration should be much simpler. If you want to set a payload, then include the Set/Payload element, and remove everything else!  Like this:

    <AssignMessage name="Set-JWTAccessToken-Response">
        <Set>
            <Payload contentType="application/json">{
     "access_token":"{oauthv2accesstoken.GetAccessToken.access_token}",
     "expires_in":"{oauthv2accesstoken.GetAccessToken.expires_in}"
    }
    </Payload>
        </Set>
    </AssignMessage>

    If you run apigeelint on your proxy, it will point out all of the extraneous elements in the configuration for you.

  2. That policy, Set-JWTAccessToken-Response in particular, is attached to the request flow. But it never gets executed. You have GenerateResponse set in the prior OAuthV2 policy, which tells the OAuthV2 policy to generate a response and switch to the Response flow. So that Set-JWTAccessToken-Response policy never executes. You should remove it, or move it and the preceding policy, to the Response flow.
  3. Your OAuthV2 policy that uses Operation=GenerateJWTAccessToken looks mostly correct, but it references a variable that does not appear to get set, anywhere in your proxy flow: private.rsa-privatekey-1 . It looks like this:

    <OAuthV2 name="GenerateJWTAccessToken">
        <Operation>GenerateJWTAccessToken</Operation>
        <SupportedGrantTypes>
            <GrantType>client_credentials</GrantType>
        </SupportedGrantTypes>
        <GenerateResponse enabled="true"/>
        <Algorithm>RS256</Algorithm>
        <PrivateKey>
            <Value ref="private.rsa-privatekey-1"/>
        </PrivateKey>
        <ExpiresIn ref="kvm.oauth.expires_in">3600000</ExpiresIn>
    </OAuthV2>​

    where does that variable get set?  You need to set it.  

  4. For the VerifyJWTAccesstoken policy, you refer to a variable private.secretkey.  But you cannot verify a JWT token signed with RS256 using a secret key.  It needs to be a public key.  I showed this in my example! Did you look at it? 
  5. The token dispensing flow accepts GET. That's always wrong. Don't do that. Requesting a token should always be done via POST.
  6. For the same flow, you use the policy Apigee-Oauth-Payload to assemble the OAuth request payload. That is inappropriate. The request payload should come from the client. You should not use a policy to contrive the request payload here.  Omit that.

    To use curl to submit the correct request payload, do this:

     curl -i -u $client_id:$client_secret \
          -d grant_type=client_credentials \
          -X POST \
          $apigee/v2/oauth/jwt/token                                                                                                          
    
    
  7. The proxy uses /v1/oauth/jwt/token as the BasePath. That's probably wrong. You want /token to be the proxy pathsuffix for the token request. So the basepath should be /v2/oauth/jwt or similar.

After I fixed those things, it works for me. I'll attach it here, but I am unsure if it will help you. I feel that I've done this before. I already gave you a perfectly good working example, with all of this shown in it. You didn't use that example. Will you use this one? (shrug)

 

Hi dchiesa,

Thanks for the details and updated zip file!

  1. We have referred and updated the AssignMessage and policies as per your feedback we are getting the token response as below, 
{
    "refresh_token_expires_in": "0",
    "api_product_list": "[Test_openID, IT_Oauth_2_legged_JWT, IT_Oauth_2_legged]",
    "api_product_list_json": [
        "Test_openID",
        "IT_Oauth_2_legged_JWT",
        "IT_Oauth_2_legged"
    ],
    "organization_name": "abcd",
    "developer.email": "mb1531",
    "token_type": "BearerToken",
    "issued_at": "123433",
    "client_id": "asdsmlf",
    "access_token": "kr0rMEFfySwJd0WJAY3vr9ZQ2GRK",
    "application_name": "sdsds2323",
    "scope": "",
    "expires_in": "3599",
    "refresh_count": "0",
    "status": "approved"
}

    2. we have also imported the same ZIP which has been attached in your previous email and tested the Proxy still we are getting to see the same response as above. 

    3. Whenever we replace the operation name from GetJWTAccessToken to any name and removed the Provate key it's generating the same response as above. 

For eg.,

 

2.png

 

  4. We wanted to know is this because of the version we are not able to get the response as JWT token? 

 

1.png

 

 

REFERENCE: https://cloud.google.com/apigee/docs/api-platform/reference/policies/oauthv2-policy

Can you please let us know if we can have a screenshare session to fix this behavior?

Thanks

we have also imported the same ZIP which has been attached in your previous email and tested the Proxy still we are getting to see the same response as above.

Hmmm, mysterious! The access_token you see in the response is (obviously) not a JWT.  So ... something is amiss here. If you are using the proxy configuration I sent, and you're seeing a token that is not a JWT, then the GenerateJWTAccessToken operation is not working as expected.

Whenever we replace the operation name from GetJWTAccessToken to any name and removed the Provate key it's generating the same response as above.

Wow, that's not good. That's a bug. The policy should not allow "any name". There's a restricted set of operation values.  I will raise a defect. For now, avoid doing that. It's good to know about this behavior, but.. . inserting any non-supported name will result in undefined behavior. 

We wanted to know is this because of the version we are not able to get the response as JWT token?

Quite possibly! What version of Apigee are you using?  As the documentation states, The GenerateJWTAccessToken works only in X and hybrid, as far as I know. Not in Apigee Edge. I guess we should have checked that in the beginning of this conversation, huh?

If you are using Edge, you should refer to documentation located at https://docs.apigee.com  . If you are using X or hybrid, then refer to documentation at  https://cloud.google.com/apigee/docs .