Domain 2 of 4 · Chapter 1 of 3

Authentication and Authorization

The model: authentication establishes identity, authorization runs one allow + no-deny evaluation

"Why was this call allowed or denied?" is the security question DVA-C02 asks most, and two rules answer nearly all of it: a request needs an Allow with no explicit Deny anywhere (a single Deny beats every Allow), and proving who the caller is (authentication) is always a separate step from deciding what they may do (authorization). Read an Effect/Action/Resource statement against those two rules and you can trace any deny scenario and pick the right credential for each job. Two distinct questions are always answered in order. Authentication (authN) answers who is the caller? It verifies an identity, whether that is a SigV4-signed AWS request (SigV4 is AWS's request-signing scheme, in which the SDK signs each call with the caller's credentials), a JSON Web Token (JWT) validated by Amazon Cognito, or a federated login. Authorization (authZ) answers what may that caller do? It is a separate policy decision, so a perfectly authenticated caller can still be denied. Conflating the two is the single most common exam distractor: a Cognito ID token[1] proves who you are and must never be used to authorize an API call, while an OAuth 2.0 access token[2] or a set of temporary AWS credentials is what grants access.

Authorization on AWS is one uniform model regardless of where the permission is written: allow + no explicit deny. A request is permitted only when some policy Allows the requested action on the requested resource AND no policy contains an explicit Deny. Two policy families feed this evaluation:

  • Identity-based policy, attached to the IAM principal (user, group, or role) making the call. Multiple attached policies are additive (unioned).
  • Resource-based policy, attached to the target resource itself (an S3 bucket policy, a Lambda resource policy, an SQS queue policy, a KMS key policy).

The one rule that separates same-account from cross-account access, stated once here, referenced throughout the page:

  • Same account: an Allow in either the identity-based policy or the resource-based policy is sufficient. AWS takes the union of the two. (KMS is the notable exception: its key policy must allow the principal explicitly.)
  • Cross account: you need both, an Allow in the caller's identity-based policy in its own account and an Allow in the resource-based policy in the resource's account.

Explicit Deny always wins. No matter how many Allows exist, a single explicit Deny in any applicable policy denies the request. This is why an "admin" principal can still be blocked from an S3 bucket whose bucket policy denies its ARN. The order is summarized in the figure: check for an explicit Deny first, then apply the same-/cross-account grant rule, then default-deny anything not allowed. The official IAM policy evaluation reference[3] is the canonical source for this logic.

Authenticated request Explicit Deny in any policy? yes no Grant rule: is the action allowed? same account: identity OR resource policy allows cross account: identity AND resource policy allow not allowed allowed ALLOW DENY (default)
From AWS policy evaluation logic: explicit Deny first, then the grant rule, then default-deny.

The application's own identity: IAM roles, STS temporary credentials, and least privilege

This section covers how the application authenticates itself to AWS, distinct from the end-user authentication of the next section. The rule is absolute: application code carries a role, never a long-lived access key. Embedding an access key ID and secret in code or config is the antipattern the exam punishes; instead you attach an IAM role to the compute and let the AWS SDK obtain rotating temporary credentials transparently. The figure groups the role-per-compute names under the single service that vends their credentials; the names are worth memorizing because the exam tests them as distractors:

  • EC2 instance profile, wraps an IAM role; the SDK fetches credentials from the Instance Metadata Service. Always enforce IMDSv2[4] (HttpTokens=required) to block SSRF-based credential theft.
  • ECS task role, the role the application inside the container uses for its AWS API calls. Do not confuse it with the task execution role, which the ECS agent uses to pull images from ECR and ship logs to CloudWatch. Putting application permissions on the execution role is a classic wrong-layer mistake.
  • Lambda execution role, set at function creation; credentials are exposed via environment variables and the SDK auto-discovers them.

Where temporary credentials come from: AWS STS. All of the above are vended by AWS Security Token Service (STS)[5] behind the scenes. When code must switch identity (cross-account access or privilege elevation) it calls sts:AssumeRole[6] explicitly. Commit these numbers; they are directly testable:

  • Default session length: 3600 seconds (1 hour) when DurationSeconds is omitted.
  • DurationSeconds range: 900 seconds (15 minutes) up to the role's configured maximum session duration, which can be set from 1 to 12 hours.
  • Role chaining (using one assumed role to assume another) caps the session at 1 hour regardless of the role's maximum, a frequently-tested gotcha.

Who is allowed to assume a role is governed by the role's trust policy, its resource-based policy. A subtle, predictably-misread detail: a trust policy Principal of arn:aws:iam::<account-id>:root denotes account-level trust. The whole account is a trusted entity (account-level delegation), NOT the AWS account root user. It means every IAM principal in that account whose own identity policy also permits sts:AssumeRole may assume the role. To trust a single principal, name it explicitly (e.g. arn:aws:iam::<account-id>:role/Build). For third-party SaaS that assume a role in your account, require sts:ExternalId in the trust policy to defend against the confused-deputy problem.

Least privilege bounds, it does not just grant. Each policy should grant only the actions and resources an identity needs, narrowed with Condition keys where possible, per the IAM best-practices guidance[7]. Note the asymmetry the exam probes: where a ceiling is present (a permissions boundary on the principal, or a session policy[8] passed to AssumeRole) effective permissions become the intersection of the grants and the ceiling, so a session can never exceed what the role's identity policy already allows. Grants union; ceilings intersect.

AWS STS vends rotating temporary credentials carry an IAM role, never a long-lived access key EC2 instance profile wraps an IAM role; SDK reads credentials via IMDSv2 ECS roles (keep the two apart) task role app inside the container task execution role agent: pull image, ship logs Lambda execution role set at function creation; SDK auto-discovers credentials
Role-per-compute taxonomy: EC2 instance profile, ECS task role vs task execution role, and Lambda execution role, all sourced from AWS STS.

End-user authentication: Cognito user pools (authN) vs identity pools (federated AWS credentials)

When the application must sign in its own end users, the two Amazon Cognito halves answer the two different questions from the model section, and the exam relentlessly tests which one to reach for. Keep them apart by their job:

  • A Cognito user pool[9] is a user directory that performs authentication. Users sign in and the pool issues three tokens. The two you must distinguish:
    • ID token, carries identity claims (email, name, sub). It proves who the user is; consume it in your app to learn about the user. It is not an authorization credential.
    • Access token, an OAuth 2.0[10] bearer token carrying scopes/groups. This is what you send to a protected API to be authorized. (A refresh token, valid much longer, is used only to mint new ID/access tokens.)
  • A Cognito identity pool[11] (a.k.a. federated identities) performs authorization to AWS: it exchanges a verified login (from a Cognito user pool, or a third-party IdP like Google, Apple, Facebook, or any SAML/OIDC provider) for temporary AWS credentials from STS, scoped by an IAM role. Use this only when a signed-in user must call an AWS service (S3, DynamoDB) directly from the client.

The decision rule, stated once: need to authenticate a user and protect your own API? → user pool (validate the access token). Need to give that user scoped AWS credentials to hit an AWS service directly? → identity pool (which returns STS credentials). The figure traces the full chain when both are used together: user pool authenticates and issues JWTs, then the identity pool swaps the token for STS credentials. Identity pools support authenticated and unauthenticated (guest) roles, letting you grant limited access to users who have not signed in.

Tokens and standards. Cognito user pools are an OIDC/OAuth 2.0 provider: the ID token is an OIDC construct, the access token is OAuth 2.0, and the Cognito Hosted UI / managed login[12] implements the standard authorization-code and other OAuth 2.0 grant flows so you do not hand-roll sign-in. The ID and access tokens are JWTs you can verify against the pool's public JWKS; the refresh token is opaque and encrypted (readable only by the user pool), so you do not validate it yourself.

Client (end user) 1. sign in Cognito user pool authentication (authN) 2. ID token + access token (JWTs) Cognito identity pool exchanges token via STS 3. temporary AWS credentials STS credentials (scoped by IAM role) 4. call AWS service directly S3 / DynamoDB
Cognito split, per identity pools docs: user pool issues JWTs; identity pool swaps a token via STS for AWS credentials.

Protecting the API and exam patterns: API Gateway authorizers, OAuth 2.0 / OIDC, common traps

This section applies the model to the most-tested DVA-C02 surface, protecting an HTTP/REST API, and collects the distractors. The scope: deciding which authorizer gates a route, and which credential each one validates. The figure branches the three authorizer types by the credential each validates; the trap list after it is a flat checklist, so it stays in prose.

Amazon API Gateway authorizer types (authorizer overview[13]):

  • Cognito / JWT authorizer, declarative, no code. You point it at a Cognito user pool (REST API: Cognito authorizer) or any OIDC issuer (HTTP API: JWT authorizer); API Gateway validates the incoming bearer JWT's signature, expiry, and scopes/claims and allows or denies the route. Use the user pool access token here, not the ID token (the authN-vs-authZ rule from the model section). This is the default answer when sign-in is already Cognito.
  • Lambda authorizer (formerly custom authorizer), your function returns an allow/deny decision (an IAM policy document, or a simple boolean for HTTP APIs). Two event types:
    • TOKEN, receives only the bearer token from a single header; the right choice for validating an opaque or non-Cognito JWT.
    • REQUEST, receives the full request context (headers, query string, path, stage variables); needed when the decision depends on more than one input. Lambda authorizer results can be cached by TTL to avoid invoking the function on every request.
  • IAM authorization (AWS_IAM), the caller signs the request with SigV4 using IAM credentials, and API Gateway evaluates it through the same identity/resource-policy model as any AWS API call. Best for service-to-service or internal callers that already hold IAM credentials, not for browser/mobile end users.

OAuth 2.0 / OIDC, in one frame. OIDC[10] is the identity (authN) layer on top of OAuth 2.0 (the authorization framework). Mapped to AWS: the ID token is the OIDC artifact (who the user is); the access token is the OAuth 2.0 artifact carrying scopes (what the bearer may do). A JWT/Cognito authorizer enforces the access token's scopes at the edge. For per-request, fine-grained decisions beyond what scopes express ("can this user edit this document?"), validate claims in code or use Amazon Verified Permissions[14]. IAM and edge authorizers gate the route, not row-level business rules.

Exam traps, distilled:

  • ID token used to authorize an API call. Wrong, the ID token proves identity; send the access token to the authorizer. The single most common Cognito distractor.
  • Long-lived IAM access keys embedded in code on EC2/ECS/Lambda. Wrong, attach a role (the application's own identity section); the SDK gets rotating STS credentials.
  • User pool vs identity pool swapped. "Let the signed-in user write to S3 directly" needs an identity pool (STS credentials), not just a user pool. "Sign users in and protect my API" needs the user pool access token.
  • Workforce vs application end users. Employees reaching AWS accounts → IAM Identity Center; application customers → Cognito user pool. Reaching for one where the other belongs is a deliberate distractor.
  • DurationSeconds and role chaining. Default 1 hour, range 15 minutes to the role max (1–12 hours), but chaining caps at 1 hour, restate of the STS facts from the application's own identity section.
  • Principal: "*" on a resource or trust policy = anyone, any account. Almost always wrong; scope to a principal ARN, an account, or aws:PrincipalOrgID.
Which authorizer gates the route? Cognito / JWT authorizer no code: validates access token Lambda authorizer your code; cacheable by TTL IAM authorization (AWS_IAM) validates SigV4-signed request Cognito sign-in already in place TOKEN single bearer header REQUEST full request context service-to-service / internal
Picking an API Gateway authorizer by the credential it checks: Cognito/JWT (access token), Lambda authorizer (TOKEN vs REQUEST), or IAM (SigV4).
MechanismPrimary jobWhat it issues / returnsTypical use in app code
Cognito user poolAuthentication (user directory)OIDC ID token + OAuth 2.0 access token + refresh token (JWTs)Sign users in; send the JWT to a protected API
Cognito identity poolAuthorization to AWS (federation)Temporary AWS credentials from STS, scoped by an IAM roleLet a signed-in user call S3/DynamoDB directly with limited rights
IAM role + STSAuthorization for the app's own AWS callsAuto-rotating temporary credentials (default 1 hour)Lambda/ECS/EC2 calls AWS APIs without static keys; AssumeRole for cross-account
API Gateway JWT/Cognito authorizerAuthorization at the API edge (declarative)Allow/deny after validating a bearer JWT's claims and scopesGate routes by token validity without custom code
API Gateway Lambda authorizerAuthorization at the API edge (custom)An IAM policy or allow/deny your function computes from the requestValidate non-JWT bearer tokens or apply bespoke logic

Decision tree

Is the caller your app's own code calling AWS APIs (not an end user)? Yes IAM role + STS execution/task/instance role; AssumeRole No Do signed-in users need temporary AWS creds to call AWS directly? Yes Cognito identity pool exchanges token for STS credentials No Must you authenticate users and run a sign-in directory? Yes Cognito user pool issues OIDC ID + OAuth access JWTs No Gating an API by a standard JWT's claims and scopes? Yes API Gateway JWT/Cognito authorizer declarative allow/deny, no custom code No API Gateway Lambda authorizer non-JWT tokens / bespoke logic

Sharp facts the exam loves — give these one last read before exam day.

Cheat sheet

Sharp facts the exam loves — scan these before test day.

Authentication proves who you are; authorization decides what you may do

Authentication and authorization are two separate steps, and an exam scenario almost always turns on telling them apart. Authentication verifies an identity (a SigV4-signed AWS request, a JWT validated by Cognito, or a federated login) while authorization is a distinct policy decision about what that verified identity may do, so a perfectly authenticated caller can still be denied. The practical consequence: a credential that proves identity (a Cognito ID token) is not the credential that grants access (an OAuth 2.0 access token or temporary AWS credentials).

Trap Treating a successful sign-in as proof a call is allowed: authorization is evaluated separately, so an authenticated principal can still hit an explicit deny.

An explicit Deny always beats any Allow

AWS authorization is allow + no-explicit-deny: a request is permitted only when some policy allows the action on the resource AND no policy anywhere contains an explicit Deny. A single explicit Deny overrides every Allow, which is why even an admin principal can be blocked from an S3 bucket whose bucket policy denies its ARN. Anything not explicitly allowed is denied by default (implicit deny), but that default is weaker than an explicit deny. Only an explicit Allow can overcome an implicit deny, and nothing overcomes an explicit one.

Trap Assuming an Allow on an admin or broadly-permissive policy can override an explicit Deny elsewhere, when a single explicit Deny wins over every Allow in the evaluation.

6 questions test this
Same account: an Allow in either the identity or the resource policy is enough

For a request within one account, AWS takes the union of the identity-based policy (attached to the user/role) and the resource-based policy (attached to the S3 bucket, Lambda function, SQS queue, etc.): an Allow in either one (or both) grants the action. Identity-based policies attached to a principal are themselves additive, so multiple attached policies combine. The notable exception is KMS, where the key policy must allow the principal explicitly for IAM permissions to take effect.

Trap Assuming KMS follows the union rule: a KMS key policy must grant the principal explicitly, so an identity policy alone is not sufficient.

Cross-account access needs an Allow in BOTH accounts

When the principal and the resource live in different accounts, the union rule no longer applies: you need an Allow in the caller's identity-based policy in its own account AND an Allow in the resource-based policy (or role trust policy) in the resource's account. Both sides must opt in, either one alone fails. This is the single behavioral difference between same-account and cross-account evaluation.

Trap Granting only the resource-based policy in the target account and expecting cross-account access to work: the caller's own account must also allow the action on its identity.

10 questions test this
Give compute a role, never embed long-lived access keys

Application code should carry an IAM role attached to the compute, not an access key ID and secret baked into code or config. Embedding static keys is the antipattern the exam punishes. With a role, the AWS SDK transparently obtains temporary credentials that auto-rotate and expire, so there is no long-lived secret to leak or rotate manually. Reach for the role mechanism that matches the compute: instance profile (EC2), task role (ECS), or execution role (Lambda).

Trap Storing IAM access keys in environment variables or config files on EC2/ECS/Lambda: attach a role instead and let the SDK fetch rotating STS credentials.

Match the role type to the compute: instance profile, task role, execution role

Each compute service exposes role credentials differently and the names are tested as distractors. An EC2 instance profile wraps a role and the SDK fetches credentials from the Instance Metadata Service. Enforce IMDSv2 (HttpTokens=required) to block SSRF-based credential theft. A Lambda execution role is set at function creation and auto-discovered by the SDK. For ECS, the task role is what the application code inside the container uses for its own AWS calls, distinct from the task execution role, which only lets the ECS agent pull images from ECR and ship logs to CloudWatch.

Trap Putting the application's AWS permissions on the ECS task execution role: that role is for the agent (ECR pull, CloudWatch logs); app calls use the task role.

STS sessions default to 1 hour, range 15 min to 12 h

When code switches identity it calls sts:AssumeRole, which returns temporary credentials valid for DurationSeconds: 3600 seconds (1 hour) when omitted. The value may range from 900 seconds (15 minutes) up to the role's configured maximum session duration, which an admin can set anywhere from 1 to 12 hours. Requesting more than the role's maximum fails, so the role's ceiling caps the session.

1 question tests this
Role chaining caps the session at 1 hour regardless of the role max

Role chaining (using one already-assumed role to assume a second role) always caps the new session at 1 hour, even if the target role's maximum session duration is set to 12 hours. This is a frequently tested gotcha: the 12-hour maximum applies to a directly assumed role, not to one reached by chaining from temporary credentials.

Trap Expecting a chained AssumeRole to honor a 12-hour role maximum: chaining silently caps the session at 1 hour.

3 questions test this
A trust policy Principal of :root means the whole account, not the root user

Who may assume a role is governed by the role's trust policy (its resource-based policy). A trust policy Principal of arn:aws:iam::<account-id>:root denotes account-level delegation: the entire account is trusted, NOT the AWS account root user. It means any IAM principal in that account whose own identity policy also permits sts:AssumeRole may assume the role. To trust a single entity, name it explicitly, e.g. arn:aws:iam::<account-id>:role/Build.

Trap Reading :root in a trust policy as the account's root user: it is account-wide delegation; the assuming principal still needs its own AssumeRole permission.

Require an ExternalId when a third party assumes your role

When a third-party SaaS assumes a role in your account, add a Condition on sts:ExternalId in the role's trust policy and share that agreed value with the vendor. The vendor must pass the matching ExternalId on AssumeRole or the call fails. This defends against the confused-deputy problem, where an attacker tricks the trusted third party into using its access on your behalf without knowing your secret value.

Trap Treating ExternalId as a secret password that authenticates the vendor, when it is a confused-deputy guard rather than a credential and offers no protection if the vendor is itself compromised.

8 questions test this
A permissions boundary or session policy caps permissions: it never grants them

Grants union, but ceilings intersect. Where a ceiling is present (a permissions boundary on the principal, or a session policy passed to AssumeRole) effective permissions become the intersection of the identity's grants and that ceiling, so a session can never exceed what the role's own policy already allows. A boundary with no matching grant in the identity policy authorizes nothing; you still need an explicit Allow in the identity policy for any action.

Trap Treating a permissions boundary or session policy as a way to add permissions: it only narrows; the underlying identity policy must still grant the action.

16 questions test this
A Cognito user pool authenticates users and issues three JWTs

A Cognito user pool is a user directory that performs authentication: users sign in and the pool returns an ID token, an access token (both JWTs you can verify against the pool's public JWKS), and a refresh token (opaque and encrypted, used only to mint new ID/access tokens). It answers "who is this user?": reach for a user pool when you need to sign your own application's end users in and protect your own API. The Hosted UI / managed login implements standard OAuth 2.0 grant flows (such as authorization code) so you do not hand-roll sign-in.

Trap Assuming the refresh token is a JWT you can verify against the JWKS like the others, when it is opaque and encrypted, usable only to mint new ID/access tokens.

Send the access token to authorize an API, not the ID token

Of the user-pool JWTs, the ID token carries identity claims (email, name, sub) and proves who the user is: consume it in your app to learn about the user, never to authorize a call. The access token is the OAuth 2.0 bearer token carrying scopes and groups, and it is what you present to a protected API to be authorized. The refresh token only mints new ID/access tokens and is never sent to your API.

Trap Sending the Cognito ID token to an API authorizer: it proves identity, not authorization; pass the access token, whose scopes the authorizer checks.

1 question tests this
Use a Cognito identity pool to hand a signed-in user temporary AWS credentials

A Cognito identity pool (federated identities) performs authorization to AWS: it exchanges a verified login (from a user pool, or a third-party IdP like Google, Apple, Facebook, or any SAML/OIDC provider) for temporary AWS credentials from STS, scoped by an IAM role. Reach for it only when a signed-in user must call an AWS service such as S3 or DynamoDB directly from the client. Identity pools also support an unauthenticated (guest) role, granting limited access to users who have not signed in.

Trap Reaching for an identity pool just to authenticate users or protect your own API: that is the user pool's job; the identity pool exists to vend AWS credentials.

User pool authenticates, identity pool federates to AWS: don't swap them

The decision rule the exam relentlessly tests: need to authenticate a user and protect your own API? Use a user pool and validate the access token. Need to give that user scoped AWS credentials to hit an AWS service directly? Use an identity pool, which returns STS credentials. Used together, the user pool authenticates and issues JWTs, then the identity pool swaps a token via STS for AWS credentials.

Trap Picking a user pool for "let the signed-in user write to S3 directly": that needs an identity pool's STS credentials, not just a JWT.

Cognito is for application users; IAM Identity Center is for your workforce

Match the identity service to the audience. Amazon Cognito signs in an application's external end users (customers of your app). IAM Identity Center signs in your own workforce: employees needing access to AWS accounts and business applications. The two are not interchangeable, and a scenario that names one where the other belongs is a deliberate distractor.

Trap Using Cognito for employee access to AWS accounts (or Identity Center for app customers): Cognito = app end users, Identity Center = workforce.

A Cognito or JWT authorizer gates a route declaratively, no code

An API Gateway Cognito authorizer (REST API) or JWT authorizer (HTTP API) validates an incoming bearer JWT's signature, expiry, and scopes/claims and allows or denies the route with no custom code. Point it at a Cognito user pool or any OIDC issuer and pass the access token. This is the default answer when sign-in is already Cognito and you only need standard token validation at the edge.

Trap Writing a Lambda authorizer to validate a standard Cognito/OIDC JWT, when the built-in Cognito or JWT authorizer does signature, expiry, and scope checks declaratively with no code.

2 questions test this
Use a Lambda authorizer for non-JWT tokens or bespoke logic

A Lambda authorizer (formerly custom authorizer) runs your function to return an allow/deny decision (an IAM policy document, or a simple boolean for HTTP APIs) and is the right choice for validating an opaque or non-Cognito token, or applying custom logic a declarative authorizer can't express. A TOKEN authorizer receives only the bearer token from a single header; a REQUEST authorizer receives the full request context (headers, query string, path, stage variables) and is needed when the decision depends on more than one input. Results can be cached by TTL to avoid invoking the function on every request.

Trap Choosing a TOKEN Lambda authorizer when the decision needs query strings or multiple headers: TOKEN sees only one header's token; use REQUEST for full request context.

8 questions test this
Use AWS_IAM authorization for SigV4 callers, not browser end users

API Gateway IAM authorization (AWS_IAM) requires the caller to sign the request with SigV4 using IAM credentials, and the request is evaluated through the same identity/resource-policy model as any AWS API call. It fits service-to-service or internal callers that already hold IAM credentials, not browser or mobile end users, who carry JWTs and should use a Cognito/JWT or Lambda authorizer instead.

Trap Choosing AWS_IAM authorization for browser or mobile end users, who hold JWTs rather than signable IAM credentials; SigV4/IAM auth fits service-to-service callers.

OIDC is the identity layer on top of OAuth 2.0's authorization framework

OAuth 2.0 is the authorization framework (delegating access via tokens); OIDC adds the identity (authentication) layer on top. Mapped to Cognito: the ID token is the OIDC artifact answering who the user is, and the access token is the OAuth 2.0 artifact carrying scopes for what the bearer may do. A JWT/Cognito authorizer enforces the access token's scopes at the API edge.

Trap Treating OAuth 2.0 itself as an authentication protocol, when OAuth 2.0 handles authorization and OIDC is the identity layer added on top to authenticate the user.

Edge authorizers gate the route; fine-grained rules go in code

API Gateway authorizers and IAM gate whether a caller may reach a route or action: they do not express per-resource business rules like "can THIS user edit THIS document?". For fine-grained, per-request decisions beyond what scopes capture, validate claims in your application code or use Amazon Verified Permissions. Don't expect a scope check at the edge to enforce row-level ownership.

Trap Expecting an edge scope or JWT authorizer to enforce row-level ownership like "can this user edit this document", when per-resource rules belong in code or Verified Permissions.

Apply least privilege and prefer MultiFactorAuthPresent for fresh-MFA checks

Each policy should grant only the actions and resources an identity needs, narrowed with Condition keys where possible. When gating a sensitive action on MFA, know the two condition keys differ: aws:MultiFactorAuthPresent is true only when the CURRENT credentials were obtained with MFA, while aws:MultiFactorAuthAge measures how long ago that MFA happened. Use the age key when you need recency, not just presence.

Trap Relying on aws:MultiFactorAuthAge to require MFA at all: the age key is absent (so a deny-on-absence can misfire) when no MFA occurred; aws:MultiFactorAuthPresent checks that MFA happened, the age key checks how recently.

Principal: "*" on a resource or trust policy means anyone, anywhere

A Principal of "*" on a resource-based or trust policy grants the action to any principal in any account (effectively public) and is almost always the wrong answer on a security scenario. Scope access instead to a specific principal ARN, a specific account, or aws:PrincipalOrgID to bound it to your organization. The exam dangles "*" as a tempting "it just works" option.

Trap Accepting Principal: "*" to make access "just work": it exposes the resource to every AWS account; scope to a principal ARN, account, or PrincipalOrgID.

ABAC compares aws:PrincipalTag to aws:ResourceTag in a StringEquals condition

Attribute-based access control grants access only when a tag on the calling principal matches a tag on the target resource. The policy uses a Condition with StringEquals comparing aws:ResourceTag/<Key> to the policy variable ${aws:PrincipalTag/<Key>} (for example team or Environment) so one policy scales to every team without per-team rules. When the principal is an IAM user rather than a tagged role, ${aws:username} is the matching variable against the resource's owner tag.

Trap Hard-coding a tag value as a literal string instead of the ${aws:PrincipalTag/...} policy variable: ABAC's whole point is the dynamic principal-vs-resource tag comparison.

5 questions test this
A Lambda authorizer must return principalId plus a policyDocument, with context values stringified

A REST API Lambda authorizer's output must include a principalId and a policyDocument (an IAM policy with execute-api:Invoke statements); omit either and API Gateway returns 500, not 403. Any context map it returns may hold only stringified primitives: you cannot set a JSON object or array as a context value. The backend reads those values via $context.authorizer.<key>.

Trap Returning a JSON object or array in the authorizer's context map: every context value must be a stringified primitive (string, number, boolean) or the request fails.

5 questions test this

Also tested in

References

  1. Using the Amazon Cognito user pool ID token
  2. Using the Amazon Cognito user pool access token
  3. IAM JSON policy evaluation logic
  4. Use IMDSv2 (configure the instance metadata service)
  5. AWS Security Token Service API Reference: Welcome
  6. AWS STS AssumeRole API
  7. Security best practices in IAM
  8. Policies and permissions in IAM (session policies)
  9. Amazon Cognito user pools
  10. Amazon Cognito user pools OIDC/OAuth 2.0 server contract reference
  11. Amazon Cognito identity pools
  12. Amazon Cognito user pool app integration (Hosted UI / managed login)
  13. Controlling and managing access to a REST API in API Gateway
  14. What is Amazon Verified Permissions?