SaveMyCert
Log in
5 of 5 free questions left today·for unlimited practice
Security

Authentication and Authorization on AWS: Cognito, IAM, and STS

15 min readDVA-C02 · SecurityUpdated

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.

What you’ll learn
  • 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.

QuestionUser poolIdentity pool
Core jobAuthentication — who is this user?Authorization — temporary AWS credentials
What it issuesID, access, and refresh JWTsAccess key, secret key, and session token
Backed byIts own managed user directoryIAM roles assumed through AWS STS
Unauthenticated guestsNoYes — 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 Principal element and allow sts:AssumeRole.
  • The caller's identity-based policy must allow sts:AssumeRole on 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.

Securing APIs: API Gateway authorizers compared

API Gateway gives you three ways to decide who may invoke an API, and choosing among them is a recurring exam question.

MechanismValidatesChoose it when
Cognito user pool authorizerJWTs issued by a user poolUsers sign in through Cognito — zero authorization code to write or run
Lambda authorizerAnything your code can checkCustom auth: third-party or legacy tokens, external OAuth servers, header or parameter schemes
IAM authorizationSigV4-signed requestsCallers are AWS principals — internal services, other accounts, CLI and SDK clients

The Cognito authorizer checks token signature, expiry, and issuer natively on REST APIs; HTTP APIs offer the equivalent JWT authorizer, which accepts any OIDC-compliant issuer. A Lambda authorizer runs your function before the backend: the TOKEN type receives a bearer token, the REQUEST type receives headers, query strings, and request context; it returns an IAM policy (or a simple allow/deny) that API Gateway enforces and caches — five minutes by default — so authorization logic doesn't execute on every request. Remember that cache when a revoked user seems to retain access briefly. IAM authorization requires callers to sign requests with SigV4 and hold execute-api:Invoke permission, making it the natural fit for service-to-service calls.

One trap to spot: API keys and usage plans are not authentication. They meter and throttle known clients; they don't prove identity. An answer that "secures" an API with API keys alone is a distractor.

Application-level and cross-service authorization

Application-level authorization picks up where IAM stops: IAM decides whether a request may reach your service, while your application decides what a specific user may do inside it. The standard pattern is role-based access control (RBAC) with Cognito groups: assign users to groups such as admins or editors, and the membership arrives in the cognito:groups claim of the ID and access tokens. Your Lambda or backend reads the claim and branches — no extra identity lookup. For AWS-credential access, an identity pool can map groups to different IAM roles, so admins assume a more privileged role than viewers.

For fine-grained, per-user data access, push identity into the policy itself. A classic: an identity pool role for DynamoDB whose policy uses the dynamodb:LeadingKeys condition with ${cognito-identity.amazonaws.com:sub}, so each user can touch only items whose partition key is their own identity ID. The same variable scopes S3 access to a per-user prefix — an access-control list enforced by IAM itself.

Cross-service authentication in microservices follows two rules. For service-to-service calls: every service runs under its own least-privilege execution role, and internal APIs use IAM authorization — the calling service's role is granted execute-api:Invoke, requests are SigV4-signed, and no shared secrets exist to rotate or leak. For user context: propagate the caller's JWT with the request and validate it at each hop (or validate once at the edge and forward verified claims), so downstream services authorize as the user, not merely as the upstream service.

Walk the full flow for a photo-sharing app: the user signs in against the user pool and receives JWTs; API Gateway's Cognito authorizer validates the access token; the backing Lambda writes metadata to DynamoDB using its execution role; and for the image itself, the app exchanges the token at the identity pool and uploads directly to S3 with temporary credentials scoped to the user's own prefix. Four authorization decisions, each made by the right layer.

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.

Key takeaways
  • 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.

Test yourself on this topic
Practice questions with full explanations.
Practice now

Sign up free to mark lessons complete, bookmark topics and track your exam readiness.