AWS supports user management and authentication with Cognito. You can save user information in Cognito securely.
To focus on Cognito itself, this tutorial will not include express
, express-session
and passport
. Also, we are doing server side authentication instead of client side.
Imagine we have a page to ask user to input email address and password to sign up. No email verification is required.
After user signed up, they can login with the same credential. We don’t ask them to change password on 1st login (because the password is provided by them).
Setup Cognito
First we need to create a User Pool which stores our users. We will name it MyUserPool
.
export USER_POOL_ID=$(aws cognito-idp create-user-pool \
--pool-name MyUserPool \
--query UserPool.Id \
--output text)
Next, we need to create a client which can access our User pool. We will name it MyUserPoolClient
. We also specify explicit-auth-flows
as ADMIN_NO_SRP_AUTH
, so we can simply provide username and password to do authentication.
export CLIENT_ID=$(aws cognito-idp create-user-pool-client \
--user-pool-id $USER_POOL_ID \
--client-name MyUserPoolClient \
--explicit-auth-flows ADMIN_NO_SRP_AUTH \
--query UserPoolClient.ClientId \
--output text)
Setup IAM
We need to create an IAM user for our server to access Cognito. We name this user as MyCognitoIAMUser
and attach managed policy AmazonCognitoPowerUser
to it. You should create IAM role instead of IAM user if you run on AWS EC2 or Lambda.
IAM_USER_NAME=MyCognitoIAMUser
aws iam create-user --user-name $IAM_USER_NAME
aws iam attach-user-policy \
--user-name $IAM_USER_NAME \
--policy-arn arn:aws:iam::aws:policy/AmazonCognitoPowerUser
CREDENTIALS=$(aws iam create-access-key --user-name $IAM_USER_NAME \
--query 'AccessKey.[AccessKeyId,SecretAccessKey]' \
--output text)
export ACCESS_KEY_ID=$(echo $CREDENTIALS | awk '{print $1}')
export SECRET_ACCESS_KEY=$(echo $CREDENTIALS | awk '{print $2}')
Setup AWS client
We need to install aws-sdk
to call Cognito API.
npm i aws-sdk
So now we can start coding. First we need to setup our AWS client first.
const AWS = require('aws-sdk')
// You don't need it if you are running on EC2 or Lambda
if (process.env.NODE_ENV === 'development') {
AWS.config.update({
region: process.env['AWS_REGION'],
accessKeyId: process.env['ACCESS_KEY_ID'],
secretAccessKey: process.env['SECRET_ACCESS_KEY']
})
}
Sign up
We first create our signUp
function first.
async function signUp(email, password) {
try {
const cognito = new AWS.CognitoIdentityServiceProvider()
await cognito.adminCreateUser({
UserPoolId: process.env.USER_POOL_ID,
Username: email,
MessageAction: 'SUPPRESS',
TemporaryPassword: password,
}).promise()
} catch (err) {
throw err
}
}
As you can see, the password is passed as a temporary password. It means user needs to change on 1st login. But we don’t want to do this because the password is already provided by the user. We need to programmatically to change the password for them.
This is how our final version of signUp
function looks like.
async function signUp(email, password) {
try {
const cognito = new AWS.CognitoIdentityServiceProvider()
await cognito.adminCreateUser({
UserPoolId: process.env.USER_POOL_ID,
Username: email,
MessageAction: 'SUPPRESS',
TemporaryPassword: password,
}).promise()
const initAuthResponse = await cognito.adminInitiateAuth({
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: process.env.CLIENT_ID,
UserPoolId: process.env.USER_POOL_ID,
AuthParameters: {
USERNAME: email,
PASSWORD: password
}
}).promise()
if (initAuthResponse.ChallengeName === 'NEW_PASSWORD_REQUIRED') {
await cognito.adminRespondToAuthChallenge({
ChallengeName: 'NEW_PASSWORD_REQUIRED',
ClientId: process.env.CLIENT_ID,
UserPoolId: process.env.USER_POOL_ID,
ChallengeResponses: {
USERNAME: email,
NEW_PASSWORD: password,
},
Session: initAuthResponse.Session
}).promise()
}
} catch (err) {
throw err
}
}
Login
Login is much simpler. Our function will return the response from AWS. You can get the access token, ID token, refresh token from the response.
async function login(email, password) {
try {
const cognito = new AWS.CognitoIdentityServiceProvider()
return await cognito.adminInitiateAuth({
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: process.env.CLIENT_ID,
UserPoolId: process.env.USER_POOL_ID,
AuthParameters: {
USERNAME: email,
PASSWORD: password
}
}).promise()
} catch (err) {
throw err
}
}
You can check the schema of the response in here.
{
"AuthenticationResult": {
"AccessToken": "string",
"ExpiresIn": number,
"IdToken": "string",
"NewDeviceMetadata": {
"DeviceGroupKey": "string",
"DeviceKey": "string"
},
"RefreshToken": "string",
"TokenType": "string"
},
"ChallengeName": "string",
"ChallengeParameters": {
"string" : "string"
},
"Session": "string"
}
Top comments (2)
Thanks for you post Frankz, just one question, How do you validate if the user you are going to create already exists due if you don't check it previously, the creation will fail saying "User account already exists"?
You can only know a user exists when you get the AliasExistsException.
Perhaps this link can help you.
github.com/aws-amplify/amplify-js/...