Where can I find Cognito or IAM details from API Gateway when using HTTP Direct?

Previously, when my ionized handler was integrated into my API Gateway as a Lambda Integration, incoming requests had two keys that held important information about the request from the API Gateway [1]:

  • datomic.ion.edn.api-gateway/json
  • datomic.ion.edn.api-gateway/data

Since switching to HTTP Direct, these keys are no longer present.

With these keys missing, where else can I find details about the request from the API Gateway, specifically the Cognito and/or IAM details of the requester? (See related post here [2])

My setup is as follows:

  • API Gateway with VPC Link integration to HTTP Direct
  • A resource / method configured with Proxy Integration
  • Method is configured with AWS_IAM authorization

Authorization works as expected, and requests proxy to the HTTP Direct project, but nowhere in the request can I find Cognito or IAM details of the requester, or information about the API Gateway.

[1] https://docs.datomic.com/cloud/ions/ions-reference.html#web-ion

[2] Datomic Ions Using Cognito User Pool APIGateway Authorizer

4 Likes

Hi @joshkh, I’ve reproduced this behavior and I am discussing with the team. I’ll update this thread as soon as I have more information to share.

2 Likes

This is relevant for me as well. I plan to move to http-direct and I’m using a custom Cognito authorizer. This will stop me from doing so.

Hi @joshkh and @steve.nextdoc,

It turns out that our documentation was incorrect. I’ve updated the docs here to reflect that these keys are present only when doing lambda proxy integration.

What information are you specifically seeking? Are you seeking the AUD or wanting to verify the JWT? Are you trying to link to users? We may be able to describe a work around we have seen that you could use manually, but I’d like to understand your needs here.

I need everything in the “authorizer” part of the lambda “requestContext”.
I use both Cognito and custom api gateway authorizers.

In the custom case, I need everything in the “context” part of this output

In the Cognito case, the access token is enough since it contains the Cognito SID and claims.

The only workaround I’m aware of is to use a cljs lambda (fast cold starts) to proxy the http-direct request. It can merge the data from the “requestContext” into the main request body which is seen by the Ion. Is this what you mean by the workaround?

Thanks @jaret for looking into this.

In my case, my API Gateway is protected via AWS_IAM authorization, and my users have Federated Identities via a Cognito Identity Pool. On the client side (using amplify) the users of the API get access keys, secret keys, session tokens, and their unique federated identities from an Identity Pool.

IAM authorization requires that a request be signed using an access key, secret key, and a session token, and the result cannot be decoded to find the user’s identity (unlike the JWT method that you and @steve.nextdoc mentioned).

I would have thought that the API Gateway would pass some information to identify the requester, which I think I’ve seen when using Lambda / Ionized handlers / User Pools, but it’s been a while and I can go back and verify.

So, in short, I am trying to find the caller’s Cognito Federated Identity (or any identifiable information about the requester) when using AWS_IAM, and I don’t think that the values in the X-Amz-Security-Token or AWS4-HMAC-SHA256 Authorization headers can provide that.

@steve.nextdoc

The only workaround I’m aware of is to use a cljs lambda (fast cold starts) to proxy the http-direct request. It can merge the data from the “requestContext” into the main request body which is seen by the Ion. Is this what you mean by the workaround?

This approach would log all the information you are proxying. I am not sure you want that.

In the custom case, we don’t really have much input to provide until amazon calls the ion. However, if I understand you correctly-- in both cases (cognito user pool case and custom lambda case), you don’t have the information you need to correlate to the user on the backend. An approach that I have seen to do this-- you can specify a custom HTTP header to include the JWT (in the api-gateway config). You then can pull the JWT from the HTTP header and you can split the JWT (which has n concatenated chunks that are base64 encoded) on a period .. Then you base64 decode each result looking for whatever information you are seeking. In this user’s experience they found they could count on the information you are seeking being in whatever (nth) chunk you find it in --every time.

Regardless if its a custom lambda or cognito authorizer, if you use your own Header the JWT contains all the information you are after.

@joshkh

I believe when you supply the Cognito identity token (not the access token --specific to the Cognito client side SDK/Amplify Cognito SDK), that the identity token is a JWT and can be decoded. Similarly, you can also supply that as an HTTP header.

Interesting. I’ll have to try direct to fully understand your suggestion.

You didn’t mention the “authorizer” data in the requestContext. I use this to pass along the data loaded by the custom authorizer lambda so that the Ion doesn’t have to re-read it.

Did you not mention it because it is not supported? In that case, I can’t use custom authorizers and I will have to move my auth code into the Ions themselves. I can do that but it’s not ideal.

I believe when you supply the Cognito identity token (not the access token --specific to the Cognito client side SDK/Amplify Cognito SDK), that the identity token is a JWT and can be decoded. Similarly, you can also supply that as an HTTP header.

Got it. I will continue to use the IAM signature related headers to authorize the API Gateway, and then also send the identity token for the purpose of decoding an identity from within our application. That should solve my problem, thanks @jaret!

Just curious, is the missing :datomic.ion.edn.api-gateway/json key a limitation of Ions + HTTP Direct? Or is it a limitation of the API Gateway’s Proxy Integration?

A limitation of API Gateway’s proxy integration. If you don’t go through a lambda you do not get those keys.

I can do this as well. Move auth from a lambda to an interceptor in the Ion.

A downside of this is DDOS. Any unauthenticated request can put load on Datomic, making it a pretty easy DDOS target.

For that reason, I’ll probably stick with my cljs proxy to http-direct.

@steve.nextdoc

Just a thought, you could greatly reduce the load of the JWT unsigning process by caching valid (and invalid) tokens, much like what the Gateway Authorizer functions do.

An easy solution could be to implement a TTL cache from clojure.cache [1]. The same token might be cached once per TTL window per node in your autoscaling group, but I don’t think that’s a big deal. And If you’re worried about memory from caching loads of tokens within the TTL window then you could look into a more sophisticated solution using AWS ElastiCache.

Either way, the goal is to reduce redundant unsigning of identical tokens. I’ve implemented both solutions to some degree, so feel free to ping me if you have any questions (either here or on Clojurians @joshkh).

[1] https://github.com/clojure/core.cache/wiki/TTL

@jaret

Got it, thanks for clarifying and also the work around!

@joshkh thanks. that is a good suggestion.

My situation is a bit different in that my tokens are not JWT and are single-use i.e. different for every request so I need to un-sign for every request. For that reason, I can’t cache.

The CPU overhead for un-signing is so low that I’m not concerned about the extra load.

Even with a cache, it still exposes the Ions to load so a DDOS attack would be possible. The best defence would be some kind of feature in the API Gateway but all of this is extra complexity. One extra lambda to transfer auth context to http headers is much simpler IMHO.