This project illustrates how to use Cognito as an identity broker for multiple client applications. Using an identity broker simplifies client applications by removing the need to implement the entire authentication process.
In addition to simplifying and unifying the authentication process, this approach has the following benefits:
- Token validity length can vary by each client (hours, days, etc)
- Limit User attribute read/write permissions per client
- Pinpoint analytics per client
- Enabling advanced security per client
- Cognito Identity Broker Web Applications -- Broker and example client application utilizing this API
- Cognito SSO Client -- A client library to facilitate the authorization code flow process for client applications.
- AWS Account
- Node v20.x
- Postman
- Create AWS credentials that have the ability to deploy all resources in this stack. Typically you want a least privilege approach, but to simplify this example, create credentials with admin access so all resources can be created.
- Create an AWS credentials profile in your local
~/.aws/credentials
file. Alternatively, use an existing profile with the--profile
command line option during deploy[deploy.dev] aws_access_key_id=xxxxxxxxxxxxxxxx aws_secret_access_key=yyyyyyyyyyyy
- Run the following commands
npm install
npx sls deploy --region us-east-1
- Note: The cognito details will be output to the console when the deployment completes. Those details will also be stored in the file
.cognito-details.json
for your reference.
- Create a user in your new User Pool
npx sls invoke local --function createUser --data '{"name":"Bob Belcher", "email": "bob@burger.com", "password": "MyPassword1"}'
- Setup Postman collection & environment
- Import
postman/Cognito Identity.postman_collection.json
- Import
postman/Cognito Identity.postman_environment.json
- Set your Postman environment variables (info available in
.cognito-details.json
after deploy)apiUrl
- The base URL of your deployed APIbrokerClientId
- The Broker client IDclientId1
- Client 1 IdclientId2
- Client 2 Id
- Turn off automatic redirects in postman. This will allow JSON responses on a GET method that returns a 302 redirect)
Settings --> General --> Headers --> Automatically follow redirects
The Authorization Code Flow process is broken into 3 main proceses:
- User logs in directly with the broker
- User's broker credentials are used to generate a code grant
- User exchanges the code for client-level tokens from the client app
The postman collection has been setup to populate required variables (code
, refreshToken
) during the auth flow to eliminate the need to copy/paste values.
Note: As an alternative to using postman, you can use the Cognito Identity Broker Web Applications to test the entire authentication flow directly from the browser.
- Send the
Login
request. This will:- Return the broker credentials (
id, access, refresh
tokens) - Set the tokens in cookies on the broker's Api domain.
- Return the broker credentials (
- Send the
Start Client Auth Flow
request. This will:- Pass the user's broker credential cookies in the request.
- Return a
code
for exchange.
- Send the
Exchange Code for Tokens
request. This will:- Return the client credentials (
id, access, refresh
tokens)
- Return the client credentials (
- Success! The user has been successfully authenticated with the client.
- To refresh the client's credentials, send the
Refresh Client Tokens
request. This will:- Return refreshed
id
andaccess
tokens.
- Return refreshed
The postman collection has been setup with a paired challenge/verifer to simplify the example. If you would like to generate a new pair, use the following command:
npx sls invoke local --function generateChallenge
- Use Client 2. - This client is configured w/tokens that are only valid for 1 hour. Client 1 tokens are valid for 2 hours.
- Use an invalid
redirectUri
when starting the auth flow -- this will result in an error - Use an invalid
codeChallenge
orcodeVerifier
while initiating or exchanging a code for tokens. -- this will result in an error
{
"clientId": "{{brokerClientId}}",
"username": "bob@burger.com",
"password": "MyPassword1"
}
{
"requestId": "979b8012-a755-45ad-9cfe-43a51b0e3473",
"requestStartTime": "2024-03-27T19:35:36.766Z",
"requestEndTime": "2024-03-27T19:35:37.193Z",
"success": true,
"data": {
"success": true,
"result": "logged_in",
"authentication": {
"accessToken": "eyJraWQiOiIy...",
"idToken": "eyJraWQiOiJ4Zz...",
"refreshToken": "eyJjdHk...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
}
}
This allows a user to login via the Broker's clientId. This is the only client that allows user login. Attempting to use consumer client's id will result in a login failure.
?clientId={{clientId1}}&redirectUri={{redirectUriClient1}}&codeChallenge=xhrMObY-MOMInTN9GTR1HdzdQskQMJeU8CwxaTL0nA8`
clientId
- A consumer Client IdredirectUri
- A redirectUri that is registered to the client in Cognito. (configured in:serverless-cognito.yml
)codeChallenge
- (optional) If provided, thecodeVerifier
must be used during the exchange of code for tokens.
{
"requestId": "445fffcb-ec7a-4712-a129-b9e4980d46f1",
"requestStartTime": "2024-03-27T19:35:53.257Z",
"requestEndTime": "2024-03-27T19:35:54.568Z",
"success": true,
"data": {
"success": true,
"result": "code_flow_initiated",
"code": "9039c5dc-c925-4407-9aea-5e857c9c4fd0",
"redirectUri": "http://localhost:3001?code=9039c5dc-c925-4407-9aea-5e857c9c4fd0"
}
}
This endpoint uses the GET
method for browser redirect support (via a 302
HTTP status code and a location
header with the redirectUri
populated with the code grant). Additionally it will return a JSON response for non-web clients.
Note: To get the JSON response in Postman, you have to turn off redirect following: Settings --> General --> Headers --> Automatically follow redirects
{
"grantType": "authorization_code",
"clientId": "{{clientId1}}",
"redirectUri": "{{redirectUriClient1}}",
"code": "{{code}}",
"codeVerifier":"utki3vQExiDuyXciGO1ww2mjJL3s0jWU5CNfUIa8rYiOACqVApLb1XPkLsIsrht5ekGYkAMCvEV6zWVKAenwfPgnFrTdRytsMaErKoZ8SU4Cs1gd1aASe2Z3oYAchARJ"
}
clientId
- A consumer Client IdredirectUri
- The same redirectUri used when starting the auth flow processcode
- The code returned from starting the auth flow processcodeVerifier
- (optional) IfcodeChallenge
was sent during the start of the auth flow process, this is required.
{
"requestId": "979b8012-a755-45ad-9cfe-43a51b0e3473",
"requestStartTime": "2024-03-27T19:35:36.766Z",
"requestEndTime": "2024-03-27T19:35:37.193Z",
"success": true,
"data": {
"success": true,
"result": "logged_in",
"authentication": {
"accessToken": "eyJraWQiOiIy...",
"idToken": "eyJraWQiOiJ4Zz...",
"refreshToken": "eyJjdHk...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
}
}
{
"grantType": "refresh_token",
"clientId": "{{clientId1}}",
"refreshToken": "{{refreshToken}}"
}
clientId
- A consumer Client IdrefreshToken
- A valid/non-expired consumer client refresh token
{
"requestId": "47a775d7-d8ca-48eb-9563-c662b5900407",
"requestStartTime": "2024-03-27T19:36:47.044Z",
"requestEndTime": "2024-03-27T19:36:47.261Z",
"success": true,
"data": {
"success": true,
"result": "refreshed",
"authentication": {
"accessToken": "eyJraWQiOiIyc1V...",
"idToken": "eyJraWQiOiJ4ZzlHMVdlW...",
"expiresIn": 7200,
"tokenType": "Bearer"
}
}
}
This allows the consumer client application to request new id
and access
tokens from the broker. A new refresh token will not be returned. The only way to get a new refresh token is to use the authorization_code
grant type.
This project has a suite of unit tests that can be run with:
npm run test
- This example uses a
username/password
approach for login. This is to keep this example focused on the broker concept. The preferred approach would be using SRP (Secure Remote Password). - To remove the entire stack, run the following command:
npx sls remove