PATTERN Cited by 1 source
Cognito group → IAM role mapping¶
What it is¶
A multi-tenant SaaS authorization pattern: every tenant is backed by a Cognito user pool group linked to a dedicated IAM role with a scoped policy, and the Cognito identity pool translates group membership into STS temporary credentials scoped to that role. The user authenticates once; the AWS credentials they obtain can only read/write their tenant's AWS resources.
The flow¶
- User authenticates against the Cognito user pool.
- Their user-pool-group membership identifies the tenant.
- Cognito identity pool maps group → IAM role (via the identity pool's
role-mapping rules, typically keyed on the
cognito:groupsclaim). - AWS STS issues temporary credentials for that role.
- The application uses those credentials to call AWS APIs — e.g. read the tenant's secret from AWS Secrets Manager, read/write the tenant's prefix in S3.
Because the IAM role policy scopes access to only the tenant's resources (by ARN, prefix, or tag), cross-tenant access is denied at the IAM layer — not at the application layer. A bug in the application cannot leak another tenant's data because the temporary credentials simply don't authorize the call.
Canonical example: PACIFIC (Catena-X PCF exchange)¶
In PACIFIC, every participating company gets:
- a Cognito user-pool group,
- a per-company IAM role whose policy grants Secrets-Manager access only to that company's secret (EDC connector credentials + DTR credentials),
- an S3 prefix under which only that company's IAM role can read/write PCF records.
When a user authenticates, the identity pool returns temporary credentials for their company's IAM role. If a bug somehow directed the request at another company's secret, IAM would deny it — the credentials are structurally incapable of reading cross-tenant data.
Why this beats account-per-tenant¶
The obvious alternative is one AWS account per tenant — strongest isolation, but also:
- manual account provisioning per customer,
- N× the operational overhead (billing, IAM, VPC, CloudTrail, quotas),
- complicated cross-account support tooling,
- N× the onboarding latency.
Cognito-group → IAM-role mapping delivers most of the same isolation guarantees (credentials simply can't reach cross-tenant resources) inside a single AWS account. PACIFIC grew +80% newly onboarded companies from 2024 to 2025 on this model without spinning an AWS account per company (Source: PACIFIC).
Why this beats application-layer tenant checks¶
Application-layer if user.tenant_id != resource.tenant_id checks are
correct when they run, but they're one bug away from leaking. Moving
the boundary to IAM means:
- the AWS SDK, not the application, is the enforcer,
- the policy is declarative and auditable (CloudTrail shows denied calls),
- new code paths automatically inherit the scope.
Complements¶
- patterns/pre-token-generation-hook — adds a
tenant_idclaim to the access token itself; useful when downstream services need to propagate tenant binding without calling STS. - patterns/per-tenant-policy-store — if fine-grained authorization is also needed, pair Cognito→IAM with a per-tenant Amazon Verified Permissions policy store.
- patterns/runtime-provisioned-per-tenant-search-index — sibling at the data-layer: per-tenant OpenSearch index rather than shared index with filtering.
Trade-offs¶
- IAM role quota: an AWS account has a soft quota on IAM roles (~1000). Tens of thousands of tenants means you hit this and need to migrate to resource-based policies or separate accounts.
- Role-provisioning latency: creating an IAM role + policy per new tenant is synchronous API work that has to happen before the first sign-in — not instant.
- Blast radius from policy bug: if the template policy is wrong, every tenant provisioned with that template is wrong. Test the policy with the smallest possible scope first.
See also¶
- concepts/tenant-isolation — the parent concept this pattern implements
- concepts/account-per-tenant-isolation — the alternative this pattern deliberately avoids
- concepts/short-lived-credential-auth — the STS property that makes the pattern practical (no static credentials to rotate)
- systems/amazon-cognito, systems/aws-iam, systems/aws-sts