24

I have successfully implemented a Lambda authorizer for my AWS API Gateway, but I want to pass a few custom properties from it to my Node.js endpoint.

My output from my authorizer follows the format specified by AWS, as seen below.

{
  "principalId": "yyyyyyyy",
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow|Deny",
        "Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
      }
    ]
  },
  "context": {
    "company_id": "123",
    ...
  }
}

In my case, context contains a few parameters, like company_id, that I would like to pass along to my Node endpoint.

If I was to use a Lambda endpoint, I understand that this is done with Mapping Template and something like this:

{
  "company_id": "$context.authorizer.company_id"
}

However, Body Mapping Template is only available under Integration Request if Lambda is selected as Integration type. Not if HTTP is selected.

In short, how do I pass company_id from my Lambda authorizer to my Node API?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Magnus Engdal
  • 5,446
  • 3
  • 31
  • 50
  • What about dropping these values into HTTP request headers? – Michael - sqlbot Aug 12 '17 at 03:21
  • @Michael-sqlbot That's pretty much what I would like to do. Headers, url or body doesn't really matter. Anything would be fine. The question is how one would do that? – Magnus Engdal Aug 12 '17 at 06:36
  • 1
    Create a header in the integration request, then specify, e.g. `context.identity.whatever` *without* a `$` at the beginning. I'm not using a custom authorizer, but this works for me accessing other things in the context object and dropping them into headers that get forwarded to the origin... for example, I tried `context.stage` and `context.apiId` and they both forward as expected, as HTTP headers. Try that? – Michael - sqlbot Aug 13 '17 at 01:07
  • 1
    Also, the "Use HTTP Proxy Integration" option seems very poorly named. If you want to use request body mapping templates... just uncheck that option. It appears that option actually means "You know what? Just stay out of my way and proxy the request, as is, whatever it is, so that I can avoid messing with body mapping templates." You can proxy a request to your back-end with or without that option checked. – Michael - sqlbot Aug 13 '17 at 01:17
  • @Michael-sqlbot Thank you Michael! Finally got it to work with the help of your answer and aws support. I needed to set `context.authorizer.company_id` as HTTP Headers under Integration Request **and** `company_id` as HTTP Request Headers under Method Execution. – Magnus Engdal Aug 15 '17 at 12:11
  • Fantastic. API Gateway has a lot of potential value, but it's hard to avoid the feeling that the documentation authors are "too close" to the service -- things that are obvious to them are not necessarily obvious to the rest of us, and go unstated... and some of the examples, I think, are too simple to really illustrate its tremendous potential. I'll do some more reading and look for some relevant docs citations, then post an answer. – Michael - sqlbot Aug 15 '17 at 12:29
  • I already posted an answer @Michael-sqlbot, but if you do as well, I'll remove mine and put yours as the accepted. Thanks! – Magnus Engdal Aug 15 '17 at 12:40
  • Apologies, actually... I didn't see your answer at the time I added my comment, so I didn't mean to imply that yours wasn't adequate. Thanks for the shout-out. – Michael - sqlbot Aug 15 '17 at 15:13

3 Answers3

47

Most of the credit goes out to @Michael-sqlbot in the comments to my question, but I'll put the complete answer here if someone else finds this question.

Authorizer Lambda

It has to return an object in this format, where context contains the parameters you want to forward to your endpoint, as specified in the question.

{
  "principalId": "yyyyyyyy",
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [{
      "Action": "execute-api:Invoke",
      "Effect": "Allow|Deny",
      "Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
    }]
  },
  "context": {
    "company_id": "123", <-- The part you want to forward
    ...
  }
}

Method Request

Under Method Request / HTTP Request Headers, add the context property you want to forward:

  • Name: company_id
  • Required: optional
  • Cashing: optional

Integration Request

And under Integration Request / HTTP Headers, add:

  • Name: company_id
  • Mapped from: context.authorizer.company_id
  • Cashing: optional
Magnus Engdal
  • 5,446
  • 3
  • 31
  • 50
  • 3
    I think this is a powerful undocumented (at least no listed in the help) feature in AWS Service Integration. – csikos.balint Sep 11 '17 at 19:54
  • I think that adding the header to the "Method Request" is not necessary – Rich Nov 13 '20 at 12:16
  • Thanks a lot for this. Saved me a lot of time. As @Rich said, header is not required in the 'Method Request' – LahiruBandara Nov 22 '20 at 10:11
  • @Magnus-Engdal - Thank you for taking time to document the complete answer. It worked for me. – Prodip Mar 15 '21 at 04:02
  • Did you finally manage to get this to work? I followed the steps, but I continually see `event.headers == undefined` – deitch May 10 '21 at 17:03
  • How this can run locally? after the deployment its working, but not locally – aleXela Jun 09 '21 at 12:07
  • 1
    @Magnus How have you addressed the security implications of this? If someone sends a header in of `company_id` does your mapping overwrite it? Curious because if not someone could spoof a different company in this instance and your Lambda will trust it. Would you still perform a validation check against a decoded bearer token and it's claims? – Johnathon Sullinger Aug 12 '21 at 02:38
  • Thanks for that. I can confirm that the header is not required in 'Method request' And if, like me, you want to access a custom attribute from a Cognito userpool authorizer, the syntax is this one: context.authorizer.claims.custom:attribute Don't use the square bracket syntax (claims['custom:attribute']), it does not work. – OBones Jan 21 '22 at 09:31
  • I have been doing the exact same thing for months and now magically one endpoint is getting an undefined authorizer for no apparent reason! How can that be possible? I'm stuck trying to fix this. I cannot access the logged user id anymore! – demian85 Mar 09 '22 at 17:00
  • In our setup (where we have VPC_PROXY / VPC_LINK integration type and "Use proxy integration" is checked) we needed to add the header to Method Request as well. – Rok T. May 11 '23 at 19:15
7

If you're using lamda-proxy, you can access the context from your event.requestContext.authorizer.

So your company_id can be accessed using event.requestContext.authorizer.company_id.

yurisich
  • 6,991
  • 7
  • 42
  • 63
Noel Llevares
  • 15,018
  • 3
  • 57
  • 81
  • 2
    Don't use lambda proxy, and I would like to avoid it if possible. – Magnus Engdal Aug 11 '17 at 13:35
  • 1
    Oddly if I use the normal lambda integration, `$context.authorizer` is empty, so I can't find a way to pass that information to the lambda via body mapping. I am using a REST api in API GW and using cognito as an authorizer. – Marcello Romani Apr 26 '20 at 15:22
0

If you're using lamda-proxy (at least with Golang backend) you can access to that values stored on authorizer context without mapping template usage! Remember re-launch API and wait a minutes!
It's working for me.

Tyler2P
  • 2,324
  • 26
  • 22
  • 31