Check out v4 of the Fauna CLI
v4 of the Fauna CLI is now in beta. The new version introduces enhancements to the developer experience, including an improved authentication workflow. To get started, check out the CLI v4 quick start. |
Amazon Cognito integration
This guide covers how to use Amazon Cognito to authenticate with a Fauna database.
Once set up, Cognito issues a JWT when end users log into your application. The JWT contains a Fauna token's authentication secret for the user in a private claim. Your application can use the secret to run queries on the user’s behalf.
Before you start
To complete this guide, you’ll need:
-
An Amazon Cognito account.
-
The AWS SAM CLI.
-
Familiarity with AWS Lambda and AWS Cognito.
Authentication flow
You can’t use Cognito as an access
provider. Cognito JWTs don’t support aud
claims, which are required for JWTs
used as an authentication secret in Fauna.
Instead, the setup uses the following authentication flow:
-
The client application sends a request to Cognito. Cognito invokes an AWS Lambda function in the token generation phase.
-
The Lambda function generates a Fauna token for the end user. The function includes the token’s secret in a private
fauna_token_secret
claim in the payload of the JWT issued by Cognito. -
Cognito returns the JWT to the client application. The client application uses the token secret to authenticate Fauna queries on the user’s behalf.
Configure Fauna
-
Log in to the Fauna Dashboard and select your database.
-
In the Dashboard Shell, run the following FQL query to create a key with the
server
role:Key.create({ role: "server" })
{ id: "404130885316640841", coll: Key, ts: Time("2099-07-22T17:08:15.803Z"), role: "server", secret: "fn..." }
Copy the key’s
secret
. You’ll use it later. -
Create a collection to store identity documents for your application’s end users.
Edit the collection’s schema to include an
email
field or similar identifier used to uniquely identify users. For example:collection Customer { unique [.email] index byEmail { terms [.email] } }
-
Create one or more user-defined roles.
Edit the role’s schema to include the previous collection in the role’s
membership
property. For example:role customer { membership Customer privileges Product { read } ... }
Create an AWS Lambda function
-
Initialize a new SAM project. In your terminal, run:
sam init
-
When prompted, select:
-
AWS Quick Start Templates
as the template source. -
Hello World Example
as the AWS Quick Start application template. -
Enter
N
(no) when asked to use the most popular runtime and package type. -
nodejs20.x
as the runtime -
Zip
as the package type -
Hello World Example
as the starter template
-
Follow the prompts and select the Hello World Example (Node.js 18.x)
starter template.
-
Navigate to the project directory and install the Fauna JavaScript driver.
cd your-project-name npm install fauna --save
-
In the project directory, open
app.mjs
. The file contains the JavaScript code for the Lambda function. Replace the file’s contents with the following:import { Client, fql } from 'fauna'; const client = new Client({ secret: process.env.FAUNA_SECRET, }); export const lambdaHandler = async (event, context) => { // Get the email from event. const email = event.request.userAttributes['email']; const fauna_response = await client.query(fql` // If a Customer document with the email exists, get it. // Otherwise, create a Customer document. let user = Customer.byEmail(${email}).first() ?? Customer.create({email: ${email}}) // Create a token for the Customer document. let token = Token.create({ document: user, ttl: Time.now().add(30, 'minutes') }) // Return the Customer document's ID and the // token's secret. let payload = { userId: user!.id, token: token.secret } payload `); event.response = { "claimsOverrideDetails": { "claimsToAddOrOverride": { fauna_token_secret: fauna_response.data.token, userId: fauna_response.data.userId }, "claimsToSuppress": ["email"] } }; context.done(null, event); return event; };
-
Notice in the Lambda function code you make a query to Fauna to create an identity document for the user. The
ttl
property sets the token’s expiration time to 30 minutes. -
Define the environment variable
FAUNA_SECRET
in thetemplate.yaml
file. In thetemplate.yaml
file, locate theEnvironment
section under your Lambda function’s configuration. If it doesn’t exist, you can add it. -
In
template.yaml
, add theFAUNA_SECRET
environment variable. Set the variable’s value to the Fauna key secret you created earlier.Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello-world/ Handler: app.lambdaHandler Runtime: nodejs20.x Environment: Variables: FAUNA_SECRET: "fn..."
-
Build and deploy the Lambda function by running the following commands:
sam build sam deploy --guided
-
Note the name of the Lambda function. You’ll use it in the next step to integrate with Cognito.
Configure AWS Cognito
-
Follow the Amazon Cognito guide to create a new user pool.
-
Note your user pool ID and client ID. You’ll use it later to integrate with your application.
-
Navigate to the user pool’s User-pool-properties tab and click Add Lambda trigger.
-
On the Add-Lambda-trigger page, select Authentication > Pre token generation trigger.
-
In the Assign Lambda function section, enter select the Lambda function you previously created.
-
Click Add Lambda Trigger.
Test user access
Verify that the setup works:
-
Create a test user in AWS Cognito.
-
Log in to the AWS Cognito user pool using the test user’s credentials.
The following Node.js sample code logs in a user using the AWS Cognito SDK: :
// signin.js import { CognitoUserPool, CognitoUser, AuthenticationDetails } from 'amazon-cognito-identity-js'; const config = { UserPoolId: '<COGNITO_USER_POOL_ID>', ClientId: '<COGNITO_CLIENT_ID>' } const poolData = { UserPoolId: config.UserPoolId, ClientId: config.ClientId, }; const userPool = new CognitoUserPool(poolData); const cognitoUser = new CognitoUser({ Username: '<USER_EMAIL>', Pool: userPool, }); const authenticationDetails = new AuthenticationDetails({ Username: '<USER_EMAIL>', Password: '<USER_PASSWORD>', }); cognitoUser.authenticateUser(authenticationDetails, { onSuccess: data => { console.log(data); }, onFailure: err => { console.log('Failed', err) }, newPasswordRequired: newPass => { console.log('New Pass Required', newPass) } })
-
After successful login, you should receive a
CognitoIdToken
JWT with thefauna_token_secret
field in the payload:ognitoUserSession { idToken: CognitoIdToken { jwtToken: '...', payload: { ... fauna_token_secret: 'fn...', ... } }, refreshToken: CognitoRefreshToken { ... }, accessToken: CognitoAccessToken { ... }, clockDrift: 0 }
-
In your client application, use the
fauna_token_secret
to run Fauna queries on behalf of the user.
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!