Oauth V2 Generate Access Token - class java.lang.Integer cannot be cast to class java.lang.String

Oauth V2 Generate Access Token is throwing this error 

{
"fault": {
"faultstring": "class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')",
"detail": {
"errorcode": "Internal Server Error"
}
}
}
 
Below is the policy
 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="OA_GenerateToken_App">
<DisplayName>OA_GenerateToken_App</DisplayName>
<StoreToken>true</StoreToken>
<Operation>GenerateAccessToken</Operation>
<!--Expires in milliseconds-->
<ExpiresIn ref="expires_in">350000000</ExpiresIn>
<SupportedGrantTypes>
<GrantType>password</GrantType>
</SupportedGrantTypes>
<Scope>scope</Scope>
<GenerateResponse>true</GenerateResponse>
<ExternalAuthorization>false</ExternalAuthorization>
<!-- <ExternalAccessToken>request.header.access_token</ExternalAccessToken>-->
<ExternalAccessToken>private.jwtmessage</ExternalAccessToken>
</OAuthV2>
 
 
 
 
0 14 312
14 REPLIES 14

I converted the parameters to string and now receiving a different error

 

{"ErrorCode":"invalid_request","Error":"Unsupported grant type : authorization_code"} 

That is very clearly a bug, and if I were you I would report that to Apigee support.

What's the value of the private.jwtmessage variable? That should be a STRING. Is it?

@dchiesa1  I converted the inut parameters to string that worked. But now I see below error

{"ErrorCode":"invalid_request","Error":"Unsupported grant type : authorization_code"} 

 

That's telling you that the request has submitted a grant_type of authorization_code, yet your policy does not allow it.  In fact your policy is insisting that the grant_type be "password".   To fix that you'd need to modify the request.formparam.grant_type to be "password".  (basically setting it to a contrived value).

Can we take a step back? What are you really trying to do ? 

 

Thanks For your response.

In the steps before I used generate JWT policy get a JWT Token which is stored in private.jwtmessage . In this step I want to create an Oauth JWT Token using private.jwtmessage.

@dch 

WHY are you generating a JWT and then storing it as a externally-generated token? 

What are you really trying to do here?  Can you explain why you'd want to do that?  I've never seen that before.  It is unusual to ingest a signed token as an Apigee-managed (implicitly opaque) token.  What is the purpose of that?

sanugu_0-1690229564874.png

I am using Assignmessage policy to set the form params.

Flow : Generate JWT -> Set the form params using AM Plociy -> Generate Oauth JWT 

@dchiesa1

I think if you are using AssignMessage you probably want to use Set/FormParams not Add/FormParams . 

Add will ... uh.... add form params.  So if you start with a message that includes this  as the complete form data:

grant_type=authorization_code

and you use Add/FormParams as shown above, you will get a form data like this: 

grant_type=authorization_code&grant_type=password&scope=X&username=Y&password=Z

As the name of the element implies, Add will add a form param. It does not replace any existing form params. 

Try using Set/FormParams. 

I updated the AM Policy to Set Form Params

<Set>
<FormParams>
<FormParam name="grant_type">password</FormParam>
<FormParam name="username">{verifyapikey.VA_Verify_Username.client_id}</FormParam>
<FormParam name="password">{verifyapikey.VA_Verify_Username.client_secret}</FormParam>
</FormParams>
</Set>

I am still seeing the error 

{"ErrorCode":"invalid_request","Error":"Unsupported grant type : authorization_code"}

 How do I change the grant type to password?

In Oauth V2 Policy I am using Password as supported Granttype

<SupportedGrantTypes>
<GrantType ref="message.formparam.grant_type">password</GrantType>
</SupportedGrantTypes>

@dchiesa1 

If you want password grant, the SupportedGrantTypes should not use a ref.

<SupportedGrantTypes>
  <GrantType>password</GrantType>
</SupportedGrantTypes>

 Also, you are showing me a part of the AssignMessage, but not all of it, so I Cannot tell if it is correct. It's important that this AssignMessage refer to the request message. Does it? It should have this:?

  <AssignTo>request</AssignTo>

Also, WHY are you generating a JWT and then storing it as a externally-generated token? What are you really trying to do here?  Can you explain that for me?  Why are you not just generating a token in the normal way? 

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="AM_Set-Token-Param">
<DisplayName>AM_Set-Token-Param</DisplayName>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<Set>
<FormParams>
<FormParam name="scope">{scope}</FormParam>
<FormParam name="grant_type">password</FormParam>
<FormParam name="username">{verifyapikey.VA_Verify_Username.client_id}</FormParam>
<FormParam name="password">{verifyapikey.VA_Verify_Username.client_secret}</FormParam>
</FormParams>
</Set>
<AssignVariable>
<Name>oauth_external_authorization_status</Name>
<Value>true</Value>
</AssignVariable>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

Above is the complete AM Policy.

All I want to do is Generate an Oauth JWT with claims I Need.

I would have to decode Oauth JWT and verify claims later.

Why are you not just generating a token in the normal way? -> what do you mean by normal way? Can you please explain

@dchiesa1 


@sanugu wrote:

All I want to do is Generate an Oauth JWT with claims I Need.


There are two types of tokens that Apigee generates, and either can be used as OAuth tokens: 

  • opaque
  • JWT

In the beginning, there was OAuth, and Apigee introduced the OAuthV2 policy. That policy has a number of different "Operations",, the most interesting of which for the purposes of this discussion are, GenerateAccessToken, and VerifyAccessToken.  When you use the GenerateAccessToken operation in the OAuthV2 policy, Apigee generates an opaque access token.  Opaque here means that the token itself is just a string of random characters, I think the length is 43 or 48 characters or maybe more. It doesn't matter. The point is there is enough randomness in the generated token that it is impossible to guess a valid token instance. And Apigee generates it - meaning Apigee chooses the random characters, and you (as API publisher) don't have influence over how that is done.  When you configure the OAuthV2/GenerateAccessToken, you can specify things like expiry, and any other "attributes" (you might call them claims) attached to this token.  And when Apigee generates it, Apigee also writes that token, and all its attributes, to its internal persistent store.  This random string of characters, the token, is a "ticket to ride" - Apigee delivers the token to a client application. The client app can "see" the token, of course, but the client app cannot make sense of the token, independently.  The token is just a random string; this is why it is called "opaque".  Only the Apigee runtime has access to the properties of the token, which are associated to the token in the persistent store. After the client receives the token from Apigee, the app presents that token with every subsequent API request. Upon receiving. request, the Apigee API Proxy can invoke the OAuthV2 policy with the Operation=VerifyAccessToken, to check whether the presented token is valid, not expired, and good for the current in-flight request.  (Not all tokens are good for all rides).  What happens behind the scenes is, the Apigee runtimes does a lookup in the token database, and retrieves the token and all its attributes. The Apigee proxy can then make a judgment about whether to allow the request or not. 

In Apigee, if you want to generate an OAuthV2 token and use it for granting access to APIs, what I just described is the "normal" way of doing things. 

Separately from that behavior, Apigee introduced a capability to ingest an externally-generated token into its persistent store. This is useful in a couple scenarios

  • there is an external system that generates token, through some OAuth v2 flow, and the customer (API Publisher) wishes an Apigee API proxy to be able to validate that externally-generated token. 
  • There is a live migration from an "old" system to Apigee, and during that live migration, the customer (API Publisher) wants all tokens to be treated as valid by Apigee.

The second is really just a special-case of the first.  Regardless, in these cases it is beneficial to ingest a known token into Apigee, instead of letting Apigee generate a token as a random string of characters. Everything else I've written in the above, still applies. The token is still treated as "opaque" and the validation step still requires a database lookup by Apigee. It's still an opaque token that cannot be verified by any party that is not the "token issuer". 

OK that's opaque tokens. Later, Apigee added support for JWT, in a couple phases. In the first phase, Apigee introduced new policies that generate and verify signed JWT.  Now a JWT is a form of token, and it can be used as an access token, but it can also be used for other purposes (like ID token). In any case, to generate a JWT, within Apigee, you can use the GenerateJWT policy, and there you specify the algorithm, the key, and the various claims (aud, sub, exp, iat, etc) you want to appear in the header or payload. And then Apigee generates your signed token. As with the opaque token, Apigee delivers this token back to the client app, and then on subsequent API requests, the client app can attach that token.  Apigee receives the JWT, and can verify the signature, and then make decisions based on the claims in that token.  In this case the token is not opaque - the client can "see" the claims in the token.  But for the purposes of granting access, that isn't really important. It's still Apigee that acts as the API Gateway here, and so it's the Apigee runtime that needs to verify the token signature with each inbound request, and then make an authorization decision based on the verified claims. Note there is no database lookup involved in verifying a signed JWT.  The token is verifiable just by looking at the token, and doing the signature arithmetic. The only thing necessary for this is access to the cryptographic key that allows signature validation. In a token signed with RS256, that verification key is an RSA public key, which means it can be disclosed publicly. Often a token issuer will disclose its public keys to everyone via a well-known JWKS endpoint.  For some examples, in the case of United Parcel Service, that endpoint is here. In the case of Google, it is here. Because there is no database lookup, any party with access to the key can verify the signature. For this reason we can say that JWT are "federated" - meaning party1 can issue the token and party2 can verify it.  This is not the case with opaque tokens - the party that issued the token is the only party that can verify it. 

There are pros and cons of opaque vs federated (JWT) formats.  What YOU are doing is an unusual combination of these two things. You are generating a JWT, which as i said, is a token format that requires no database lookup to verify.  You are then telling Apigee (a) that this JWT is an opaque token; (b) that this token was generated externally, and (c) please store it in the opaque token store. This is the part that does not make sense to me. It is highly unusual to STORE a JWT.  There's no need!  This is why I am asking, WHY ARE YOU DOING THIS? WHAT IS YOUR BUSINESS GOAL?

Usually a person would either (a) generate an opaque token (and particularly, let Apigee handle the generation), and store it; or (b) generate a JWT, and not treat it as an opaque token, and not store it.  You are doing (c), generate a JWT, and store it.  This seems to lose the benefits of either option. 

When you say "my goal is to create an OAuth JWT" that does not explain why.  Why do you want a JWT?  Why does it have to be a JWT?  Why can it not be a normal, opaque OAuth token? What problem are you trying to solve? 

I think it would help me to understand if you could provide an explanation that is more than 8 words in length. 

 

 

Thanks for the above Explanation. 

I would have insert a scope attribute (which is specific to consumer/client) in the token. In the subsequent requests, When Consumers presents this token to an API call, the scope inside the token needs to be verified and allow the request to the Target Endpoint.

How do I do this in case of an Opaque Token? I know I could obtain the claims in Case of Decode JWT Policy.

@dchiesa1 

With an opaque token, you can associate a scope to the token at the time of issuance, when you use OAuthV2/GenerateAccessToken. That policy accepts a Scope element to set the scope of the issued token.  To make this work, you first need to configure a set of allowable scopes on the API Product itself.  

scope-settings.png

Then, in OAuthV2/VerifyAccessToken, you can check the scope on the attached token.  (Specify the Scope element here, again). 

And the VerifyAccessToken with a Scope element will succeed if and only if the presented (opaque) token was originally issued with the scope that is specified. The client app does not "see" the scope, because, as I explained, this is an opaque token. To the client, it's just a random string of characters. 

Normally this Scope is set as part of a 3-legged OAuth flow, in which the user specifically consents to a set of 1 or more scopes. And that is the scope-set that you attach to the token during OauthV2/GenerateAccessToken. 

Having said all of this - are you sure you need scopes?  the API Product concept in Apigee might solve your problem more elegantly.  On each API Product, you can configure a set of operations  - verb+path pairs - which are allowed by the API Product.  Through this mechanism, the API product operation functions as a  way to wrap automatic authorization for specific operations.  A client app authorized for Product1 might be able to perform GET /widgets , and a client app authorized for Product2 might be able to perform GET /widgets as well as POST /widgets. 

There are multiple ways to solve the authorization problem in Apigee.

In any case I think it's a good idea to avoid generating a JWT and then trying to ingest it as an opaque token - that seems like a long, complicated path to get to your goal.