Oauth2 token generate client-to-IDP-Apigee or client-Apigee-IDP

We want to generate a token in IDP (Azure AD) for external clients, and then verify it at Apigee.

I am confused between two scenarios -

A. client communicate directly to IDP fetch the token and then present it to Apigee and Apigee Validates

B. client communicate directly to Apigee and then Apigee communicates to IDP(Azure AD) to generate the token and give it back to client

In some posts I see option A has been been used, but not much has been given for reasoning to choose this option.

@Dino-at-Google @Priyadarshi Ajitav Jena if you can share some thoughts.

10675-apigee-azure001.jpeg

A.

Pros

1. Gateway is not doing the token generation and cache handling

2. JWT expiry is taken care directly at client level not at Apigee

Cons

1. Client has to maintain {clientid,secret} of IDP(Azure AD) and at-least client id of Apigee

B.

Pros

1. One set of clientid, secret only i.e Apigee

2. Abstracts the token generation and interaction with IDP part from clients

3. Reduces coupling if we move to another IDP clients will not break as they can still use same client id, secret. Changes would be at Apigee level

4. Can cache the token at Apigee to avoid callouts for each request

Cons

1. Expiry of tokens in Apigee cache

2. Maintaining priviliges in Apigee for IDP. May be concern as per organization policies

thanks,

Aakash

1 2 338
2 REPLIES 2

I think you can use either approach. To my eyes, option A seems more appropriate and simple, and more secure.

In fact, security alone may be a good reason to reject option B. If the client transmits its credentials to Apigee, only for Apigee to then propagate them to the IdP (Azure AD), Apigee now handles credentials that do not belong to Apigee. That is an unnecessary exposure. Each system that handles credentials adds to the potential attack surface of the overall supersystem or application. A good rule to follow is: don't share credentials beyond the boundaries of those actors that REQUIRE the credentials. The client obviously needs to have its credentials - the client credentials. Those must be stored by the client. The Identity provider must have a way to verify those credentials. If authentication is based on public/private keys, then the client must possess the private key and the IdP must hold the public key (or be able to obtain the public key). On the other hand if the client credentials are based on shared secrets (example: client id + secret), then both the client and the IdP must have access to the same id+secret pair. But in either case, no other system needs access to those credentials.

If you apply option B, Apigee will also handle and possess (even briefly) the client credentials. This violates the rule, and so should be avoided.

As for your Pros and Cons, I don't agree that the things you identified are pros or cons. For example, you listed as pro for Option A:

1. Gateway is not doing the token generation and cache handling

2. JWT expiry is taken care directly at client level not at Apigee

What I mean is: Both of those are value-free statements. They are true, but whether they are good or bad is dependent upon other external factors.

Also, you wrote, as a Con for Option A:

1. Client has to maintain {clientid,secret} of IDP(Azure AD) and at-least client id of Apigee

And I think this one is not necessarily true. It is possible to architect the system in such a way as to require the client maintain two distinct client ids, but that would present complexity to the developer that ideally we could avoid. Why not synchronize the client ids across Apigee ad Azure so you can avoid that?

Regarding the Pros for Option B, which you listed as:

1. One set of clientid, secret only i.e Apigee

2. Abstracts the token generation and interaction with IDP part from clients

3. Reduces coupling if we move to another IDP clients will not break as they can still use same client id, secret. Changes would be at Apigee level

4. Can cache the token at Apigee to avoid callouts for each request

I have already addressed the "one set of clientid+secret" - you can synchronize these across Apigee and Azure.

Regarding #2, I think OpenID Connect is a standard and many client libraries already include support for the authentication flows that AzureAD requires. So this may not be a large advantage.

Regarding #3, I suppose it is a matter of judgment, if you believe you may change IdP. If you change to another OIDC-compliant IdP, then the flows should be minimal - just change the endpoints and the credentials.

Finally, regarding the last "Pro" of option B, caching. I have comments.

  1. Verifying a JWT within Apigee takes milliseconds. Many people believe it is required that a token verifier must cache verified tokens, to deliver acceptable performance. This isn't necessarily true. I'd encourage you to measure and benchmark your scenario before drawing conclusions. If you have a very high throughput, you will get better scale if you cache the verified tokens, but it's not always required.
  2. If you DO decide to to use caching, you can design the Apigee API Proxy to cache a previously verified JWT. The pattern is: the client presents a JWT signed by Azure AD. Apigee performs a SHA256 hash on the token, obtaining a unique hash of that token. Then Apigee checks the cache for an entry using that hash. If the entry exists, retrieve it and honor that token. If the entry does not exist, call VerifyJWT. If the token is valid and not expired, then populate the cache with the verified and relevant claims from the token, setting the cache expiry to match the token expiry. This is extra work, but the retrieval of the token from cache will consume <1ms while verifying an RSA signature can take ~5ms. At scale this can make a material difference. The point is, Apigee can cache the results of signed JWT.
  3. Another option is to design Apigee to accept the Azure-signed JWT and return an Apigee-generated opaque token with a lifetime that coincides with the lifetime of the JWT. On subsequent calls the client presents the apigee-generated token, and the Apigee proxy calls OAuthV2/VerifyAccessToken. This is effectively the same as the approach above, but the API Proxy does not need to directly interact with the cache; instead it just uses the builtin caching of VerifyAccessToken.

Also, regarding token verification, you wrote:

Can cache the token at Apigee to avoid callouts for each request

Apigee never needs to call out to Azure AD in order to verify a signed token generated by Azure AD. When Azure AD generates a token with an audience that is NOT hxxps://graph.microsoft.com/.default, Azure signs it using its own private key. External interested parties can then verify the token using Azure's public key, which is accessible at a public endpoint, and which is cacheable. Apigee can call out to the Azure public key endpoint, retrieve the public keys, and then use those keys to repeatedly verify Azure-generated JWT, without every contacting Azure AD again. There's no need for Apigee to call out to Azure in order to verify a JWT that Azure has generated and signed.

OK In summary I think Option A is preferred, for reasons of security. I think the Pros/Cons analysis you offered is a little incomplete - there are other things to consider. Also I think the Pros you listed for Option B are not limited to Option B, or are not real advantages.

Thanks for detailed explanation. I have some questions

1.

"the client transmits its credentials to Apigee, only for Apigee to then propagate them to the IdP (Azure AD), Apigee now handles credentials that do not belong to Apigee."

AS: In (B) step 1 the clientid,secret are not of azure AD but one that would be issued from Apigee itself. So client would just maintain apigee's clientid,secret. The communication between Apigee to Azure for token generation is abstracted from client. Yes we would still need azure client app credentials at Apigee in this case.

In option A he would need to maintain client id of apigee and client id,secret of azure. (i have updated diagram if it wasn't explicit)

2. synchronize the client ids across Apigee ad Azure

AS: I would explore this.

3.

"Another option is to design Apigee to accept the Azure-signed JWT and return an Apigee-generated opaque token with a lifetime that coincides with the lifetime of the JWT"

AS: As i understand client gets azure token and then we create a token endpoint in Apigee where this azure token gets validated and we issue an opaque token. Subsequent calls to access resource api will need to use opaque token. Having two token generate (or exchange) endpoints for client could there be any other implications with this design.

thanks,

Aakash