Phan C么ng Th岷痭g
Posted on September 11, 2021
I'm standing in domain A, I'd like to call an API that was hosted in domain B. Normally, I can't do it without setting CORS for the API. In this post, I'm going to do it using API Gateway and AWS Cognito.
Basic concept
API Gateway
I assume I already have had many resources(API), but I didn't set up the access permission yet. In this case, API Gateway is so helpful, I can config the middleware for whatever API I would like to control.
Apart from authorizer, API Gateway also helps us for controlling the resources(API), connecting with other AWS services.
The reason I use API Gateway is that It's so easy to config middleware between API Gateway and AWS Cognito.
AWS Cognito
AWS Cognito is a service that helps us for building authentication. We can sign up user, sign in, logout, etc...
If I sign in successfully, I can get 3 types of tokens.
- access token: the token of my account.
- id token: use to access other AWS services. I will use it to verify my API Gateway.
- refresh token: use to re-new session.
Lambda function
In this demo, I'm going to use lambda to fake responses in API. You can imagine it's a function that we can make the response for our API.
Implementation
I will have 2 patterns with CORS.
- Access the outside API without credentials.
- Access the outside API by sending credentials from the client side.
This is my logic.
Without credentials
Credentials
Setup
Nextjs
Create a new project:
npx create-next-app --typescript
I give it the name dev-cors
.
AWS
I assume you already had an IAM account in order to use AWS services.
Lambda function
I will access to AWS Lambda console environment, and create a new function dev-cors-function
.
Edit the response in order to make it looks like a realistic API:
const response = {
statusCode: 200,
body: {
userId: 1,
id: 1,
title: "delectus aut autem",
completed: false
},
};
And Hit Deploy
in order to finish our set up.
AWS Cognito
I'm going to the AWS Cognito console environment, and create new user pool with name dev-cors-cognito
.
Hit Add app client...
I need to create app client in order to make request sign up
, sign in
, etc...
Give it a name dev-cors-app-client
.
The important thing here is that I need to uncheck Generate client secret
, if I don't do this, I'm not able to make a request successful(it was an error of AWS Cognito).
Hit Create pool
and I finished creating a user pool for our app.
API Gateway
I'm going to API Gateway console, and create new API for our demo.
Hit button Build
in the REST API
column.
And give it a name like dev-cors-api
, then click Create API
.
Now I have had an API, let's make two resources for our app.
- without-credentials
- credentials
Note: we need to check button
Enable API Gateway CORS
in order to make use our API will be enabled CORS.
In without-credentials
, let's create a method GET
, and connect to the lambda function that I just created in the previous step.
In order to make a request successful from the client-side, I need to config Access-Control-Allow-Origin
in my API.
In GET
method, hit Integration Response
-> Method Response
.
Hit Add Header
, and add Access-Control-Allow-Origin
.
Now, I go back to Integration Response
, I'm able to see
Access-Control-Allow-Origin
. But I have to change the value to '*'
.
With credentials
I do the same way with without-credentials
, create a method GET
, but I will create an authorizer for the resource.
Hit Authorizers
tab, and select the user pool
that I just created in the previous step.
Give it the name dev-cors-authorizer
, and fill out Authorization
in Token Source *
input. I will need to send Authorization: token
from the client in order to verify.
Ok cool! Now, I come back to credentials
resource, and hit GET
-> Method Request
and select dev-cors-authorizer
for Authorization
. If it wasn't showed, you maybe need to reload your page.
Finally, I need to deploy my API. Let's do it:
Coding
Let's create a file .env
, and set up a variable point to our API Gateway:
NEXT_PUBLIC_API_GATEWAY='https://i83ybr0e1d.execute-api.us-west-2.amazonaws.com/dev'
Without Credentials
Let's create a page in nextjs without-credentials.tsx
import * as React from 'react'
function WithoutCredentials() {
React.useEffect(() => {
async function fetchData() {
const data = await fetch(
`${process.env.NEXT_PUBLIC_API_GATEWAY}/without-credentials`,
)
const resJson = await data.json()
// do some stuff in here.
}
fetchData()
}, [])
return (
<div>
<h1>Without Credentials</h1>
</div>
)
}
export default WithoutCredentials
Hit Network
tab and take a glance at the response:
You can try to remove Access-Control-Allow-Origin
in API Gateway, and you will see the CORS error.
Credentials
This is the way that I'm going to do:
- Sign up an account to AWS Cognito.
- Sign in with the account from step one.
- Get id token after signed in.
- Use id token to call to API Gateway.
Let's do it step by step!
First of all, I need to install amplify
in order to interact with AWS Cognito:
npm install aws-amplify
In order to connect to AWS Cognito, I also need to config some property in amplify
:
Amplify.configure({
Auth: {
region: 'us-west-2.',
userPoolId: 'us-west-2_ltdxXTVoV',
userPoolWebClientId: '7n2162uuqkck3jpa4cjv7fnh69',
},
})
And the code for signing up an account to AWS Cognito:
const username = 'Username'
const password = 'Password@1
async function signUp() {
try {
const {user} = await Auth.signUp({
username,
password,
attributes: {
email: 'email@gmail.com',
},
})
console.log(user)
} catch (error) {
console.log('error signing up:', error)
}
}
}
<button onClick={signUp}>Sign Up</button>
After signing up, I have to sign in with the username
, password
that I just signed up for. But before I do this, I need to go to the AWS Cognito in order to confirm the user.
const [idToken, setIdToken] = React.useState('')
async function signIn() {
try {
const user = await Auth.signIn(username, password)
console.log('user', user)
setIdToken(user.signInUserSession.idToken.jwtToken)
} catch (error) {
console.log('error signing in', error)
}
}
Now, I have had the idToken
that allows me access to API Gateway's resources.
Let's try a request without idToken
:
async function callAPIGateway() {
try {
const data = await fetch(
`${process.env.NEXT_PUBLIC_API_GATEWAY}/credentials`
)
} catch (error) {
console.log('error calling API gateway', error)
}
}
I got an CORS error:
Let's try to add the idToken
I have just received from signing in.
async function callAPIGateway() {
try {
const data = await fetch(
`${process.env.NEXT_PUBLIC_API_GATEWAY}/credentials`,
{
headers: {
Authorization: idToken,
},
},
)
} catch (error) {
console.log('error calling API gateway', error)
}
}
No! It doesn't work! Hmm, When I send a request to API that is a different domain and with credentials(Authorization), I have to specific
Access-Control-Allow-Origin
in the API. It means I have to change '*' -> 'http://localhost:3000'
Let's change it in API Gateway console.
Then re-call the API Gateway.
It worked!
Conclusion
We are done with calling API from other domains and have a basic overview about CORS, API Gateway, AWS Cognito.
I hope it was helpful for you. Please feel free to refer to the source code.
Posted on September 11, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.