Does OAuth Verification is still required after App Key and JW Token Verification?

I have created 2 proxies.

1. Token Generation Proxy (input : App Key, Login User ID & Password)

a) App Key verification

b) Login Details verification

c) If login details are valid, Generate JWT Token and return

2.Business API Proxy (input : App Key, JWT Token, Local Input Parameters)

a) App Key Verification

b) JWT Verification

c) If JWT is valid, business API is called and response is returned

Do I still need to add OAuth 2.0 policies (grant type, client id, client credentials etc)?

Please suggest, do we need to include OAuth 2.0 as well.

Solved Solved
1 1 325
1 ACCEPTED SOLUTION

You can include OAuth2 token verification as an option. But it's not necessary.

I'd like to give a really brief, concise answer to you, but as I thought about it and started writing, I Realized this is going to be a bit of a longish discussion of the issues. Sorry about that. I think it's worth it to explore the issue to the proper degree.

First, I'm not clear on exactly how the JWT are being generated in your case. But from your description I am imagining that you are using something like an OpenID Connect flow, or maybe a password grant flow, in which the response is a JWT, rather than an opaque OAuthV2 token. One way or the other, the API Proxy verifies (a) the identity of the user, and (b) the identity of the app (client), and then issues a JWT. I suppose the JWT includes claims identifying both the user and the client, and the JWT is signed by Apigee using a key.

If that is the case, then the client app can use that JWT as an authentication mechanism for future API calls. The client can send the JWT to Apigee, Apigee can verify that the JWT was originally signed by Apigee, and then Apigee can rely on the claims contained within the JWT, make authorization decisions based on those claims, and so on.

You said in the case of the Business API Proxy, you plan to have the client send the API Key ("App Key"), the JWT, and other input parameters. Let's talk about that.

If the JWT is provided by Apigee, then it is possible for you to configure the GenerateJWT policy in Apigee to embed as a claim within the JWT, the clientid, in other words the API Key. If you configured the GenerateJWT policy this way, then there would be no need for the client to subsequently send the pair of (API Key, JWT) to the Business API Proxy. The JWT itself would contain the API Key, and APigee could (a) verify the JWT, then (b) extract the client id claim into a context variable (this is implicit and automatic during verification), and (c) verify the extracted API key. Obviously it would be redundant for the client to send the clientid alongside the JWT if you packaged the JWT in this way.

In general, Apigee architects advise against using the API Key for authentication of apps, because the API key generally has a long life, and that means the window of compromise for the API key is very Long (Time wise). This is part of the benefit of using OAuth2 tokens - they have a constrained lifetime, and compromise of any single token will mean a security exposure of 15-30 minutes or so if you've configured your token to have that expiry. API Keys don't work that way, so we generally advise people to take care when using API Keys for authentication.

Mitigating this concern is that the JWT does have an expiry. So when the client sends the JWT into Apigee, Apigee can evaluate the expiry of the JWT (nb: the VerifyJWT policy does this implicitly), and then reject any JWT that has expired. Because of the wrapper JWT, using a non-expiring credential (API Key) is not a problem. Which means it's reasonable to just have the client send the JWT to Apigee and let Apigee do the verification of the JWT and the embedded clientid, and everything will be good.

BUT, there's another twist.

JWT are designed to be verifiable by any party. While the JWT structure is inherently larger than an opaque OAuth token, it directly carries information, aka the claims. And so any party with access to the verification key (a secret key in the case of HS* algorithms, or a public key in the case of asymmetric algorithms like RS*, PS*, and EC*) can verify the JWT and then rely on the claims. This means the issuer of the JWT can be very far separated from the verifier of the JWT - that's the key benefit of shipping around a JWT of 256 or 370 or 500 bytes or more.

For comparison, the opaque OAuth2 token issued by Apigee is 28 bytes. A pretty minimal JWT can be 27 bytes in the header to declare the typ and alg, and then 100 bytes or so in the payload, plus the signature. Base64-encoding that can give you 500 bytes pretty easily. (try this page to explore sizes) Be thoughtful before designing your system so that clients must send 500+ bytes with every request. It may not be necessary, and it certainly is concerning, especially for a mobile client operating on a restricted network.

If the client is always sending the authorization token back to the original issuer, then there is no need to send a JWT, when a much smaller opaque token would do the job. In concrete terms, if Apigee generated the JWT, then it isn't necessary and it is probably inappropriate to configure your client to send that full JWT back to Apigee for verification purposes! In that case just use an opaque oauth token .

But there still is a good reason to use a JWT: to provide authenticated claims to the client itself! Apigee can sign the JWT, and send it to the client, and then the client app can verify the signature on the JWT and then rely on the claims within it. Maybe the client wants to make local decisions about those claims, for example presenting a different UI based on the asserted claims in the JWT.

To authenticate calls sent back to Apigee, the client can send an opaque OAuth2 token. In fact the OpenID Connect specification describes a response_type field that enables exactly this scenario: the authentication flow results in the issuance of a JWT AND an opaque oauth token. (See here for an example).

You don't need to explicitly use OpenID Connect in order to follow this practice.

So in summary, I think what I would do is design the system so that

  • in the token-issuing proxy, Apigee issues the JWT (GenerateJWT) and an OAuth token (OAuthV2/GenerateAccessToken)
  • the client verifies the signature on the (500+ byte) JWT from Apigee and can then rely on its claims
  • The client sends the opaque (28 byte) oauth token to Apigee for subsequent API calls
  • Apigee calls OAuthV2/VerifyAccessToken to authorize the Business API calls.

View solution in original post

1 REPLY 1

You can include OAuth2 token verification as an option. But it's not necessary.

I'd like to give a really brief, concise answer to you, but as I thought about it and started writing, I Realized this is going to be a bit of a longish discussion of the issues. Sorry about that. I think it's worth it to explore the issue to the proper degree.

First, I'm not clear on exactly how the JWT are being generated in your case. But from your description I am imagining that you are using something like an OpenID Connect flow, or maybe a password grant flow, in which the response is a JWT, rather than an opaque OAuthV2 token. One way or the other, the API Proxy verifies (a) the identity of the user, and (b) the identity of the app (client), and then issues a JWT. I suppose the JWT includes claims identifying both the user and the client, and the JWT is signed by Apigee using a key.

If that is the case, then the client app can use that JWT as an authentication mechanism for future API calls. The client can send the JWT to Apigee, Apigee can verify that the JWT was originally signed by Apigee, and then Apigee can rely on the claims contained within the JWT, make authorization decisions based on those claims, and so on.

You said in the case of the Business API Proxy, you plan to have the client send the API Key ("App Key"), the JWT, and other input parameters. Let's talk about that.

If the JWT is provided by Apigee, then it is possible for you to configure the GenerateJWT policy in Apigee to embed as a claim within the JWT, the clientid, in other words the API Key. If you configured the GenerateJWT policy this way, then there would be no need for the client to subsequently send the pair of (API Key, JWT) to the Business API Proxy. The JWT itself would contain the API Key, and APigee could (a) verify the JWT, then (b) extract the client id claim into a context variable (this is implicit and automatic during verification), and (c) verify the extracted API key. Obviously it would be redundant for the client to send the clientid alongside the JWT if you packaged the JWT in this way.

In general, Apigee architects advise against using the API Key for authentication of apps, because the API key generally has a long life, and that means the window of compromise for the API key is very Long (Time wise). This is part of the benefit of using OAuth2 tokens - they have a constrained lifetime, and compromise of any single token will mean a security exposure of 15-30 minutes or so if you've configured your token to have that expiry. API Keys don't work that way, so we generally advise people to take care when using API Keys for authentication.

Mitigating this concern is that the JWT does have an expiry. So when the client sends the JWT into Apigee, Apigee can evaluate the expiry of the JWT (nb: the VerifyJWT policy does this implicitly), and then reject any JWT that has expired. Because of the wrapper JWT, using a non-expiring credential (API Key) is not a problem. Which means it's reasonable to just have the client send the JWT to Apigee and let Apigee do the verification of the JWT and the embedded clientid, and everything will be good.

BUT, there's another twist.

JWT are designed to be verifiable by any party. While the JWT structure is inherently larger than an opaque OAuth token, it directly carries information, aka the claims. And so any party with access to the verification key (a secret key in the case of HS* algorithms, or a public key in the case of asymmetric algorithms like RS*, PS*, and EC*) can verify the JWT and then rely on the claims. This means the issuer of the JWT can be very far separated from the verifier of the JWT - that's the key benefit of shipping around a JWT of 256 or 370 or 500 bytes or more.

For comparison, the opaque OAuth2 token issued by Apigee is 28 bytes. A pretty minimal JWT can be 27 bytes in the header to declare the typ and alg, and then 100 bytes or so in the payload, plus the signature. Base64-encoding that can give you 500 bytes pretty easily. (try this page to explore sizes) Be thoughtful before designing your system so that clients must send 500+ bytes with every request. It may not be necessary, and it certainly is concerning, especially for a mobile client operating on a restricted network.

If the client is always sending the authorization token back to the original issuer, then there is no need to send a JWT, when a much smaller opaque token would do the job. In concrete terms, if Apigee generated the JWT, then it isn't necessary and it is probably inappropriate to configure your client to send that full JWT back to Apigee for verification purposes! In that case just use an opaque oauth token .

But there still is a good reason to use a JWT: to provide authenticated claims to the client itself! Apigee can sign the JWT, and send it to the client, and then the client app can verify the signature on the JWT and then rely on the claims within it. Maybe the client wants to make local decisions about those claims, for example presenting a different UI based on the asserted claims in the JWT.

To authenticate calls sent back to Apigee, the client can send an opaque OAuth2 token. In fact the OpenID Connect specification describes a response_type field that enables exactly this scenario: the authentication flow results in the issuance of a JWT AND an opaque oauth token. (See here for an example).

You don't need to explicitly use OpenID Connect in order to follow this practice.

So in summary, I think what I would do is design the system so that

  • in the token-issuing proxy, Apigee issues the JWT (GenerateJWT) and an OAuth token (OAuthV2/GenerateAccessToken)
  • the client verifies the signature on the (500+ byte) JWT from Apigee and can then rely on its claims
  • The client sends the opaque (28 byte) oauth token to Apigee for subsequent API calls
  • Apigee calls OAuthV2/VerifyAccessToken to authorize the Business API calls.