Aus dem Quell-Repository gerendert; Überschriften, Beispiele, Code, Tabellen, Links und Bilder bleiben erhalten.
VTEX IO access control (RBAC)
When this skill applies
Use this skill when you need to control who can access your VTEX IO app's routes and resources:
- Deciding between role-based (
policies.json) and resource-based (service.jsonpolicies) access control - Securing REST endpoints so only specific apps, users, or API keys can call them
- Setting up GraphQL authorization with the
@authdirective - Understanding VRN (VTEX Resource Name) syntax for declaring principals
- Debugging 403 Forbidden errors caused by missing or misconfigured policies
Do not use this skill for:
- General service architecture (use
vtex-io-service-apps) - PCI compliance and payment security (use
payment-pci-security) - Route prefix and CDN behavior (use
vtex-io-service-paths-and-cdn)
Decision rules
Role-based vs resource-based policies
Role-based (policies.json) | Resource-based (service.json policies) | |
|---|---|---|
| Who can call? | Only other IO apps (by themselves or on behalf of other apps) | Apps, users, and integrations (API keys) |
| API types | GraphQL and REST | REST only |
| How callers get access | Must declare required policies in their manifest.json | No policy declaration needed; just call with auth token |
| Where configured | policies.json in app root | policies array inside route definition in service.json |
| Use when | Exposing GraphQL endpoints; exposing REST endpoints for app-to-app only | Controlling access for users, API keys, or specific apps to REST endpoints |
Choosing the right approach
- GraphQL endpoints → Use role-based policies (
policies.json) and/or the `@auth` directive in the schema for user-level authorization. - REST endpoint called only by other IO apps → Use role-based policies (
policies.json). Consuming apps must declare the policy in theirmanifest.json. - REST endpoint called by users or API keys → Use resource-based policies in
service.json. Set the route as"public": falseand define principals. - Public REST endpoint (no auth) → Set
"public": trueinservice.json. No policies needed, but be aware this means anyone can call it.
VRN syntax
VRNs (VTEX Resource Names) identify resources and principals:
vrn:{service}:{region}:{account}:{workspace}:{path}- Apps:
vrn:apps:*:*:*:app/{vendor}.{app-name}@{version} - Users:
vrn:vtex.vtex-id:*:*:*:user/{email} - API keys:
vrn:vtex.vtex-id:*:*:*:user/vtexappkey-{account}-{hash} - Wildcards:
*matches any value in a segment.app/*matches all apps.user/*@gmail.commatches all Gmail users.
Hard constraints
Constraint: Use resource-based policies when users or API keys need access
Role-based policies only work for app-to-app communication. If users (admin or storefront) or integrations (API keys) need to call your endpoint, you must use resource-based policies in service.json with the route set to "public": false.
Why this matters — Setting up a role-based policy for a route that users or API keys call results in 403 Forbidden for those callers, because role-based policies don't evaluate user/integration tokens.
Detection — A private route that should be callable by admin users or external integrations, but only has policies.json configuration and no policies array in service.json.
Correct — Resource-based policy in service.json for user/integration access.
{
"routes": {
"orders": {
"path": "/_v/private/my-app/orders",
"public": false,
"policies": [
{
"effect": "allow",
"actions": ["GET", "POST"],
"principals": [
"vrn:vtex.vtex-id:*:*:*:user/*@mycompany.com",
"vrn:apps:*:*:*:app/partner.integration-app@*"
]
}
]
}
}
}Wrong — Only policies.json for a route that users need.
// policies.json — this only covers app-to-app, not users
[
{
"name": "access-orders",
"statements": [
{
"effect": "allow",
"actions": ["GET"],
"resources": ["vrn:my-app:*:*:*:/_v/private/my-app/orders"]
}
]
}
]
// Users calling this route still get 403Constraint: Deny policies take precedence over allow policies
When resource-based policies have overlapping principals between an allow and a deny rule, the deny always wins. Be careful with wildcards in allow rules that intersect with specific deny rules.
Why this matters — A broad allow for app/* combined with a specific deny for app/vendor.bad-app@* correctly blocks bad-app. But the reverse—a broad deny with a specific allow—blocks everything including what you wanted to allow.
Detection — Multiple policy entries for the same route with conflicting effects and overlapping principals.
Correct — Allow broadly, deny specifically.
{
"policies": [
{
"effect": "allow",
"actions": ["POST"],
"principals": ["vrn:apps:*:*:*:app/*"]
},
{
"effect": "deny",
"actions": ["POST"],
"principals": ["vrn:apps:*:*:*:app/untrusted.app@*"]
}
]
}Wrong — Deny broadly, try to allow specifically (the allow is overridden).
{
"policies": [
{
"effect": "deny",
"actions": ["POST"],
"principals": ["vrn:apps:*:*:*:app/*"]
},
{
"effect": "allow",
"actions": ["POST"],
"principals": ["vrn:apps:*:*:*:app/trusted.app@*"]
}
]
}Preferred pattern
Role-based policy (policies.json)
[
{
"name": "resolve-graphql",
"description": "Allows apps to resolve GraphQL requests",
"statements": [
{
"effect": "allow",
"actions": ["POST"],
"resources": [
"vrn:vtex.store-graphql:{{region}}:{{account}}:{{workspace}}:/_v/graphql"
]
}
]
}
]The consuming app declares the policy in its manifest.json:
{
"policies": [
{
"name": "resolve-graphql"
}
]
}Resource-based policy for mixed access
{
"routes": {
"webhook": {
"path": "/_v/private/my-app/webhook",
"public": false,
"policies": [
{
"effect": "allow",
"actions": ["POST"],
"principals": [
"vrn:apps:*:*:*:app/vtex.orders-broadcast@*",
"vrn:vtex.vtex-id:*:*:*:user/vtexappkey-myaccount-*"
]
}
]
}
}
}GraphQL @auth directive
For GraphQL endpoints, use the @auth directive for user-level authorization:
type Query {
orders: [Order] @auth(productCode: "10", resourceCode: "list-orders")
adminSettings: Settings
@auth(productCode: "10", resourceCode: "admin-settings")
}
type Mutation {
updateSettings(input: SettingsInput!): Settings
@auth(productCode: "10", resourceCode: "admin-settings")
}The @auth directive checks the caller's License Manager role for the specified productCode and resourceCode.
Common failure modes
- 403 for users on role-based routes — Route only has
policies.json; users and API keys get 403 because role-based policies don't apply to them. - Overly broad `public: true` — Route set to public when it should be private. Anyone can call it without auth.
- Missing policy in consumer manifest — App tries to call a role-based protected route but didn't declare the policy in its
manifest.json. Results in 403. - VRN typo — Misspelled vendor, app name, or principal format in VRN. Silently fails to match, resulting in 403.
- Wildcard in deny — Broad deny with
app/*blocks all apps including trusted ones. Deny takes precedence. - No `@auth` on GraphQL mutations — Mutations that modify data accessible without role checks.
Review checklist
- [ ] Is the access control type (role-based vs resource-based) correct for the callers (apps vs users/integrations)?
- [ ] Are private routes set to
"public": falsewith appropriate policies? - [ ] Are VRNs correctly formatted for the principal type (apps, users, API keys)?
- [ ] Do consuming apps declare required role-based policies in their
manifest.json? - [ ] Are deny rules used carefully (they override allow rules for intersecting principals)?
- [ ] Do GraphQL mutations have
@authdirectives with correctproductCodeandresourceCode? - [ ] Are wildcard principals scoped as narrowly as possible?
Related skills
- vtex-io-service-apps — Service class, clients, and route configuration
- vtex-io-app-contract — Manifest, builders, and policy declarations
- vtex-io-graphql-api — GraphQL schema and
@authdirective details - vtex-io-service-paths-and-cdn — Route prefix patterns
Reference
- Controlling Access to App Resources — Role-based and resource-based policies, VRN syntax, principal types
- App Authentication Using Auth Tokens — Auth token types for app-to-app and user-to-app communication
- GraphQL Authorization in IO Apps — @auth directive usage
- VTEX IO VRN — VTEX Resource Name format and examples

