Skip to content

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

  1. User authenticates against the Cognito user pool.
  2. Their user-pool-group membership identifies the tenant.
  3. Cognito identity pool maps group → IAM role (via the identity pool's role-mapping rules, typically keyed on the cognito:groups claim).
  4. AWS STS issues temporary credentials for that role.
  5. 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

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

Last updated · 476 distilled / 1,218 read