Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] REST API with Cognito User Pool authorization #345

Open
lawmicha opened this issue Feb 10, 2020 · 45 comments
Open

[Feature Request] REST API with Cognito User Pool authorization #345

lawmicha opened this issue Feb 10, 2020 · 45 comments
Labels
api-rest feature-request New feature or request p3

Comments

@lawmicha
Copy link

lawmicha commented Feb 10, 2020

Is your feature request related to a problem? Please describe.
Amplify CLI provides a way to create a REST api (API Gateway + lambda) with IAM authorization. in the JS docs, there are manual steps to add cognito user pool as the authorizator for the requests to API gateway: https://aws-amplify.github.io/docs/js/api#cognito-user-pools-authorization (however, this could be improved, see step 11 below). Amplify iOS (aws-amplify/amplify-swift#312) and Android also supports this API with user pools as the auth mechanism.

The pain point is here is that Amplify CLI doesn't support creating API Gateway + Cognito User Pool authorizator. I'm opening this issue here to provide more details around what needs to done to enable API Gateway with Cognito.

The following steps show how to set up an API endpoint with APIGateway and Lambda source. The auth configured will be Cognito User Pool.

  1. Initialize an amplify project. amplify init

  2. Create an API Gateway which proxies requests to an AWS Lambda with no authorization needed. amplify add api.

? Please select from one of the below mentioned services: `REST`
? Provide a friendly name for your resource to be used as a label for this category in the project: `restAPI`
? Provide a path (e.g., /items) `/items`
? Choose a Lambda source `Create a new Lambda function`
? Provide a friendly name for your resource to be used as a label for this category in the project: `restwithuserpoolinte22de6072`
? Provide the AWS Lambda function name: `restwithuserpoolinte22de6072`
? Choose the function template that you want to use: `Serverless express function (Integration with Amazon API Gateway)`
? Do you want to access other resources created in this project from your Lambda function? `No`
? Do you want to edit the local lambda function now? `No`
Succesfully added the Lambda function locally
? Restrict API access `No`
? Do you want to add another path? `No`
Successfully added resource apid7c040db locally
  1. Create Cognito User Pool. Run amplify add auth
Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections. 
 How do you want users to be able to sign in? Email
 Do you want to configure advanced settings? No, I am done.
Successfully added resource restwithuserpoolintea05fdd00 locally
  1. Provision the resources. Run amplify push to provision the API Gateway, Lambda, and the Cognito User Pool.

  2. In amplifyconfiguration.json. update authorizationType to AMAZON_COGNITO_USER_POOLS like so

{
    "UserAgent": "aws-amplify-cli/2.0",
    "Version": "1.0",
    "api": {
        "plugins": {
            "awsAPIPlugin": {
                "apid7c040db": {
                    "endpointType": "REST",
                    "endpoint": "https://endpoint.execute-api.us-west-2.amazonaws.com/devo",
                    "region": "us-west-2",
                    "authorizationType": "AMAZON_COGNITO_USER_POOLS"
                }
            }
        }
    }
}
  1. Find your API name. Run amplify console to open the AWS Console. The latest deployment activty logs will indicate the API Gateway that is provisioned. There will be a Resource ID that looks like <api name> (api). Navigate to API Gateway console, select your API.

  2. Find your Cognito User Pool name by click on the Authentication tab in the AWS Console.

  3. Add Cognito User Pool as an authorization mechanism. Select Authorizers, click on "+ Create New Authorizer",

  • type in a Name
  • select Cognito as the type
  • Select the Cognito UserPool
  • For Token Source, enter Authorization
  • Once completed, refresh the page.
  1. Enable requests to the API with the Cognito User Pool Authorizer as the authorization mechanism.
  • Select Resources on the left, Under Resources, and each individual resource path, select Any. You will see a Test section, Method Request, Method Response, Integration Request, etc
  • Click on Method Request, under Settings, Authorization, click on edit. In the drop down, select the User Pool authorizer, then click on the check mark to save it.
  • Repeat this for each of the resource paths
  • Click on Actions, deploy API, and select the deployment stage, and click Deploy.

There are some additional gotcha's that need to be noted here:

  • the configuration file will be overwritten on amplify push, so developer will need to programmatically instantiate Amplify.

Describe the solution you'd like
A clear and concise description of what you want to happen.
amplify add api

Restrict API Access? Yes
Cognito User Pool or IAM? Cognito User Pool
Auth access? [create|update|delete]
Unauth access? [create|update|delete]

Alternatives
JS docs could use some improvements with step number 11, for example it doesn't guide the develop to re-deploy the API. iOS and Android Amplify docs will be written simiarly.

Dedupped to
aws-amplify/amplify-cli#3058
#438 (Just realized this one is for API + API Key, not user pool)

@attilah
Copy link
Contributor

attilah commented Feb 11, 2020

@lawmicha thanks for opening the issue, I see the customer need here and this is something the CLI could support out of the box. I take this to the team for discussion and get back to you.

@alexladerman
Copy link

I agree this is really important. API+Lambda+Cognito did not work out of the box for me!

@hi120ki
Copy link

hi120ki commented Feb 13, 2020

I need this feature. Cognito User Pool Authorization provides us to powerful and useful permission management.

@harakiro
Copy link

harakiro commented Mar 26, 2020

I spent hours banging my head on this and went through multiple tutorials and digging through code. Thank you for such a great walk-thru and this needs to be added!

@xitanggg
Copy link

xitanggg commented Apr 3, 2020

Great walk through. Thanks a lot. Hope this automation is added soon. for such an important feature. It is essentially the same as AdminQueries API. It should be easy for the Amplify team to copy what has done over.

@smakinson
Copy link

@lawmicha I wondered if something may have changed or if perhaps a step is missing. I have tried a couple times, at first with the auth I already had set up and the removed all the api, functions & auth and tried again and it doesn't seem to generate amplifyconfiguration.json. Where do you see that file generated? I've also tried searching for plugins and authorizationType I apologize if I am just missing it some how.

I had previously tried manually adding the Cognito Authorizer in another project and realized when I ran push again for some changes the Authorizer got trashed, so I'm hoping this will solve that. Thanks for any info you can provide.

@lawmicha
Copy link
Author

lawmicha commented Apr 7, 2020

@lawmicha I wondered if something may have changed or if perhaps a step is missing. I have tried a couple times, at first with the auth I already had set up and the removed all the api, functions & auth and tried again and it doesn't seem to generate amplifyconfiguration.json. Where do you see that file generated? I've also tried searching for plugins and authorizationType I apologize if I am just missing it some how.

Hi @smakinson, amplifyconfiguration.json is used in iOS and Android projects. Sorry I should have clarified. Are you using Amplify CLI with your JS app? (amplify init -> which type of project). I believe JS should be aws_exports.json or something

I had previously tried manually adding the Cognito Authorizer in another project and realized when I ran push again for some changes the Authorizer got trashed, so I'm hoping this will solve that. Thanks for any info you can provide.

This sounds like expected behavior but also a problem if amplify push reverts any manual changes done through AWS Console. If confirmed, then is it supported to modify the cloud formation templates ourselves after using Amplify CLI that will not get overwritten after another amplify push?

@smakinson
Copy link

@lawmicha Thanks for your response, you are correct, it is a javascript project. Hoping to find a way around it losing the Authorizer within Amplify.

aws-exports.js shows:

// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

so it seems that is not the file I want. I'll keep digging.

@lawmicha
Copy link
Author

lawmicha commented Apr 7, 2020

@smakinson aws-exports.js will be reverted after amplify push. So maybe the APIGateway still has the authorizer set up but the configuration file in the app is reverted. this will be same issue with amplifyconfiguration.json. You can also programmatically configure Amplify.API without using aws-exports.js, although that will mean you need to transfer all the values over to code, and configure Amplify.Auth programmtically and any other categories you have as well

I just want to note that this is a feature request, and not necessarily steps for developers to follow and get working, they are steps provided to assist in turning this into an Amplify CLI feature. Following these steps are not supported by Amplify CLI. As we've seen the issue with the configuration file being overwritten.

@smakinson
Copy link

smakinson commented Apr 10, 2020

@lawmicha Incase this is helpful for others I thought I'd post up here what I am looking at doing with the cloudformation template, and if you have a moment I have 2 questions, one general and the other (without taking too much of your time) if you might know the fix for an error:

  • Do you know at what point backend-config.json and the myApi-cloudformation-template.json gets overwritten and if I will lose any manual changes there? Kinda making this still risky if changes are easily lost...so is this even a good idea?

  • I have it working if I hardcode my POOL ARN in the last bit of this and I'm trying to change now to get it based on the current environment based on https://aws-amplify.github.io/docs/cli-toolchain/quickstart#custom-cloudformation-stacks Since I'm editing an existing resource I think a few steps aren't relevant, but I have modified backend-config.jsonby adding an additional item to the dependsOnarray for the REST api, added:

"myApi": {
  "service": "API Gateway",
    "providerPlugin": "awscloudformation",
    "dependsOn": [
    {
      "category": "function",
      "resourceName": "myFunc",
      "attributes": [
        "Name",
        "Arn"
      ]
    },
    {
      "category": "auth",
      "resourceName": "myAuthResource",     // from amplify status
      "attributes": [
        "UserPoolId"
      ]
    }
  ]
}

in the Parameters section of the myApi-cloudformation-template.json added:

"authmyAuthResourceUserPoolId": { // The format out here is `<category><resource-name><attribute-name>
  "Type": "String"
}

the methods have the authorizer attached by adding the following under x-amazon-apigateway-any-method

"security": [
  {
    "CognitoAuthorizer": []
  }
],

and further down in the file I'm trying to reference it like:

"securityDefinitions": {
  "CognitoAuthorizer": {
    "type": "apiKey",
      "name": "Authorization",
      "in": "header",
      "x-amazon-apigateway-authtype": "cognito_user_pools",
      "x-amazon-apigateway-authorizer": {
      "providerARNs": [
        {
          "authmyAuthResourceUserPoolId": {   // The format out here is `<category><resource-name><attribute-name>

            "Fn::GetAtt": [
              "authmyAuthResource",   // I've tried this with and without the auth category prefix
              "Outputs.UserPoolId"
            ]
          }
        }
      ],
        "type": "cognito_user_pools"
    }
  }
}

Next I ran amplify env checkout CURRENT_ENV and then amplify push

The error in question is the one I see when I run amplify-push:

Template error: instance of Fn::GetAtt references undefined resource authmyAuthResource

Maybe I'm just mistyping something, hopefully this can be helpful for anyone in a javascript amplify project until the cli supports it. Thanks for any insight.

@lawmicha
Copy link
Author

Thanks @smakinson for posting your details. I'm not very familar with those files you are asking about in your questions. + @SwaySway et al to help answer

@SwaySway
Copy link
Contributor

Hello @smakinson,
Your API specific changes in backend-config.js and myApi-cloudformation-template.json are changed when you run amplify update API.

I have it working if I hardcode my POOL ARN in the last bit of this and I'm trying to change now to get it based on the current environment based on https://aws-amplify.github.io/docs/cli-toolchain/quickstart#custom-cloudformation-stacks

The docs here specify a way to get the user pool id into another stack. What you are looking for is the UserPoolArn. For that you'll need to import will be different, I've detailed this below.

You'll need to use a different ref for the ARN such as "Ref": "authMyAuthResourceNameUserPoolArn" and add that as a parameter in the template.

        "authMyAuthResourceNameUserPoolArn": {
          "Type": "String"
        }

In parameters.json you'll need to point to the auth stack to reference the UserPoolArn

    "authMyAuthResourceNameUserPoolArn": {
        "Fn::GetAtt": [
            "MyAuthResourceName",
            "Outputs.UserPoolArn",
            "Arn"
        ]
    }

Currently arn is not included in the outputs of the auth stack, this is something we can consider as a part of this feature request. For this you'll want to export the Arn as well from the auth stack.

  UserPoolArn:
    Value: !GetAtt UserPool.Arn
    Description: Arn for cognito userpool 

The last thing here to edit would be to change the backend-config.json to denote that the api resource depends on the auth stack.

	"api": {
		"demorest": {
			"service": "API Gateway",
			"providerPlugin": "awscloudformation",
			"dependsOn": [
				{
					"category": "function",
					"resourceName": "demoRestFN",
					"attributes": [
						"Name",
						"Arn"
					]
				},
				{
					"category": "auth",
					"resourceName": "MyAuthResourceName",
					"attributes": [
						"UserPoolArn"
					]
				}
			]
		}
	}

All that's left here is to run amplify env checkout <current-env-name> and amplify push.

@smakinson
Copy link

@SwaySway Thank you for your help. I currently have manually setup an api for this, do you think its worth (risky?) going this route or would it be better to wait on the feature request and change it then?

@nibab
Copy link

nibab commented Apr 28, 2020

This is a great workaround for now for creating Cognito Authorizers. Perhaps in a similiar vein, Is it safe to edit lambdaFunction-cloudformation-template.json? When does it get overwritten ? If I needed to add custom resources (ie a lambda policy), is the recommended approach to modify this cfn template ? Im looking for an analogy to the CustomResources.json template in the api/stacks folder.

@jwoehrle
Copy link

I'm trying to accomplish the same thing (calling API GW with Cognito Auth) and made it work with above method.

However: my call only succeeds if I use an ID Token to authenticate:

let init = {
            headers: { Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}` }
        }
API.get('xxx', '/items', myInit).then( result => {
      console.log(result);
    })

It's not working with an access token:

let init = {
            headers: { Authorization: `Bearer ${(await Auth.currentSession()).getAccessToken().getJwtToken()}` }
        }
API.get('xxx', '/items', myInit).then( result => {
      console.log(result);
    })

Fails with 401 and body { "message" : "unauthorized"}

I'd expect an access token to work instead of an id token. Is there anything I'm doing wrong?

@ahansson89
Copy link

What's the latest status on this?

Been scratching my head for hours trying to figure out why this was not working out of the box. The latest CLI makes you go through steps to set it all up, but does not build a CloudFormation template that actually works.

Again, it is blowing my mind how often I have to fix this stuff myself in vs having the Amplify CLI do what it is supposed to do. Does the Amplify product team not have any QA engineers?

@jfhidakatsu
Copy link

jfhidakatsu commented Sep 3, 2020

I got this working using @lawmicha 's steps and @jwoehrle 's code. To my understanding, I'm going to have to follow these steps every time we deploy (though I haven't tested this part yet). Is there any other workaround that will work automatically?

Edit: It is working well, but does require this configuration to be set every time the API is updated from the amplify CLI.

@aws-amplify aws-amplify deleted a comment from sapher Oct 19, 2020
@AtomicSimon
Copy link

Any news on getting this implemented? Overriding the amplify config manually is a time-consuming process for a feature that should already be there.

@sapher
Copy link

sapher commented Oct 20, 2020

I'm trying to accomplish the same thing (calling API GW with Cognito Auth) and made it work with above method.

However: my call only succeeds if I use an ID Token to authenticate:

let init = {
            headers: { Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}` }
        }
API.get('xxx', '/items', myInit).then( result => {
      console.log(result);
    })

It's not working with an access token:

let init = {
            headers: { Authorization: `Bearer ${(await Auth.currentSession()).getAccessToken().getJwtToken()}` }
        }
API.get('xxx', '/items', myInit).then( result => {
      console.log(result);
    })

Fails with 401 and body { "message" : "unauthorized"}

I'd expect an access token to work instead of an id token. Is there anything I'm doing wrong?

Here's from the documentation.

With the COGNITO_USER_POOLS authorizer, if the OAuth Scopes option isn't specified, API Gateway treats the supplied token as an identity token and verifies the claimed identity against the one from the user pool. Otherwise, API Gateway treats the supplied token as an access token and verifies the access scopes that are claimed in the token against the authorization scopes declared on the method.

My last comment got deleted, pretty good Team.

@mansdahlstrom1
Copy link

Is this being worked on? This seems like a very important feature and i was surprised when it did not work out of the box. Have been trying to solve an issue for some hours now and stumbled upon this now

@FlaegelEmbed
Copy link

This would be a very useful feature! For now, we have to set the authorizer for all resources after every "amplify update api", because amplify overwrites the template file : (

@ataravati
Copy link

Setting this up was a bit unclear for me, but eventually I got it through a combination of @brymartinez and @SwaySway post's. Thanks for that!

This consolidates those posts into one to make it easier for future dwellers to implement this functionality.

The following will apply to the JavaScript Amplify library.

STEP 1

Set up the appropriate parameters coming into your REST API's CloudFormation (CF) template. This is found in: ./amplify/backend/api/<the name of your api>/parameters.json.

From my guess this file acts as a way for the backend resources to pass values to each other just as long as they have been set in the resources CF template Outputs key.

Add the following key/value after whatever key/values are there already or within the empty object. For me this was an empty file, but for you it could have preexisting values. You'll find name for auth in ./amplify/backend/backend-config.json under the "auth" key.

You'll have to check the ./amplify/backend/auth/<the name of your auth>/<name of your auth>-cloudformation-template.yml to see if UserPoolId is being outputted. I'd be surprised if it wasn't.

AuthCognitoUserPoolId is a descriptive name I gave it. This will be used in the next step as reference to the UserPoolId.

  "AuthCognitoUserPoolId": {
    "Fn::GetAtt": ["auth<name for auth>", "Outputs.UserPoolId"]
  }

STEP 2

Set up the appropriate Parameters, securityDefinitions, security, and parameter values in your REST API's CF template. This is found in ./amplify/backend/api/<the name of your api>/<the name of your api>-cloudformation-template.json.

In the "Parameters" key add the following key/value:

"AuthCognitoUserPoolId": {
      "Type": "String",
      "Description": "The id of an existing User Pool to connect. If this is changed, a user pool will not be created for you.",
      "Default": "NONE"
    },

In "Resources" > "<your api name>" > "Properties" > "Body" add the following key/value. This is creating the authorizer and the AuthCognitoUserPoolId parameter is being used her to reference the Cognito user pool you want to use.

I originally thought AuthCognitoUserPoolId was the literal user pool id value, but I got an error when I added that as the key for the parameter.

The "Cognito" key is a custom name and you can name it anything.

"securityDefinitions": {
            "Cognito": {
              "type": "apiKey",
              "name": "Authorization",
              "in": "header",
              "x-amazon-apigateway-authtype": "cognito_user_pools",
              "x-amazon-apigateway-authorizer": {
                "type": "cognito_user_pools",
                "providerARNs": [
                  {
                    "Fn::Join": [
                      "",
                      [
                        "arn:aws:cognito-idp:",
                        {
                          "Ref": "AWS::Region"
                        },
                        ":",
                        {
                          "Ref": "AWS::AccountId"
                        },
                        ":userpool/",
                        {
                          "Ref": "AuthCognitoUserPoolId"
                        }
                      ]
                    ]
                  }
                ]
              }
            }
          },

In "Resources" > "<your api name>" > "Properties" > "Body" > "paths" > "<your path/s name>" > "x-amazon-apigateway-any-method" > "parameters" add the following key/value to the array. This is defining the Authorization header as a parameter that has the token.

This has to be added to all of the methods to which you want to apply this authorization. It will be under the appropriate http method key such as "get", "post", etc. I didn't apply it to "options" because it's not needed.

{
                    "name": "Authorization",
                    "in": "header",
                    "required": false,
                    "type": "string"
                  }

In "Resources" > "<your api name>" > "Properties" > "Body" > "paths" > "<your path/s name>" > "x-amazon-apigateway-any-method" > "security" add the following key/value. Like the parameter above it needs to be added to any methods you want this authorization to apply. This has to be the same as the name you gave the key in the "securityDefinitions" object.

This will select this authorizor for the method. If it's not added when you go into the API Gateway console and select the method you'll see NONE in the Auth setting even though the Cognito authorizer has been setup and is an option in the dropdown menu.

For anything other than OAuth2 type security scheme this must be empty.

"security": [
                  {
                    "Cognito": []
                  }
                ],

That's it for the implementation of the infrastructure. Run amplify push to set it all up.

From what I can tell the configuration setup doesn't get overridden with updates. But I've not thoroughly tested it.

For usage, this is what I'm doing:

import { API, Auth } from 'aws-amplify'

....

 const apiName = '<your api name>'
      const path = '/<your api path>'
      const myInit = {
        body: {
          test: 'test'
        },
        headers: {
          Authorization: `Bearer ${(await Auth.currentSession())
            .getIdToken()
            .getJwtToken()}`
        },
        response: true,
        queryStringParameters: {}
      }
      const finaliseImportResp = await API.post(apiName, path, myInit)

For reference here's some AWS documentation:

@johnEthicalTechnology Thank you so much for this. Could you also explain how to restrict the api access?

@evankirkiles
Copy link

The above solution no longer works due to some changes in how cloud formation templates are generated / overridden in AWS Amplify (which is now done with CDK instead of directly modifying the cloud formation template). I threw together a gist for an override.ts file which should be put in your API folder instead as an update to the wonderful solution put together by @johnEthicalTechnology:

https://gist.github.com/evankirkiles/9de81f026a2e2c961a2b6a3d80d35519

Feel free to recommend changes!

@blbigelow
Copy link

The above solution no longer works due to some changes in how cloud formation templates are generated / overridden in AWS Amplify (which is now done with CDK instead of directly modifying the cloud formation template). I threw together a gist for an override.ts file which should be put in your API folder instead as an update to the wonderful solution put together by @johnEthicalTechnology:

https://gist.github.com/evankirkiles/9de81f026a2e2c961a2b6a3d80d35519

Feel free to recommend changes!

@evankirkiles Thanks for sharing! Your gist worked great for me.

@sshvaiko
Copy link

sshvaiko commented Jun 22, 2022

Hello, I've used this manual to configure Cognito User Pool authorizer, but during the deployment, I'm getting this error:

Resource Name: 1rfy2sfng2 (AWS::ApiGateway::RestApi)
Event Type: update
Reason: Errors found during import:
Unable to create authorizer 'Cognito': ProviderARNs need to be valid Cognito Userpools. Invalid ARNs-
arn:aws:cognito-idp:{AWS::Region}:{AWS::AccountId}:userpool/{AuthCognitoUserPoolId}
Unable to put method 'x-amazon-apigateway-any-method' on resource at path '/invite/verify': Invalid authorizer ID specified. Setting the authorization type to CUSTOM or COGNITO_USER_POOLS requires a valid authorizer.
Unable to put method 'x-amazon-apigateway-any-method' on resource at path '/invite/verify/{proxy+}': Invalid authorizer ID specified. Setting the authorization type to CUSTOM or COGNITO_USER_POOLS requires a valid authorizer. (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 0ef6e1aa-3d3f-4d45-990b-e41b21ad64f8; Proxy: null)

I did not forget to replace your auth name here :) Any thoughts?

And one more question, we can make the function private from CLI. Then, there is information about the user in event.requestContext.identity. Why not use it?

@shkomg
Copy link

shkomg commented Jul 11, 2022

shit, every time I add new API path I need to recreate the Authorizer & redeploy the api

kinda a pain when quickly prototyping project

:(

@rondondaniel
Copy link

rondondaniel commented Jul 12, 2022

Hi @shkomg I'm experiencing exactly the same problem. If override I get the same errors @sshvaiko had.

UPDATE:
ARNs- arn:aws:cognito-idp:{AWS::Region}:{AWS::AccountId}:userpool/{AuthCognitoUserPoolId} doesn't update variables. So you should manually modify the values: AWS::Region, AWS::AccountId; AuthCognitoUserPoolId. 😄 😄 😄

It effectively works using Amplify-CLI 9.1.0 but in my opinion could lead to security issues. Workarounds could be to modify the script or to add a env. var. for example.

@shkomg
Copy link

shkomg commented Jul 13, 2022

Hi @rondondaniel I definitely expect it working out of the box with default CLI prompts and waiting for Amplify team to resolve this. Meanwhile for me updating over the API Gateway once in while isn't perfect but Ok. It just simpler to remember VS changes in code, at least for me :)

@rafhuys-klarrio
Copy link

@sshvaiko @rondondaniel no need to manually update the values. That's the whole idea of the script, right :|
I believe the documentation is just wrong and you should put $ before the {...} substitutions, like { "Fn::Sub": "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${AuthCognitoUserPoolId}" }

@Duder-onomy
Copy link

I had the same issue as @shkomg and @rondondaniel.
@rafhuys-klarrio fix (adding the $'s ) worked for me.

@tzutalin
Copy link

tzutalin commented Feb 13, 2023

It is pretty bad that Amplify does not support this out of box

In a word, there are 3 steps to achieve this.
Ref: https://docs.amplify.aws/cli/restapi/override/#add-a-cognito-user-pool-authorizer-to-your-rest-api

Step1:
$ amplify override api

Step2:
edit amplify/backend/api/yourProject/override.ts

export function override(resources: AmplifyApiRestResourceStackTemplate) {

    // Add a parameter to your Cloud Formation Template for the User Pool's ID. The name of resource is the name of the folder in amplify/backend/auth
    resources.addCfnParameter({
        type: "String",
        description: "The id of an existing User Pool to connect. If this is changed, a user pool will not be created for you.",
        default: "NONE",
      },
      "AuthCognitoUserPoolId",
      { "Fn::GetAtt": ["auth<YourAuthSetting>", "Outputs.UserPoolId"], }
    ); // YourAuthSetting can be found `ls amplify/backend/auth`

    // Create the authorizer using the AuthCognitoUserPoolId parameter defined above
    resources.restApi.addPropertyOverride("Body.securityDefinitions", {
      Cognito: {
        type: "apiKey",
        name: "Authorization",
        in: "header",
        "x-amazon-apigateway-authtype": "cognito_user_pools",
        "x-amazon-apigateway-authorizer": {
          type: "cognito_user_pools",
          providerARNs: [
            { 'Fn::Sub': 'arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${AuthCognitoUserPoolId}' },
          ],
        },
      },
    });

    // For every path in your REST API
    for (const path in resources.restApi.body.paths) {
      if (path.startsWith('/somepath')) {
         console.log('Skip the path not to use Authorization header', path);
         continue;
      }
       console.log('Make the path for restapi to have Authorization', path);
      // Add the Authorization header as a parameter to requests
      resources.restApi.addPropertyOverride(
        `Body.paths.${path}.x-amazon-apigateway-any-method.parameters`,
        [
          ...resources.restApi.body.paths[path]["x-amazon-apigateway-any-method"]
            .parameters,
          {
            name: "Authorization",
            in: "header",
            required: false,
            type: "string",
          },
        ]
      );
      // Use your new Cognito User Pool authorizer for security
      resources.restApi.addPropertyOverride(
        `Body.paths.${path}.x-amazon-apigateway-any-method.security`,
        [ { Cognito: [], }, ]
      );
    }
}

Step3: Update config applying the above CloudFormation

amplify push

@orome
Copy link

orome commented Mar 6, 2023

Again, it is blowing my mind how often I have to fix this stuff myself in vs having the Amplify CLI do what it is supposed to do. Does the Amplify product team not have any QA engineers?

I have the same experience and the same frustrations. I think it has less to do with the product team than what I expect is a disconnect between how executives have positioned Amplify, and what the team is actually resourced and directed to do. Mismanagement in other words.

@mburakergenc
Copy link

It has been 3 years since this feature was requested and no updates yet. It is very disappointing to see the Amplify team not taking any action on this.

@orome
Copy link

orome commented Aug 2, 2023

It has been 3 years since this feature was requested and no updates yet. It is very disappointing to see the Amplify team not taking any action on this.

As near as I can tell Amplify is dead. The whole thing is riddled with fatal bugs that eventually crop up and wreck you project. I regret ever using it.

@Blackmesa-Canteen
Copy link

Still not fixed.

@mikeswain
Copy link

OMG just came to add a simplay lambda with Cognito auth and see the hoops you've got to jump through. What a pile of junk

@orome
Copy link

orome commented Feb 19, 2024

OMG just came to add a simplay lambda with Cognito auth and see the hoops you've got to jump through. What a pile of junk

Nothing in my software experience has wasted more of my time than Amplify. Use Heroku.

@bubai2000
Copy link

I can suggest an easier solution for NodeJS, you can use this library https://github.com/awslabs/aws-jwt-verify in the lambda function itself.
Initializer:
const verifier = CognitoJwtVerifier.create({
userPoolId: process.env.COGNITO_USER_POOL_ID,
tokenUse: "id",
clientId: process.env.COGNITO_CLIENT_ID,
});
Verifier:
try {
await verifier.verify(event.headers.Authorization)
} catch {
return {
statusCode: 401,
body: "Unaothorized!",
};
}

@MyNameIsTakenOMG
Copy link

Hi guys, for who are still looking for solutions of adding authorizers, here's the link I found in the amplify doc, which shows how to add cognito user pool authorizer or custom lambda authorizer : https://docs.amplify.aws/javascript/build-a-backend/restapi/override-api-gateway/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-rest feature-request New feature or request p3
Projects
None yet
Development

No branches or pull requests