AWS Cognito
Amazon Cognito is a relatively simple and secure service offering sign-up, sign-in and access control to web, mobile apps in a simple and easy way. Supports sign-in using social identity providers such as Google, Facebook, Apple, Amazon.
User pool
Cognito user pool is a user directory that provides a sign-in/sign-up option.
Identity pool
Enables access grant to other AWS services.
Use case
Assume following requirements:
- users sign-in and sign-up in the system
- system features are accessible only after successful sign-in
Diagram
Following setup consists of multiple users interacting with the sistem using different client types like web interface, mobile, or both. In all cases users can login and will access system features only while being logged in the system.
Notes
User flow starts at login page, which leads to user authentication via Cognito and depending on configuration can result in session token generation. This token will be used during client (web/mobile) and server interaction,
API Gateway
will useCognito
service for token verification, so that only valid active sessions can access the system.
SAM Template
Following configuration contains Cognito user pool and client, few API Gateway V1 rest endpoints representing the system features. API Gateway Authorizer is responsible for access control by verifying JWT token via Cognito service.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
AWS Cognito User pool and client configuration example
Parameters:
Env:
Type: String
Description: Logical sandbox prefix
Default: val
AllowedValues:
- val
- test
App:
Type: String
Description: Application name
Default: cognito-example
Globals:
Function:
Timeout: 3
MemorySize: 128
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Sub ${Env}-user-pool
Schema:
- Name: email
Required: true
Mutable: true
AutoVerifiedAttributes: ['email']
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
Policies:
PasswordPolicy:
MinimumLength: 6
RequireLowercase: true
RequireUppercase: true
RequireNumbers: true
RequireSymbols: true
TemporaryPasswordValidityDays: 15
UsernameAttributes:
- email
UsernameConfiguration:
CaseSensitive: false
UserPoolTags:
Key: App
Value: !Ref App
UserClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: !Sub ${Env}-user-pool-client
UserPoolId: !Ref UserPool
AccessTokenValidity: 1
IdTokenValidity: 1
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
GenerateSecret: false
RestApi:
Type: AWS::Serverless::Api
Properties:
Description: Rest API for foo bar
StageName: !Ref Env
Auth:
Authorizers:
CognitoAuth:
UserPoolArn: !GetAtt UserPool.Arn
AuthType: COGNITO_USER_POOLS
DefaultAuthorizer: CognitoAuth
Tags:
App: !Ref App
Env: !Ref Env
FooFunction:
Type: AWS::Serverless::Function
Properties:
Description: Foo feature
FunctionName: !Sub ${Env}-${App}-foo
CodeUri: app/foo/
Handler: handler
Runtime: go1.x
Tracing: Active
Events:
Foo:
Type: Api
Properties:
Method: get
Path: /foo
RestApiId: !Ref RestApi
Tags:
App: !Ref App
Env: !Ref Env
BarFunction:
Type: AWS::Serverless::Function
Properties:
Description: Bar feature
FunctionName: !Sub ${Env}-${App}-bar
CodeUri: app/foo/
Handler: handler
Runtime: go1.x
Tracing: Active
Tags:
App: !Ref App
Env: !Ref Env
Outputs:
PoolId:
Description: User pool identifier
Value: !Ref UserPool
ClientId:
Description: User pool client identifier
Value: !Ref UserClient
Notes
Auth flow SRP (Safe Remote Password) prevents password exposure for Man in the middle scenario. SRP
Authentication via Amplify
Nice thing about amplify is that it’s highly modular and it’s possible to take only Auth
related packages like this one @aws-amplify/auth
.
Current pool configuration allows admin only user registration AllowAdminCreateUserOnly: true
(either via web cli or programmatically via SDK).
User Confirmation & SignIn
Amplify setup requires user pool identifier and client indentifier.
import { Auth } from "@aws-amplify/auth";
Auth.configure({
Auth: {
userPoolId: 'XXX',
userPoolWebClientId: 'XXX',
region: 'eu-west-1',
}
})
Next step is sign in attempt using temporary password (that was sent by cognito to specified user email) and changing it to new one, this will result in valid cofgirmed user.
const user = 'foo@bar.com'
const pass = '7uJ,q9'
const newPass = 'secret'
const cognitoUser = await Auth.signIn(user, pass)
.then(user => {
if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
Auth.completeNewPassword(
user,
newPass,
).then(user => {
return user
})
}
return user
})
Successful sign in & confirmation will result in cognito user details provided to the client, and cognito access token requird for lambda api gateway authorizers.
All related code sits in Git