Authentication and Authorization on AWS: Cognito, IAM, and STS
Authentication proves who a caller is; authorization decides what that caller may do — and on AWS you implement both with Amazon Cognito, IAM, and AWS STS. Security is 26% of the DVA-C02 exam, and this task supplies many of its questions: almost every serverless scenario ends in "how do users sign in?" or "how does this service get permission to call that one?". The exam rewards a short list of sharp distinctions: user pools authenticate people and issue JWTs, identity pools exchange those tokens for temporary AWS credentials, IAM roles always beat long-term access keys, and an explicit deny beats every allow. This lesson works through each mechanism — federation, bearer tokens, STS role assumption, SigV4 signing, IAM policy evaluation, API Gateway authorizers, and service-to-service authentication in microservices — at the depth the exam probes, with the comparisons you need to have memorized.
On this page8 sections
- User pools vs identity pools: the exam's favorite comparison
- Federated sign-in through Cognito
- Bearer tokens and JWTs: ID, access, and refresh
- AWS STS and assuming IAM roles
- Programmatic access: roles beat access keys
- Defining permissions for IAM principals
- Securing APIs: API Gateway authorizers compared
- Application-level and cross-service authorization
- Distinguish Cognito user pools (authentication and JWTs) from identity pools (temporary AWS credentials) and pick the right one from scenario wording
- Explain JWT structure and the distinct jobs of the ID, access, and refresh tokens
- Configure programmatic access with IAM roles, STS temporary credentials, and the SDK credential provider chain instead of long-term keys
- Apply IAM policy evaluation — identity-based vs resource-based, explicit deny wins — to grant least-privilege permissions
- Choose between Cognito, Lambda, and IAM authorizers to secure an API Gateway API
- Design role-based and service-to-service authorization for microservice architectures
User pools vs identity pools: the exam's favorite comparison
A Cognito user pool is a managed user directory that handles authentication: sign-up, sign-in, password policies, MFA, and account recovery. When a user signs in, the user pool issues JSON Web Tokens (JWTs) that prove who the user is. A Cognito identity pool handles authorization to AWS services: it exchanges an identity token — from a user pool or an external provider — for temporary AWS credentials from AWS STS, so your app can call services such as S3 or DynamoDB directly.
| Question | User pool | Identity pool |
|---|---|---|
| Core job | Authentication — who is this user? | Authorization — temporary AWS credentials |
| What it issues | ID, access, and refresh JWTs | Access key, secret key, and session token |
| Backed by | Its own managed user directory | IAM roles assumed through AWS STS |
| Unauthenticated guests | No | Yes — supports unauthenticated identities |
| Typical exam wording | "sign up", "sign in", "user directory", "MFA" | "access AWS services", "temporary credentials", "guest access" |
The two are independent and frequently combined: users authenticate against the user pool, then the identity pool trades the resulting token for scoped AWS credentials. Read exam stems for the verb. If the application needs users to log in, the answer involves a user pool. If the application itself needs to call AWS services on behalf of users — including anonymous guests, which only identity pools support — the answer involves an identity pool. Many correct answers involve both.
Federated sign-in through Cognito
Federation lets users sign in with an identity they already own — a corporate SAML directory, an OIDC provider, or a social account (Google, Facebook, Apple, Login with Amazon) — instead of creating yet another password. Cognito supports federation in both pool types, and the exam expects you to know which layer is doing the federating.
A user pool as identity broker: you register the external IdP (SAML 2.0 or OIDC) or social provider with the user pool, and users pick their provider on the hosted sign-in page. Whichever provider they choose, the user pool normalizes the result and issues its own standard JWTs. That normalization is the selling point: your API validates one token format regardless of whether the user arrived via Google or a corporate Active Directory federated over SAML.
An identity pool federating directly: identity pools accept tokens from user pools, social providers, SAML and OIDC IdPs, and even developer-authenticated identities, and exchange any of them for temporary AWS credentials. Under the hood this is STS web-identity federation — the identity pool maps the token to an IAM role and returns that role's credentials.
Exam trigger: "employees already authenticate with a corporate SAML identity provider" means federate — never migrate or duplicate accounts into a new directory. If those users then need to call AWS services from the app, chain the pieces: external IdP signs the user in, the user pool brokers and issues JWTs, the identity pool exchanges them for temporary credentials.
Bearer tokens and JWTs: ID, access, and refresh
A bearer token grants access to whoever presents it — possession is proof. That is why bearer tokens travel only over HTTPS, expire quickly, and must be validated on every request. Cognito's tokens are JWTs: three base64url-encoded parts separated by dots — header (algorithm and key ID), payload (claims), and signature. Your backend verifies the signature against the user pool's published public keys (the JWKS endpoint) and rejects tokens whose exp (expiry), iss (issuer), or audience claims don't check out. Claims to recognize on sight: sub (the user's unique ID), token_use (distinguishes ID from access tokens), and cognito:groups (group membership).
A user pool issues three tokens per sign-in:
- ID token — who the user is: name, email, and other profile claims. Use it for identity information inside your app.
- Access token — what the caller may do: OAuth scopes and groups. Use it to authorize API requests.
- Refresh token — a long-lived credential used only against Cognito to obtain fresh ID and access tokens without re-login. Never send it to your API.
By default ID and access tokens live one hour and refresh tokens thirty days; both are configurable per app client. One nuance the exam likes: an API Gateway Cognito authorizer configured with OAuth scopes requires the access token, while one without scopes validates the ID token. When in doubt, remember the split: ID token carries identity claims, access token carries authorization.
AWS STS and assuming IAM roles
AWS STS mints temporary credentials, and sts:AssumeRole is its core operation: a caller assumes an IAM role and receives a short-lived access key ID, secret access key, and session token. Sessions default to one hour and can extend to the role's configured maximum (up to 12 hours). When the session expires the credentials are useless — nothing to revoke, nothing leaked forever.
Assuming a role is a two-sided handshake, and the exam tests both sides:
- The role's trust policy (a resource-based policy on the role) must name the caller in its
Principalelement and allowsts:AssumeRole. - The caller's identity-based policy must allow
sts:AssumeRoleon the role's ARN.
Miss either side and the assumption fails. Cross-account access is the classic scenario: a developer in account A assumes a role in account B — account B's role trusts account A, account A grants the developer sts:AssumeRole, and the developer temporarily operates in account B with only that role's permissions.
STS variants map to federation: AssumeRoleWithSAML serves enterprise IdPs, and AssumeRoleWithWebIdentity serves OIDC tokens — the call Cognito identity pools make on your behalf.
Finally, know when to attach versus assume. Compute running your code gets a role attached: a Lambda execution role, an EC2 instance profile, an ECS task role — the platform handles assumption and credential refresh automatically. You assume a role explicitly for cross-account access, temporary elevation, or federation flows.
Programmatic access: roles beat access keys
Programmatic access to AWS comes in exactly two flavors: long-term access keys (an access key ID plus secret) and temporary credentials from an assumed role. On the exam the rule is absolute: any answer that embeds access keys in source code, environment files, AMIs, or container images is wrong. The correct answer attaches a role to the compute — instance profile on EC2, execution role on Lambda, task role on ECS — and lets the platform deliver rotating temporary credentials. Long-term keys survive only for narrow cases such as local development or systems genuinely outside AWS, and even then federation is preferred.
The SDKs and CLI locate credentials through the credential provider chain, checking sources in order and stopping at the first hit: credentials set explicitly in code, then environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), then the shared credentials file (~/.aws/credentials), then web-identity tokens, then container credentials on ECS, and finally the EC2 instance metadata service. This is why role-based designs need zero credential code — the chain reaches the instance or task role automatically. It is also a debugging answer: stale keys in an environment variable silently win over the instance role because they sit earlier in the chain.
Every AWS API request is authenticated with Signature Version 4 (SigV4): the SDK derives a signing key from your secret key, signs the canonical request, and sends the signature in the Authorization header — temporary credentials add the X-Amz-Security-Token header. You almost never hand-roll SigV4; know that it is what "signed request" means, and that presigned S3 URLs are SigV4 signatures carried in the query string.
Defining permissions for IAM principals
Permissions for IAM principals come from two policy families. Identity-based policies attach to a user, group, or role and state what that principal may do. Resource-based policies attach to the resource itself — S3 bucket policies, SQS queue policies, Lambda function policies, API Gateway resource policies, KMS key policies — and carry a Principal element stating who may act on it. Resource-based policies are how you grant access to another account or another AWS service without creating a role to assume.
Evaluation logic is a guaranteed exam topic. Everything starts as an implicit deny; an explicit Allow in any applicable policy grants access; an explicit Deny anywhere overrides every allow, full stop. Within one account, permission can come from either an identity-based or a resource-based policy. Across accounts, both sides must allow: the resource's policy must grant the external principal, and that principal's own identity-based policy must grant the action. "Explicit deny wins" resolves most tricky policy questions in one step.
Policies come in three packages: AWS managed (maintained by AWS, convenient but broad), customer managed (you author, version, and reuse them — the least-privilege workhorse), and inline (embedded in a single principal and deleted with it — for strict one-to-one policies). Least privilege means scoping all three dimensions: specific Action entries instead of wildcards, specific resource ARNs instead of *, and Condition keys — source VPC, TLS-only via aws:SecureTransport, tag matching — to narrow the circumstances further.
Tip. Expect scenario questions that hinge on one phrase: "authenticate users" or "user directory" points to a user pool; "access AWS services", "temporary credentials", or "guest users" points to an identity pool; "existing corporate SAML IdP" means federation, never new accounts. Any option that embeds long-term access keys in code, configuration, or an AMI is wrong — the answer is an IAM role with temporary STS credentials. For API protection, match the mechanism to the caller: Cognito authorizer for user pool JWTs, Lambda authorizer for custom schemes, IAM with SigV4 for AWS-native callers. Policy questions almost always resolve with "explicit deny wins" and the cross-account both-sides-must-allow rule.
- User pool = authentication (who are you, JWTs); identity pool = authorization to AWS (temporary credentials).
- Only identity pools support unauthenticated guest access to AWS services.
- ID token = identity claims, access token = authorize API calls, refresh token = renew both — and it never goes to your API.
- IAM roles with temporary STS credentials always beat long-term access keys embedded anywhere.
- AssumeRole needs two allows: the role's trust policy names the caller, and the caller can sts:AssumeRole.
- Explicit deny beats every allow; anything not explicitly allowed is implicitly denied.
- Cognito authorizer = user pool JWTs with no code; Lambda authorizer = custom logic (cached); IAM auth = SigV4 for AWS callers.
- API keys and usage plans meter clients — they are never an authentication mechanism.
Frequently asked questions
What is the difference between a Cognito user pool and an identity pool?
A user pool is a managed user directory that authenticates people — sign-up, sign-in, MFA — and issues ID, access, and refresh JWTs. An identity pool authorizes access to AWS services: it exchanges an identity token (from a user pool, social, SAML, or OIDC provider) for temporary AWS credentials through STS. They are commonly combined, and only identity pools support unauthenticated guest identities.
Which Cognito token should my application send to an API?
Send the access token — it exists to authorize requests and carries OAuth scopes and group membership. The ID token carries identity claims like name and email for your app's own use, though an API Gateway Cognito authorizer without OAuth scopes validates the ID token. The refresh token is only ever exchanged with Cognito itself for fresh tokens and must never be sent to your API.
How does code running on EC2 or Lambda get AWS credentials without access keys?
Attach an IAM role to the compute — an instance profile on EC2, an execution role on Lambda, a task role on ECS. The platform continuously delivers short-lived STS credentials for that role, and the SDK's credential provider chain finds them automatically, so no credential appears in code, configuration, or images, and rotation is handled for you.
When should I use a Lambda authorizer instead of a Cognito authorizer?
Use a Lambda authorizer when authorization logic doesn't fit Cognito's JWT validation: third-party or legacy tokens, an external OAuth server, signature schemes in custom headers, or lookups against your own data. If users authenticate through a Cognito user pool, the built-in Cognito authorizer validates their JWTs with no code to run. Remember Lambda authorizer results are cached (five minutes by default), so permission changes can lag briefly.
What is required for cross-account access to an AWS resource?
Both accounts must allow it. Either the resource carries a resource-based policy granting the external principal while that principal's identity-based policy grants the action, or the caller assumes a role in the target account — which requires the role's trust policy to name the caller and the caller to hold sts:AssumeRole permission. In every variant, a single explicit deny anywhere overrides all allows.
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.