# Authorizing API Gateway against a Cognito federated identity pool

**URL:** <https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985>\
**Category:** Serverless Framework\
**Created:** [March 12, 2018, 11:03am UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985 "2018-03-12T11:03:40Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![candlerb](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.serverless.com/candlerb/32/1350_2.png) [@candlerb](https://forum.serverless.com/u/candlerb)\
**Post date:** [March 12, 2018, 11:03am UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985/1 "2018-03-12T11:03:40Z")

</div>

I am trying to get an API Gateway/Lambda web application (python flask with serverless-wsgi) to use a Cognito federated identity pool to authenticate/authorize web clients. The underlying requirement is to get a number of apps to permit access only to authorized Salesforce or Office365 users; and I don’t want to write OpenID Connect code in each application, but want to outsource this pain to Cognito.

So I created a Salesforce identity provider in IAM, and a Cognito identity pool linked to this, following [this guide](https://aws.amazon.com/blogs/security/building-an-app-using-amazon-cognito-and-an-openid-connect-identity-provider/). (Aside: unclear as to what the callback URL should be, but I just used my app URL with `/callback` appended for now)

And I added to serverless.yml:

```
  web:
    handler: wsgi.handler
    events:
      - http:
          path: /
          method: ANY
          authorizer:
            arn: arn:aws:cognito-identity:eu-west-1:XXXX:identitypool/eu-west-1:YYYY
      - http:
          path: "{proxy+}"
          method: ANY
          authorizer:
            arn: arn:aws:cognito-identity:eu-west-1:XXXX:identitypool/eu-west-1:YYYY

```

This is based on the example [here](https://serverless.com/framework/docs/providers/aws/events/apigateway/) for an existing Cognito User Pool, although I realise that a Cognito Identity Pool is different (nice explanation [here](https://serverless-stack.com/chapters/cognito-user-pool-vs-identity-pool.html))

Now when I do `sls deploy` I get this error:

```
Serverless: Checking Stack update progress...
...........
Serverless: Operation failed!

  Serverless Error ---------------------------------------

  An error occurred: A3e1b0cdac43LambdaPermissionApiGateway - Unable to parse HTTP response content.

```

If I do `SLS_DEBUG="*" sls deploy` then I get a slightly different error:

```
Serverless: Checking Stack update progress...
.........
Serverless: Operation failed!

  Serverless Error ---------------------------------------

  An error occurred: A3e1b0cdac43ApiGatewayAuthorizer - Invalid lambda function.

  Stack Trace --------------------------------------------

ServerlessError: An error occurred: A3e1b0cdac43ApiGatewayAuthorizer - Invalid lambda function.
    at provider.request.then (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/monitorStack.js:112:33)
From previous event:
    at AwsDeploy.monitorStack (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/monitorStack.js:26:12)
    at provider.request.then (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/updateStack.js:84:30)
From previous event:
    at AwsDeploy.update (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/updateStack.js:84:8)
From previous event:
    at AwsDeploy.BbPromise.bind.then (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/updateStack.js:101:12)
From previous event:
    at AwsDeploy.updateStack (/usr/lib/node_modules/serverless/lib/plugins/aws/lib/updateStack.js:95:8)
From previous event:
    at AwsDeploy.BbPromise.bind.then (/usr/lib/node_modules/serverless/lib/plugins/aws/deploy/index.js:135:39)
From previous event:
    at Object.aws:deploy:deploy:updateStack [as hook] (/usr/lib/node_modules/serverless/lib/plugins/aws/deploy/index.js:131:10)
    at BbPromise.reduce (/usr/lib/node_modules/serverless/lib/classes/PluginManager.js:372:55)
From previous event:
    at PluginManager.invoke (/usr/lib/node_modules/serverless/lib/classes/PluginManager.js:372:22)
    at PluginManager.spawn (/usr/lib/node_modules/serverless/lib/classes/PluginManager.js:390:17)
    at AwsDeploy.BbPromise.bind.then (/usr/lib/node_modules/serverless/lib/plugins/aws/deploy/index.js:101:48)
From previous event:
    at Object.deploy:deploy [as hook] (/usr/lib/node_modules/serverless/lib/plugins/aws/deploy/index.js:97:10)
    at BbPromise.reduce (/usr/lib/node_modules/serverless/lib/classes/PluginManager.js:372:55)
From previous event:
    at PluginManager.invoke (/usr/lib/node_modules/serverless/lib/classes/PluginManager.js:372:22)
    at PluginManager.run (/usr/lib/node_modules/serverless/lib/classes/PluginManager.js:403:17)
    at variables.populateService.then (/usr/lib/node_modules/serverless/lib/Serverless.js:102:33)
    at runCallback (timers.js:672:20)
    at tryOnImmediate (timers.js:645:5)
    at processImmediate [as _immediateCallback] (timers.js:617:5)
From previous event:
    at Serverless.run (/usr/lib/node_modules/serverless/lib/Serverless.js:89:74)
    at serverless.init.then (/usr/lib/node_modules/serverless/bin/serverless:42:50)

```

If I look in `.serverless/cloudformation-template-update-stack.json` then it looks like it’s trying to use a custom lambda authorizer (although I really don’t grok cloudformation)

```
"A3e1b0cdac43ApiGatewayAuthorizer": {
  "Type": "AWS::ApiGateway::Authorizer",
  "Properties": {
    "AuthorizerResultTtlInSeconds": 300,
    "IdentitySource": "method.request.header.Authorization",
    "Name": "a3e1b0cdac43",
    "RestApiId": {
      "Ref": "ApiGatewayRestApi"
    },
    "AuthorizerUri": {
      "Fn::Join": [
        "",
        [
          "arn:aws:apigateway:",
          {
            "Ref": "AWS::Region"
          },
          ":lambda:path/2015-03-31/functions/",
          "arn:aws:cognito-identity:eu-west-1:XXXX:identitypool/eu-west-1:YYYY",
          "/invocations"
        ]
      ]
    },
    "Type": "TOKEN"
  }
},

```

So my problems are:

1. Is it even possible to use API Gateway with Cognito federated Identity Pool as an authorizer?

2. If it is possible, does serverless support this, and if so how do I configure it?

Thanks… Brian.

---

<div class="post-metadata">

**Author:** ![candlerb](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.serverless.com/candlerb/32/1350_2.png) [@candlerb](https://forum.serverless.com/u/candlerb)\
**Post date:** [March 12, 2018, 11:28am UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985/2 "2018-03-12T11:28:18Z")

</div>

Update: [this page](https://aws.amazon.com/blogs/compute/secure-api-access-with-amazon-cognito-federated-identities-amazon-cognito-user-pools-and-amazon-api-gateway/) is the closest I can find; it describes an angular js app which authenticates three different resources. The `/google` and `/cip` resources are authenticated against a federated identity pool, and the `/google` resource is authorized only to people who authenticated via the Google identity provider within that pool.

The code for this application is [published](https://github.com/awslabs/aws-cognito-apigw-angular-auth), but I’m lost in the [cloudformation template](https://github.com/awslabs/aws-cognito-apigw-angular-auth/blob/master/sam/sam.yaml).

Note: I don’t necessarily want to create the identity pool from serverless; having a single pre-existing identity pool which is shared by many apps would in fact be better.

But I will need, somehow, to create permissions such that only authenticated users can access each app. Preferably this will be further restricted for authorization, e.g. only to users in a particular group, although as long as the claims are passed through to the app I can check it there.

My head is starting to explode 🙂

---

<div class="post-metadata">

**Author:** ![candlerb](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.serverless.com/candlerb/32/1350_2.png) [@candlerb](https://forum.serverless.com/u/candlerb)\
**Post date:** [March 12, 2018, 12:23pm UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985/3 "2018-03-12T12:23:27Z")

</div>

Oh, and just a bit of background. Right now the way I solve this is to have a couple of EC2 instances running Apache, mod\_proxy and mod\_auth\_openidc.

Users hit the proxy URL, and mod\_auth\_openidc transparently redirects them to the OpenIDC provider if necessary; only when they’ve authenticated does it proxy the request through to API Gateway. It can also enforce authorisation, e.g. specific organisation claim.

The great thing about this is that it’s completely transparent to the Lambda/Flask application. The only code needed in Lambda is to check the source IP address of the connection - to check that the request is coming from the proxy, so that people can’t bypass the authentication by hitting the API gateway URL directly.

With AWS able to attach custom domains to API gateway instances, and ACS issuing the certificates for those custom domains, I thought it would be much cleaner if I could get rid of the proxy VMs and have people connect directly to API gateway. But that means doing the full OpenID authentication/authorisation in API gateway too.

Right now it looks like the web app would need to serve a bunch of Javascript which handles the entire [flow](https://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html):

1. Direct the user to the identity provider(s) to pick up credentials (IDP-specific?)
2. Pass these credentials to Cognito to get an IAM token ([this?](https://docs.aws.amazon.com/cognito/latest/developerguide/getting-credentials.html#getting-credentials-1.javascript) and/or [this?](https://docs.aws.amazon.com/cognito/latest/developerguide/open-id.html#set-up-open-id-1.javascript))
3. Include the IAM token for each subsequent web access
4. Deal with expired tokens

… plus configure IAM roles to perform the authorisation to specific API gateway URLs.

---

<div class="post-metadata">

**Author:** ![nachoab](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.serverless.com/nachoab/32/163_2.png) [@nachoab](https://forum.serverless.com/u/nachoab)\
**Post date:** [March 16, 2018, 7:37am UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985/4 "2018-03-16T07:37:05Z")

</div>

Yes it is possible to use Federated identities. This is a simple way to do it:

- User requests a token directly to the provider (i.e. Google)

- User sends the token to my public APIG endpoint:

- On Lambda:

- From now, user has to sign requests from the client using those credentials to endpoints like this:

* * *

You can also take a look at this [serverless auth boilerplate](https://github.com/laardee/serverless-authentication-boilerplate)

---

<div class="post-metadata">

**Author:** ![candlerb](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.serverless.com/candlerb/32/1350_2.png) [@candlerb](https://forum.serverless.com/u/candlerb)\
**Post date:** [March 16, 2018, 9:17am UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985/5 "2018-03-16T09:17:53Z")

</div>

Thank you. Can I just clarify a few points: my scenario is a Flask web app running in Lambda, and user has a regular web browser accessing this app, just following links in HTML.

> User requests a token directly to the provider (i.e. Google)

If this takes place in the browser, presumably this means my Flask app has to return a page containing Javascript for the user to request the token - or else the Flask app returns a HTTP redirect pointing the user directly to Google.

I’m guessing that the callback URL I should use when requesting the token is the `auth/signin/google` service you mentioned (which I need to provide as part of my Flask app)

> Verify the token

Does this mean the Flask app needs to get involved in JWT decoding and signature verification? This is similar to what I’d have to do to use OpenID Connect without Cognito.

> Return credentials to the user

In a cookie? Otherwise how else will the browser retain them for the next request?

> From now, user has to sign requests from the client using those credentials to endpoints like this:

This part I don’t understand. How can I make the client’s web browser send headers like `X-Api-Key` and `X-Amz-*` with every request? Does every page request have to come from Javascript XmlHttpRequest, rather than just letting the browser follow links? If so this would be a complete rewrite of the app, using a client-side framework like sammy or angular.

The way I handle it at the moment is:

1. Flask app runs in API Gateway (using serverless-wsgi)
2. I have an Apache reverse proxy running in EC2
3. Reverse proxy runs mod\_auth\_openidc
4. Flask app has a small wrapper that blocks all requests unless the source IP is the proxy’s public IP:

```auto
def lambda_app(environ, start_response):
    if environ["REMOTE_ADDR"] not in TRUSTED_SOURCES:
        start_response("403 Forbidden", [("Content-Type", "text/html")])
        return ["Access denied"]
    return app(environ, start_response)

```

The net effect is that the Flask app is unmodified - it just receives HTTP requests and returns HTML pages. If an unauthenticated / unauthorized user connects to the proxy, then mod\_auth\_openidc intercepts this, redirects them to Google/O365 or whatever, handles the `/callback` response, gives them a suitable cookie, and performs the necessary authorization. e.g.

```auto
        <Location "/admin">
                AuthType openid-connect
                Require claim organization_id:XXXXXXXX
        </Location>

```

I was hoping to achieve something similar with API Gateway and Cognito, but looks like I was hoping for too much.

Aside: the proxy is going to remain for some other applications anyway, because those applications require HTTPS client certificate authentication, which API Gateway definitely can’t do.

> You can also take a look at this serverless auth boilerplate

Ah, thank you.

As a non-Javascript person, I’m trying to decode the [source](https://github.com/laardee/serverless-authentication-gh-pages) to [serverless-authentication-gh-pages](http://laardee.github.io/serverless-authentication-gh-pages). As far as I can see:

- It’s client-side HTML and Javascript
- When the user clicks on the Google login button, it triggers a redirect to page `authenticationEndpoint + '/authentication/signin/google` (via `window.location.href`)
- authenticationEndpoint is an API Gateway URL front-end to lambda, in this case it’s `handler.signin` from serverless-boilerplate
- this in turn calls the signin code from `serverless-authentication-google`: this part I get lost in
- by the time this is complete, the user somehow lands back at the original page. However if I start from a local checkout of index.html I end up at [http://laardee.github.io/serverless-authentication-gh-pages:](http://laardee.github.io/serverless-authentication-gh-pages:) so either this is the Google callback URL, or the authentication lambda function knows this URL.
- somehow an `authorization_token` ends up in local storage
- when the user tries to access the protected URL (`/test-token`), this is done via XHR with an added `Authorization:` header containing this token
- back in serverless-authentication-boilerplate, somehow the `Authorization` header is checked:

```auto
          authorizer:
            arn: replace-with-arn-of-the-authorizer-function
            resultTtlInSeconds: 0
            identitySource: method.request.header.Authorization
            identityValidationExpression: .*

```

(so presumably this is using a Lambda custom authorizer, not IAM authorization. In fact I don’t think it’s using Cognito at all)

But even given that I don’t understand this fully, my main take-away is that the client browser is unable to access any protected page except via XHR (to add the `Authorization:` header)

---

<div class="post-metadata">

**Author:** ![HintikkaKimmo](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.serverless.com/hintikkakimmo/32/797_2.png) [@HintikkaKimmo](https://forum.serverless.com/u/HintikkaKimmo)\
**Post date:** [May 9, 2018, 7:45am UTC](https://forum.serverless.com/t/authorizing-api-gateway-against-a-cognito-federated-identity-pool/3985/6 "2018-05-09T07:45:42Z")

</div>

Run into same issue after you and your post probably saved me several hours of hitting my head to the wall… Thanks!
