What Amazon Verified Permissions does
Amazon Verified Permissions is a managed, fine-grained authorization service for applications you build. It answers one question for your code: is this principal allowed to perform this action on this resource, in this context? You write the rules as policies in Cedar, an open source policy language, store them in Verified Permissions, and your application asks the service for an allow or deny decision before it does the work.

It is worth being clear about what it is not. Verified Permissions does not control access to your AWS resources; that is still the job of AWS Identity and Access Management (IAM). It also does not authenticate users. It assumes the user has already signed in through Amazon Cognito, another OpenID Connect (OIDC) provider or your own system, and it only makes the authorization decision.
The benefit is that permission logic stops being scattered across if statements in every service. Policies live in one place, can be reviewed and tested on their own, and can change without a code deploy.
Core concepts
Before you start, it helps to know the five building blocks:
- Policy store: a container for the policies and schema of one application (or one tenant, if you isolate tenants). Each authorization request targets a single policy store.
- Schema: a description of your entity types (such as
User,Group,Document), their attributes, and the actions that apply to them. With strict validation turned on, Verified Permissions rejects policies that do not match the schema. - Policies: Cedar
permitandforbidstatements. Cedar denies by default, and a matchingforbidalways overrides apermit. - Policy templates: policies with
?principaland?resourceplaceholders. You create template-linked policies from them, for example when a user shares a document with a colleague. - Identity source: an Amazon Cognito user pool or an OIDC identity provider whose tokens Verified Permissions can read to build the principal and its groups.
Step 1: Create a policy store
You can create a policy store in the Verified Permissions console, with AWS CloudFormation or the AWS CDK, or with the AWS CLI. The console also offers starting points, including building a policy store from an existing Amazon API Gateway API and an identity source, which generates a schema and an authorizer for you.
From the AWS CLI, the only required setting is the validation mode:
aws verifiedpermissions create-policy-store \
--validation-settings "mode=STRICT"
The response includes a policyStoreId, which every later call needs. STRICT mode requires a schema; until you add one, all static policies and templates are rejected. For production stores, also consider --deletion-protection ENABLED, which blocks the store from being deleted.
Step 2: Define a schema
Model your application in terms of principals, actions and resources. For a simple document app, the schema in Cedar JSON format might look like this:
{
"DocApp": {
"entityTypes": {
"User": {
"memberOfTypes": ["Group"],
"shape": {
"type": "Record",
"attributes": {
"department": { "type": "String" }
}
}
},
"Group": {},
"Document": {
"shape": {
"type": "Record",
"attributes": {
"owner": { "type": "Entity", "name": "User" },
"department": { "type": "String" }
}
}
}
},
"actions": {
"ViewDocument": {
"appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Document"] }
},
"EditDocument": {
"appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Document"] }
}
}
}
}
You upload it with aws verifiedpermissions put-schema, passing the schema as a single-line string in the cedarJson field of the definition. You can also edit the schema visually in the console. put-schema replaces the whole schema, and the change applies to policies submitted after it.
Step 3: Write policies
Start with the rules your product already has, written as plain sentences, then translate each one. Two examples for the schema above:
// Owners can view and edit their own documents
permit (
principal,
action in [DocApp::Action::"ViewDocument", DocApp::Action::"EditDocument"],
resource
)
when { resource.owner == principal };
// Anyone in the same department can view a document
permit (
principal,
action == DocApp::Action::"ViewDocument",
resource
)
when { principal.department == resource.department };
Create each one with aws verifiedpermissions create-policy, which takes a definition file containing a static object with a statement (the Cedar text) and an optional description. In strict mode, a policy that refers to an attribute or action not in the schema fails at creation, which catches typos before they reach users.
For sharing features, use a template instead of one static policy per share:
permit (
principal == ?principal,
action == DocApp::Action::"ViewDocument",
resource == ?resource
);
Your application then creates a template-linked policy for each share. If you later change the template, every linked policy changes with it.
Step 4: Connect an identity source (optional)
If your users sign in with Amazon Cognito or another OIDC provider, add that provider as an identity source on the policy store. For an Amazon Cognito user pool you provide the user pool ID, the principal entity type (for example DocApp::User), the group entity type and the app client IDs that may call the store. The user pool must be in the same AWS account.
Verified Permissions then builds the principal from the token. For Amazon Cognito the principal ID takes the form <user pool ID>|<sub>, claims in an ID token become principal attributes, claims in an access token become context, and a groups claim maps to group membership. Keep token lifetimes short: a JWT stays valid until it expires, even if you revoke the user’s session.
Step 5: Call the authorization API
Your application calls Verified Permissions at the point where it enforces access, usually in API middleware. There are four operations:
IsAuthorized: you pass the principal, action, resource, optional context and the entity data (attributes and group memberships) yourself.IsAuthorizedWithToken: you pass an ID or access token from your identity source instead of a principal.BatchIsAuthorizedandBatchIsAuthorizedWithToken: several decisions in one call, useful when you render a list and need to know which items a user can open.
A quick test from the CLI:
aws verifiedpermissions is-authorized \
--policy-store-id PSEXAMPLEabcdefg111111 \
--principal entityType=DocApp::User,entityId=alice \
--action actionType=DocApp::Action,actionId=ViewDocument \
--resource entityType=DocApp::Document,entityId=doc-42 \
--entities file://entities.json
The response contains a decision of ALLOW or DENY, the determiningPolicies that produced it, and any errors. Log the determining policy IDs: they make it easy to explain why a request was allowed or refused.
In production, call the service from your backend through an AWS SDK, not from the browser. For Express.js applications, AWS provides a middleware integration, and for Amazon API Gateway you can use a Lambda authorizer that calls Verified Permissions.
Practical tips
- Keep entity data accurate. Cedar decides only on the attributes and memberships you send. Stale data gives wrong answers, however good the policies are.
- Test policies like code. Keep Cedar files and the schema in version control, deploy them with CloudFormation or the CDK, and run allow and deny test cases in CI.
- Use
forbidfor hard rules, such as blocking suspended accounts, so no laterpermitcan open a gap. - Watch cost and latency. Pricing is per request, with batch calls priced differently from single calls (pricing page). Batching and short-lived caching of decisions in your application reduce both.
Getting help
9Acts is an AWS Advanced Tier Services Partner and AWS MSP Partner with the AWS DevOps Competency, and has helped clients build on AWS since 2016. If you want a second pair of eyes on your authorization model, talk to our security team.
