Application authorization with Verified Permissions
Lesson 3 of 4 in Designing and Reviewing Access.
There is a line in every application that IAM cannot cross, and Amazon Verified Permissions exists on the other side of it.
IAM authorises AWS API calls. Can this principal call s3:GetObject on this bucket? Can it call dynamodb:Query on this table? Those are the only questions IAM is asked, and it answers them for every request to an AWS service.
Verified Permissions authorises your application's actions. May this user comment on that document? May this manager approve an expense over £10,000? May this customer view that other customer's order? IAM has no vocabulary for any of it — a DynamoDB table policy cannot express "the author of the parent thread, or a moderator, but only during business hours".
Historically that logic lived scattered through controllers and service classes: an if here, a role check there, a query filter somewhere else. Verified Permissions pulls it out into Cedar policies held in a managed policy store, so application authorization becomes something you can review, version, and reason about rather than something you grep for.
Cedar is the language, and it is open source — the same engine runs outside Verified Permissions if you need it embedded. A policy reads close to the sentence it encodes:
permit (
principal in Group::"regional-managers",
action == Action::"ApproveExpense",
resource in Region::"emea"
)
when { context.amount <= 10000 && context.mfa == true };
forbid (
principal,
action == Action::"ApproveExpense",
resource
)
when { resource.submittedBy == principal };
Four things to notice. The triple is always principal, action, resource, with context for everything else. when adds a requirement and unless adds an exception. forbid beats permit — the same precedence rule as IAM's explicit deny, which makes the second policy above a clean separation-of-duties control: nobody approves their own expense, regardless of group. And Cedar is designed for automated reasoning, so tools can analyse what a policy set permits rather than only testing examples.
The pieces around it: a policy store holds the policies; an optional schema declares your entity types, their attributes, and which actions apply to which resource types, so the console and SDK can validate policies as you write them; policy templates are parameterised policies instantiated per resource (one template, ten thousand "owner of this document" policies); and identity sources connect a Cognito user pool or an OIDC provider so IsAuthorizedWithToken can read the principal straight from a JWT.
| IAM | Amazon Verified Permissions | |
|---|---|---|
Authorises | Calls to AWS service APIs | Actions defined by your application |
Who asks | AWS, on every API request, automatically | Your code, explicitly, by calling IsAuthorized |
Language | IAM JSON policy documents | Cedar |
Vocabulary | Service actions and ARNs | Your entity types, attributes, and actions, declared in a schema |
Precedence rule | Explicit Deny beats Allow |
|
Typical question | "May this role read this bucket?" | "May this user edit this document?" |
If you try to use the other one | An IAM policy cannot express per-record, per-user business rules | Cedar cannot authorise an S3 or DynamoDB API call — AWS does not consult it |
One more organizational device the exam guide names alongside these: IAM paths.
A path is a slash-delimited namespace inside an IAM entity's ARN, chosen at creation and fixed thereafter:
arn:aws:iam::111122223333:role/teams/payments/deploy
arn:aws:iam::111122223333:role/teams/search/deploy
arn:aws:iam::111122223333:policy/boundaries/TeamBoundary
It turns a flat list of names into a hierarchy you can match with a wildcard, which is what makes delegated administration expressible: allow a team administrator to manage role/teams/payments/* and nothing else; allow iam:PassRole over one branch rather than over every role in the account. Without paths the same intent depends on a naming convention, and a naming convention is one typo away from granting more than you meant.
Two practical notes. Paths are not changeable after creation — plan them with the account structure, not after it. And you have already seen one: IAM Identity Center puts its generated roles under /aws-reserved/sso.amazonaws.com/, which is exactly why a resource policy should match that path with a wildcard instead of pinning the unstable role suffix.
Key terms: Amazon Verified Permissions, Cedar, IAM path, Cognito user pool
Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.