skip to content

A team in a different AWS account must call your Amazon API Gateway REST API that uses `AWS_IAM` authorization. What has to be in place on both sides, and what can the API's resource policy express that an identity policy cannot?

level: seniorimportance: nice to knowfreq 36%

answer

  1. signed for the execute-api service
  2. both sides must allow, across accounts
  3. explicit deny always wins
  4. resource policy adds network conditions
  5. redeploy the stage to apply it

basics

~20 s

Cross-account access needs both sides to allow it: the caller's IAM identity policy must permit execute-api:Invoke on your method ARN, and your API's resource policy must allow their principal. The resource policy adds network conditions such as source IP, VPC endpoint or organization ID.

solid answer

~50 s

The caller signs the request with SigV4 using credentials from a role in their own account, for the `execute-api` service. Two allows are then required: an identity-based policy in their account permitting `execute-api:Invoke` on the ARN `arn:aws:execute-api:<region>:<your-account>:<api-id>/<stage>/<METHOD>/<path>`, and a resource policy on your API allowing that principal. Cross-account access needs both — within a single account the two are evaluated together and an allow on either side suffices — and an explicit `Deny` on either side always wins. The resource policy is also where you express conditions no identity policy in someone else's account could enforce: `aws:SourceIp` ranges, `aws:SourceVpc` or `aws:SourceVpce` for private access, and `aws:PrincipalOrgID` to admit any account in your organization. Two operational traps: a resource-policy change only takes effect after you redeploy the stage, and HTTP APIs do not support resource policies at all.

code

json · 14 lines
json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::222222222222:role/PartnerCaller" },
      "Action": "execute-api:Invoke",
      "Resource": "arn:aws:execute-api:us-east-1:111122223333:a1b2c3d4e5/prod/GET/orders",
      "Condition": {
        "IpAddress": { "aws:SourceIp": ["203.0.113.0/24"] }
      }
    }
  ]
}

go deeper

for a junior

Know that AWS_IAM authorization means the caller signs the request with AWS credentials and needs permission for execute-api:Invoke on the API's method ARN.

for a middle

Explain the execute-api ARN structure and that a resource policy is a second, resource-attached policy carrying a Principal element, unlike an identity policy.

for a senior

Diagnose the two-sided consent rule for cross-account calls, name the conditions only a resource policy can enforce, and remember the redeploy step that makes a policy change take effect.

for a principal

Own the partner-access model: direct cross-account grants versus a published assumable role, how organization-wide conditions keep policies from rotting, and what the API-type choice costs you when resource policies are required.

## How the call is actually authenticated When a REST API method's authorization type is `AWS_IAM`, API Gateway expects the request to carry a SigV4 `Authorization` header — an `AWS4-HMAC-SHA256` signature computed over the canonical request with a credential scope naming the region and the service `execute-api`. The caller's secret never travels; only the derived signature does. Every AWS SDK does this for you when told to sign for `execute-api`, and short-lived role credentials additionally carry `X-Amz-Security-Token`. Once the signature identifies the principal, API Gateway asks the authorization question: may this principal perform `execute-api:Invoke` on this method? ## The two policies, and why cross-account needs both The resource being invoked is named by an execute-api ARN: ``` arn:aws:execute-api:us-east-1:111122223333:a1b2c3d4e5/prod/GET/orders ``` where `a1b2c3d4e5` is the API id, `prod` the stage, then the HTTP method and the resource path. Wildcards are allowed in the trailing segments — `.../prod/*/*` covers the whole stage. Two policies can speak to that resource: - The **identity-based policy** attached to the caller's IAM role, living in *their* account. - The **API's resource policy**, a policy document attached to the API itself, living in *your* account and carrying a `Principal` element. Within one account, these are evaluated together: an allow from either, with no explicit deny anywhere, is enough. **Across accounts, both are required** — your resource policy must name their principal, and their administrator must grant their role the action. Neither team can unilaterally open the door, which is exactly the property you want. And an explicit `Deny` on either side ends the evaluation immediately, no matter what the other says. This is the source of the most common cross-account symptom: an admin in the caller's account grants `execute-api:Invoke` on everything, tests, and still gets `403`. The missing half is in the API owner's account. ## What only the resource policy can express A resource policy is attached to *your* API, so it can impose conditions on callers you do not administer: - **`aws:SourceIp`** — restrict an internet-facing API to known office or partner CIDR ranges. - **`aws:SourceVpc` / `aws:SourceVpce`** — restrict a private API to traffic arriving through a specific VPC or a specific interface endpoint, so the API is unreachable from the internet regardless of IAM. - **`aws:PrincipalOrgID`** — admit any principal in your AWS organization without enumerating account ids, which stops the policy from rotting as accounts are added. There is a further capability that surprises people: a resource policy applies **even to methods whose authorization type is `NONE`**. An unauthenticated API can still be fenced to a CIDR range or a VPC endpoint by resource policy alone. No identity policy can do that, because there is no identity. ## The operational traps **Deployment.** A resource policy change on a REST API does not take effect until you deploy the API to the stage. Editing the policy and testing immediately gives you the old behaviour and a confusing debugging session. **API type.** HTTP APIs do not support resource policies. If your access model depends on `aws:SourceVpce` or `aws:PrincipalOrgID` at the gateway, that requirement pushes you to a REST API — or moves the control to a different layer. **Signing mistakes.** Unsigned requests to an `AWS_IAM` method fail with a missing-authentication-token error rather than a policy denial, and clock skew or a mismatched region in the credential scope produces signature errors, not `403`s. Reading the exact error message tells you whether you have an authentication problem or an authorization problem — a distinction worth stating explicitly in an interview. ## The alternative shape Instead of granting the foreign principal directly, you can publish a role in *your* account that the partner assumes, and have them sign with the resulting temporary credentials. Then the caller is a principal you own, and your resource policy is simple. Which shape to pick is a real design conversation: direct cross-account grants keep the audit trail on their identity, while a published role gives you a single place to revoke and to attach conditions. Either way, the same two-sided consent applies — their side must allow the call, and your side must allow the caller.

  • The partner's admin grants `execute-api:Invoke` on `*` and the call still returns 403. What is missing?
    The API owner's half. Cross-account invocation requires an allow in the API's resource policy naming that principal as well as the identity-based allow in the caller's account. Check the resource policy exists, names the right principal and resource ARN, and — easy to miss — that the stage has been redeployed since the policy was last changed.
  • How would you restrict an API to callers inside your AWS organization without listing account ids?
    Use a resource policy condition on `aws:PrincipalOrgID` matching your organization id. Every principal in any member account satisfies it, and accounts added later are covered automatically, so the policy does not rot. Pair it with the specific `execute-api:Invoke` resource ARNs you intend to expose rather than a blanket wildcard.
  • Why can a resource policy protect a method whose authorization type is NONE, when an identity policy cannot?
    Because it is attached to the resource rather than to a principal, so it is evaluated even when the request carries no identity at all. That lets you fence an unauthenticated API to a CIDR range with `aws:SourceIp` or to an interface endpoint with `aws:SourceVpce`. An identity policy has nothing to attach to when there is no authenticated caller.

saying these in an interview costs you the question

  • Thinks an identity-policy allow alone opens cross-account access
  • Believes a resource-policy allow overrides an explicit deny elsewhere
  • Forgets that a resource-policy change needs a stage deployment
  • Expects resource policies to exist on HTTP APIs
  • Confuses a signature failure with an authorization denial

context