Search code examples
amazon-web-servicescurlaws-lambdaamazon-cognito

How can I secure lambda functions with a cognito user pool?


Originally asked: What is the pure javascript or curl solution to get a cognito identity pool token using a user pool token? But question clarification led us in a different direction. Editing to make this question/answer easy to follow.

I am currently prototyping, and I have a working pure javascript solution to login from a website and obtain a cognito user pool token. How can I access lambdas using this token?

I setup my lambdas with serverless framework to use a cognito user pool

    events:
      - http:
          path: hello
          method: get
          authorizer:
            name: authorizer
            arn: arn:aws:cognito-idp:us-east-2:12345:userpool/us-east-2_UvCsU4UcR

This automatically creates an API gateway authorizer and associates it with the endpoint.

Solution:

Working with @Quassnoi, I discovered I was using the wrong token when accessing the lambdas. The object that comes back from POSTing to:

<YOUR_COGNITO>.auth.us-east-2.amazoncognito.com/oath2/token

contains an id_token, and an access_token. If you grab the id_token and paste it into curl as specified in the solution, you can access the secured lambda, with no other configuration required other than the 3 lines to your serverless configuration.


Solution

  • It looks like there is a different way to directly specify a user pool for authentication

    That's exactly how I would use it. You don't need an identity pool for that.

    I'm assuming you know how to create a user pool on cognito and exchange your credentials for a token.

    1. Set up the Authorizer on your API Gateway. I'm not sure how to do that on Serverless, but on Terraform you would use something like this:

      resource "aws_api_gateway_authorizer" "hello" {
          name                   = "demo"
          rest_api_id            = "myapiid"
          type                   = "COGNITO_USER_POOLS"
          provider_arns          = ["arn:aws:cognito-idp:{region}:{account_id}:userpool/{user_pool_id}"]
      }
      

      By default, this will use Authorization header for the identity source, you will see it in the console.

    2. In the Resource definition of your API Gateway, set up your authorizer:

      resource "aws_api_gateway_method" "hello_get" {
        rest_api_id          = "myapiid"
        resource_id          = "myapiid.hello.id"
        http_method          = "GET"
        authorization        = "COGNITO_USER_POOLS"
        authorizer_id        = aws_api_gateway_authorizer.hello.id
        authorization_scopes = ["my.authorization.scope"]
      
        # Lambda integration setup here
      }
      
      
    3. Deploy and pass the id token from your User Pool in the request:

      curl -H "Authorization: Bearer [id-token]" https://my-gateway-url.example.com/stage/hello
      

    That's it!

    If your token has my.authorization.scope in its scope claim, the request will get authorized and passed to the Lambda handle.

    All the information from JWT will get passed to the integration in the variable $event.requestContext.authorizer.jwt assessible within the mapping template.

    You're free to use it in a mapping and pass it to the Lambda code, should you want to.