Applicable when
- AWS Cognito User Pool is used as a mechanism for authenticating your lambda invocation and you would like to access attributes associated with the user in your lambda.
Implementation
Getting the necessary parameters for authentication
In order to authenticate your user from lambda, you must ensure that certain parameters are available inside your lambda. You need username
, password
, userPoolId
and clientId
. You can add the userPoolId
and the clientId
as part of the env variables to your lambda from your backend stack. For example,
// scope refers to the stack where the cognito user pool resource is created
identityProviderLambda.addEnvironment('USER_POOL_ID', scope.userPool.userPoolId);
identityProviderLambda.addEnvironment('USER_POOL_CLIENT_ID', scope.userPoolClient.userPoolClientId);
username
and password
can be passed into the lambda via the API gateway that invokes your lambda as part of the pathParameters
or headers
in your request. You can access them in your lambda as follows:
import { APIGatewayProxyEvent, APIGatewayProxyResult} from 'aws-lambda';
import { authenticateCustomer } from '../shared/service/auth-service';
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
const userPoolId = process.env.USER_POOL_ID;
const clientId = process.env.USER_POOL_CLIENT_ID;
const username = decodeURIComponent(event.pathParameters?.username);
const password = event.headers?.Password;
// See 'auth-service.ts' below for implementation
await authenticateCustomer(username, password, process.env.USER_POOL_ID, process.env.USER_POOL_CLIENT_ID);
return {
statusCode: 200,
body: JSON.stringify(responseBody),
};
}
Note that you may be required to decode parameters passed to your request since some characters get encoded with escape sequences. Example: test@example.com
is received as test%40example.com
Authenticating your user
// auth-service.ts
// Common log method
import { Log } from '../util/logging';
// You will need to install node-fetch as part of your lambda dependencies
const fetch = require('node-fetch');
// You need to set node fetch as global variable for 'amazon-cognito-identity-js' library to work
global.fetch = fetch;
// This is the official library provided by AWS for authentication
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const { CognitoUser, CognitoUserPool, AuthenticationDetails } = AmazonCognitoIdentity;
/*
* Authenticate user in cognito pool
* @param username Customer Username
* @param password Customer Password
* @returns string if authentication was successfuly or not
*/
export async function authenticateCustomer(username: string, password: string, userPoolId: string, clientId: string): Promise<string> {
try {
const authDetails = new AuthenticationDetails({
Username: username,
Password: password,
});
const userPool = new CognitoUserPool({ UserPoolId: userPoolId, ClientId: clientId });
const cognitoUser = new CognitoUser({
Username: username,
Pool: userPool,
});
// The library only exposes authentication callbacks, so we create our own promise here
await new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authDetails, {
onSuccess: resolve,
onFailure: reject,
newPasswordRequired: resolve,
});
});
return 'User was successfully authenticated';
} catch (e) {
Log.error('Error authenticating user in identity provider lambda');
return 'User authentication failed';
}
}
Getting user attributes
You can also get attributes associated with the user in your cognito
/**
* The suitable interface could not be imported from AmazonCognitoIdentity library and used here
* The library does not provides an @types version either
*/
interface ICognitoUserAttributeData{
Name: string;
Value: string;
}
export async function authenticateCustomer(username: string, password: string, userPoolId: string, clientId: string): Promise<string> {
// ...
// Once you authenticate your user, session is created on cognitoUser
await new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authDetails, {
onSuccess: resolve,
onFailure: reject,
newPasswordRequired: resolve,
});
});
// you can use cognitoUser.getUserAttributes since session exists on cognitoUser
const attributes: ICognitoUserAttributeData[] = await new Promise((resolve, reject) => {
cognitoUser.getUserAttributes((err: Error, attrs: ICognitoUserAttributeData[]) => {
if (err) {
reject(err);
}
resolve(attrs);
});
});
// You can then find your required attribute from the list of 'attributes'
const attribute = (attributes || []).find(attribute => attribute.Name === 'custom:customerId')?.Value ?? '';
}
Creating user via CLI for testing
In order to confirm that everything is working as expected, you need to test your auth call. And to test, you need users in your cognito user pool. While the application should have code (at some point) that allows for the creation of users in your pool, it is easier to create users via the AWS CLI for testing purposes. This allows you to test and complete the current functionality independent of when user creation is implemented in your repo.
You can create your user in the cognito pool using the following code:
aws cognito-idp admin-create-user --user-pool-id <user-pool-id> --username <username>
The command will return an UUID username which you should note down for using with the next set of commands. Next you have to set the permanent password to your user,
aws cognito-idp admin-set-user-password --user-pool-id <user-pool-id> --username <uuid-username> --password <password> --permanent
You can set attributes to user using the following command:
aws cognito-idp admin-update-user-attributes --user-pool-id <user-pool-id> --username <uuid-username> --user-attributes Name="custom:customerId",Value="customer1"
Take note to replace the values for <user-pool-id>,<username>, <uuid-username>, <password> etc. in all your commands with appropriate values.
Comments
0 comments
Please sign in to leave a comment.