Branch-Scoped Roles in Amazon Cognito Without Group Sprawl
Cognito groups are flat, so multi-location apps struggle to express "Admin at Branch A, Staff at Branch B." This article shows how to handle it with one custom attribute and a Pre Token Generation Lambda.
The problem
In a multi-branch app (dorms, clinics, franchises), a role only makes sense per branch. Cognito user pool groups are flat, so the obvious fix is one group per combination:
branchA-admin, branchA-staff, branchB-admin... That grows with every new branch and turns into a maintenance problem quickly.The approach
- Store each user's branch roles in a single custom attribute.
- Use a Pre Token Generation Lambda to put them into the access token.
- Check the claim in your backend.
1. Add a custom attribute
Create
custom:branch_roles in your user pool and store a JSON string:1
{"branch-a": "admin", "branch-b": "staff"}Important: remove write permission for this attribute from your app client. Otherwise users could edit their own roles. Only your admin backend (via
AdminUpdateUserAttributes) should set it.2. Pre Token Generation Lambda (event version V2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export const handler = async (event) => {
let branchRoles = {};
try {
branchRoles = JSON.parse(event.request.userAttributes["custom:branch_roles"] || "{}");
} catch {
branchRoles = {};
}
event.response = {
claimsAndScopeOverrideDetails: {
accessTokenGeneration: {
claimsToAddOrOverride: { branch_roles: branchRoles }
}
}
};
return event;
};Note: customizing the access token requires the V2 trigger event, which is available on the Essentials or Plus feature plan.
3. Enforce it in your API
1
2
3
4
5
6
7
8
9
10
11
12
import { CognitoJwtVerifier } from "aws-jwt-verify";
const verifier = CognitoJwtVerifier.create({
userPoolId: process.env.USER_POOL_ID,
tokenUse: "access",
clientId: process.env.CLIENT_ID,
});
const payload = await verifier.verify(token);
if (payload.branch_roles?.[branchId] !== "admin") {
return res.status(403).json({ error: "Forbidden" });
}Trade-offs
- Role changes take effect only after the user's token refreshes.
- You now maintain a Lambda for something that arguably belongs in Cognito itself.
That second point is why I submitted a Wishlist request **** for native scoped groups in Cognito. If you've hit the same problem, give it a vote.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article