Questions on Security for Authorization Code Grant Type

I am currently building a login app that implements the authorization code grant type (3-legged OAuth).

I have it working, where a calling app calls a /authorize endpoint, the login app verifies credentials and gets an authorization code, then the login app redirects back to the calling app with the authorization code which then makes the call for the access token.

My concern is in the call for the authorization code made by the login app. The intent is that only users with verified credentials should be able to fetch an authorization code. But couldn't anybody with knowledge of the public ClientId of the apigee app call that proxy endpoint and effectively bypass my credentials verification? Then they would have the authorization code and ability to fetch an access token despite not offering credentials.

Is there a measure I need to take to prevent this malicious use or something built in I am unaware of that prevents it? Thanks!

0 4 362
4 REPLIES 4

YES

The sequence diagram for the AC grant type is shown here:

https://github.com/DinoChiesa/devjam3-20170405/tree/master/Resources/oauth2-ac

You are talking about the connection between the login-and-consent endpoint and Apigee. That is probably a request like POST /code (or similar, it's outside the scope of the OAuth2 standard) from the login UI back to the Apigee token management endpoint.

I suggest using something like:

  • a token issued via client_credentials grant, or maybe urn:ietf:params:oauth:grant-type:jwt-bearer
  • an HMAC-signed request with a timestamp in it, to prevent replays.
  • a signed JWT representing the request.

In any of these cases, the login app needs to have credentials of its own. A client id and secret ,or a client_id and a keypair. The Apigee endpoint would need to verify the inbound credentials (token or signature) before generating the code.

If you were doing HMAC , the string-to-sign should probably be something like path:verb:timestamp . Use the client_secret (belonging to the login app!) to sign. And that results in a base64- or base16-encoded string. The client (login app) should place that in an Authorization header with the client_id (of the login app!) and timestamp. The server (apigee) would extract those three items: client id, timestamp, and signature. Verify the timestamp is not more than 10 seconds old (to allow for clock skew and transit delay). Then VerifyApiKey on the client_id. That will pull in the associated client_secret into a context variable. Then produce the string-to-sign (path:verb:timestamp) on the server side. Use an HMAC policy to generate the signature, and then check that the signatures match. Only if all this checks out, should Apigee issue the code. For best hygiene you could rate-limit this request as well.

If you use a signed JWT to hold the request, it's the same idea as above.

  • require asymmetric signature (RSA or ECDSA key)
  • require the JWT to include an iat claim (issued-at time)
  • require the JWT to include an issuer claim, which holds the client_id of the login app.
  • The JWT payload would also include a claim indicating the client id for which the code should be issued.

The process for a JWT would be:

  1. DecodeJWT to get the client_id (issuer)
  2. VerifyApiKey on that ; this retrieves the custom attrs on the login app, which includes the public key
  3. VerifyJwt on the inbound request, using the retrieved public key. This implicitly verifies the issued-at time (IF ONE EXISTS!)
  4. use Conditions to check for existence of the iat claim
  5. rate limit
  6. Then issue the code

A lot to unpack here, greatly appreciate the in-depth explanation. Wouldn't the calling app and login app be two different Apigee Apps though? And thus an authorization code generated using ClientId+Secret of the Login app not be valid to create an access token using the ClientId+Secret from the calling app? Or is that not an issue?

Yes, two distinct Apigee Apps.

The issue you are describing is not a concern. (I think)

The credentials of the login app are used only in the request sent by the login app to Apigee to retrieve the authorization session. They are not used for generating the code.

In fact I would use different grant_types.

client app to Apigee: authorization code

login app to Apigee: client_credentials (or one of the others I Described above, like HMAC or JWT)

In that case there is no "authorization code" generated with the credentials fo the login app. Remember, authorization_code grant_type is used only for third-party user-facing apps.

A trusted "headless" (or multiuser) app like the login app is better authenticated via client_credentials or similar.

Ah, so we are taking advantage of the fact that only our internal apps have the secret key to our separate apigee app, and using that as a signature to verify only approved users are calling the /code endpoint. That makes sense. I saw in another resource that you mentioned a HMAC policy will be released soon, is that out yet? I don't see it as available but it seems it may be useful for this flow.