Cognito and Permit Integration
Introduction
This tutorial will show you how to integrate Cognito with Permit. Cognito is a fully managed identity provider service offered by AWS. It allows you to create and manage user pools that can be used for authentication in your application. Permit is a fully managed authorization service that allows you to manage all your application's authorization policies in one place.
Using Cognito and Permit together allows you to easily manage your application's authentication and authorization in a secure and scalable way.
What you’ll build
In this tutorial, you’ll learn how to integrate Cognito with Permit. We will create a WebApp that uses Cognito for secure authentication and Permit.io for easily manageable authorization, allowing us to change our application's permissions on the fly.
Prerequisites
- AWS account
- Permit account
- An application that uses Cognito for authentication; in this example, we will use a Vanilla JS WebApp with a Node server Check out the demo explanation for more details.
Getting Started
1. Setting up Permit
1.1. Setup roles in Permit
To sync users and roles from Cognito to Permit, we must recreate identical roles in Permit.
Create your roles according to your application's needs.
For example, many simple RBAC applications will have three roles: admin
, editor
, and viewer
.
Setting up roles and resources can be done through the Permit dashboard, by using the Permit API, or with one of our SDKs. You can also follow this Permit RBAC guide
1.2. Create the right resources in Permit
Create a corresponding resource in Permit for each of your application's resources. For instance, if your app has a user management section, you will need a user
resource with create
, delete
, and get
actions. If you have a task management section, you need a task
resource with create
, delete
, get
, and check
actions.
Setting up resources can be done through the Permit dashboard, by using the Permit API, or with one of our SDKs.
1.3. Set up the right permissions in Permit.io:
You must set up the right permissions for each role you create in step 1.1.
Check each action you want to allow for each resource in each role.
As noted earlier, these permissions can be set up through either the Permit policy editor or by using the Permit API.
2. Integrate your application with Cognito and Permit
2.1. Connect Cognito to your frontend application
As we want to use Cognito for authentication, we will use the Cognito JWT to get each user's information and access token. You will need to add in your FE code a check to see if the user is logged in and redirect them to the login page if they are not.
2.2. Sync Cognito with Permit after successful login
After the user logs in, we must sync their information with Permit. There are two options to achieve this:
We can tell Cognito to redirect us to a specific URL after the user is logged in, or we can use the code
query parameter they send after the login to sync the user to Permit.
Here we show an example of syncing the user if we see the code query parameter in the URL.
// frontend/app.mjs
if (searchParams.get("code") !== null) {
//...
const syncUser = await fetch("api/sync", {
method: "POST",
headers: new Headers({ Authorization: `Bearer ${tokens.access_token}` }),
});
//...
}
On your backend side, you will need to add code that will check the validity of the Cognito JWT, retrieve user information, and sync the user to Permit.
// standalone_be/index.mjs
// init Cognito verifier and Permit SDK
import { CognitoJwtVerifier } from "aws-jwt-verify";
import { Permit } from "permitio";
// verifier for the id token:
const verifierIdToken = CognitoJwtVerifier.create({
userPoolId: [your user pool id],
tokenUse: "id",
clientId: [your client id],
}
);
const permit = new Permit(
{
token: [your permit token],
// in production, you might need to change this url to fit your deployment
pdp: "https://cloudpdp.api.permit.io",
}
);
// cognitoUser example:
//{
// "at_hash": "dttAg8DUmRrf_xxxxxxxxx",
// "sub": "e69bd1ef-0067-40ef-9262-xxxxxxxxx",
// "email_verified": true,
// "iss": "https://cognito-idp.us-east-2.amazonaws.com/us-east-xxxxxxxxx",
// "cognito:username": "test_username",
// "origin_jti": "ce6914d5-5cd2-40d4-a0c3-xxxxxxxxx",
// "aud": "27fr56i7g292frkxxxxxxxxx",
// "token_use": "id",
// "auth_time": 1689097274,
// "name": "test_name",
// "exp": 1689100874,
// "iat": 1689097274,
// "jti": "1345944f-d8a8-4301-80f9-xxxxxxxxx",
// "email": "test@permit.io"
//}
// sync user route
app.post("/api/sync", async (req, res) => {
try {
const cognitoUser = await verifier.verify(
req.headers.authorization?.split(" ")[1] // the JWT as string
);
} catch (error) {
res.status(403).send("Token not valid!");
}
const syncUser = await permit.api.syncUser({
"first_name": cognitoUser.name,
"key": cognitoUser.sub,
"email": cognitoUser.email,
}
);
// you can also assign role to a user here if you have mapping between Cognito groups and Permit roles
// with the assign role SDK method
// await permit.api.assignRole({
// "key": cognitoUser.sub,
// "role": caseSensitiveRoleKey,
// "tenant": caseSensitiveTenantKey, // if you don't use tenants, use 'default'
// });
res.status(200).send(syncUser);
}
);
You need to add the following information to the code above:
your user pool id
- the user pool id of your Cognito user pool, can be found in the Cognito dashboard: Amazon Cognito > User pools > Select your user poolyour client id
- the client id of your Cognito user pool client, can be found in the Cognito dashboard: Amazon Cognito > User pools > Select your user pool > App clients > Select your app clientyour permit token
- the token of your Permit account, Get your environment API Key
2.2. Enforce permissions with Permit
After we have the policy that we created in step 1, and synced the user to Permit in the previous step, we can enforce the permissions on the backend side. We will use the Permit SDK to get the user's permissions and check if they have the right ones.
All you need to do is add the permit.check
function before each action you want to protect (All of your CRUD actions in the API).
// An example of a protected route
// app.delete("/api/tasks/:id", async (req, res) => {
const permitted = permit.check(
cognitoUser.sub, // the user key from the syncUser step
'delete', // the action name
'task' // the resource name
);
if (!permitted) {
res.status(403).send('Not permitted');
return;
}
// delete the task
//...
});
2.3. Removing a user
If you want to remove a user from your app, make sure you remove the user from both Cognito and Permit. You can do this by adding the following code to your remove user function:
const removedUser = await permit.api.deleteUser(permitUserObj.key);
3. Let's test it!
Now that we have everything set up, let's test it out. Login to your app and try to perform actions that you have permissions for. Now revoke some of the permissions in the Permit dashboard and try to perform the same actions again, it will take effect immediately without refresh.