I’ve created an AWS Amplify function with amplify add function
resulting in the following basic configuration:
General information
- Name: MyFunction
- Runtime: python
Resource access permission
- Not configured
Scheduled recurring invocation
- Not configured
Lambda layers
- Not configured
Environment variables:
- Not configured
Secrets configuration
- Not configured
I then added a REST API using amplify add api
that uses this function, and added a path with "create" and "read" access for authenticated users resulting in the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:us-west-2:...:.../staging/POST/reply/*/*",
"arn:aws:execute-api:us-west-2:...:.../staging/POST/reply/*",
"arn:aws:execute-api:us-west-2:...:.../staging/GET/reply/*/*",
"arn:aws:execute-api:us-west-2:...:.../staging/GET/reply/*" ],
"Effect": "Allow"
}
]
}
But when I invoke the API from my app, with a logged in authenticated user (who has no trouble using my GraphQL API via DataStore
) I get a 403 error.
I can’t figure out what’s happening here. What would cause a 403 error in this case? This is all pretty much out of the box from the Amplify CLI. What’s wrong with the authentication I’m providing?
The code for the Lambda function (generated by the CLI, with no further edits) is:
def handler(event, context):
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
},
'body': json.dumps('Hello from your new Amplify Python lambda!')
}
The invoking code (copied from the Amplify documentation) is:
import ... { API } from 'aws-amplify'
// ...
const callLambdaFunction = async () => {
try {
const response = await API.post( 'Chat', '/reply/whatever', {
body: { data },
headers: {
Authorization: `Bearer ${ (await Auth.currentSession())
.getIdToken()
.getJwtToken() }`,
},
} )
setResult( response )
}
catch ( error ) {
console.log( error )
}
}
Some notes:
- Why do I even need to provide authentication? Doesn’t the API.post already know about the currently authenticated user and append the necessary headers? DataStore does.
- What do "read", "create", etc. mean in the
amplify api
CLI? How do the relate to what the endpoint does or is, or who can access it. Is it a secret code for "GET", "POST", etc.? - I’ve tried pasting the JWT I get from
Auth.currentSession
into Postman but get nonsense:{ "message": "'eyJhbG...0HMs' not a valid key=value pair (missing equal-sign) in Authorization header: 'Bearer eyJhbG...0HMs'." }
even if I just paste random text.
2
Answers
Amplify REST APIs do not use Cognito authorization by default. In order to use Cognito authorization you have two options:
Create a Cognito Authorizer in API Gateway
This is clearly a terrible way to proceed, since it takes place outside of Amplify and will have unpredictable results downstream. There is however a slightly better approach that's a bit more Amplify-ish.
Override the REST API for use with Cognito user pools
First execute
to create a stub
overrides.ts
file underamplify/backend/api/<resource-name>/
(if one is not already present) then add the following inside of the stuboverride(...)
function inoverrides.ts
:where
<your auth name here>
is the name of the folder inamplify/backend/auth
.This is not a whole lot better than the API Gateway option (that's a lot a mysterious code), but at least it's managed my Amplify, and has the advantage of providing the authentication headers for you (no need for the code in the OP or step 4 above).
(Note that you may get errors when you push after making these changes, but you can safely ignore those.)
All of this begs the question of why Amplify doesn't support Cognito for REST out of the box (e.g. as a choice in
amplify update api
) since that is almost certainly the most common scenario.Looks like you need to send up the authorization header.
This Amplify API Docs page has the details. You can have it injected by adding a function into the app’s config or per API call. I’m not sure why you need to add the auth header yourself and I’ve seen tutorials where it’s not used and protected calls seem to work. It may be you need to to add the
<Authenticator.Provider>
HOC around your app inmain.tsx
.Example from doc:
EDIT:
Although I’d expect a 401 or 403, not a 502. But you are missing Auth, so I think the above will solve the problem.
Edit 2
Since you’re seeing 502 and not 403, the issue could be you’re not calling your endpoint properly. Check the first two parameters to API.put(). Perhaps follow this tutorial, when you have REST API working, alter to fit your needs.
Edit 3
Great progress. Put your token into jwt.io to test it. Try using the Access Token instead of IdToken.