vtex-io-rbac

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VTEX IO access control (RBAC)

VTEX IO 访问控制(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.json
    policies) access control
  • Securing REST endpoints so only specific apps, users, or API keys can call them
  • Setting up GraphQL authorization with the
    @auth
    directive
  • 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
    )
当您需要控制谁可以访问您的VTEX IO应用的路由和资源时,可使用本技能:
  • 选择基于角色(
    policies.json
    )还是基于资源(
    service.json
    策略)的访问控制方式
  • 保护REST端点,仅允许特定应用、用户或API密钥调用
  • 使用
    @auth
    指令配置GraphQL授权
  • 理解用于声明主体的VRN(VTEX资源名称)语法
  • 调试因策略缺失或配置错误导致的403 Forbidden错误
本技能不适用于:
  • 通用服务架构(请使用
    vtex-io-service-apps
  • PCI合规与支付安全(请使用
    payment-pci-security
  • 路由前缀与CDN行为(请使用
    vtex-io-service-paths-and-cdn

Decision rules

决策规则

Role-based vs resource-based policies

基于角色 vs 基于资源的策略

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 typesGraphQL and RESTREST only
How callers get accessMust 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 whenExposing GraphQL endpoints; exposing REST endpoints for app-to-app onlyControlling access for users, API keys, or specific apps to REST endpoints
基于角色(
policies.json
基于资源(
service.json
策略)
允许调用者仅其他IO应用(自行调用或代表其他应用调用)应用、用户和集成(API密钥)
支持API类型GraphQL和REST仅REST
调用者获取权限方式必须在其
manifest.json
中声明所需策略
无需声明策略;只需携带认证令牌调用
配置位置应用根目录下的
policies.json
service.json
中路由定义内的
policies
数组
适用场景暴露GraphQL端点;仅为应用间调用暴露REST端点控制用户、API密钥或特定应用对REST端点的访问权限

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 their
    manifest.json
    .
  • REST endpoint called by users or API keys → Use resource-based policies in
    service.json
    . Set the route as
    "public": false
    and define principals.
  • Public REST endpoint (no auth) → Set
    "public": true
    in
    service.json
    . No policies needed, but be aware this means anyone can call it.
  • GraphQL端点 → 使用基于角色的策略(
    policies.json
    和/或 Schema中的**
    @auth
    指令**实现用户级授权。
  • 仅被其他IO应用调用的REST端点 → 使用基于角色的策略(
    policies.json
    )。消费应用必须在其
    manifest.json
    中声明该策略。
  • 被用户或API密钥调用的REST端点 → 使用
    service.json
    中的基于资源的策略。将路由设置为
    "public": false
    并定义主体。
  • 公开REST端点(无需认证) → 在
    service.json
    中设置
    "public": true
    。无需配置策略,但请注意这意味着任何人都可以调用该端点。

VRN syntax

VRN语法

VRNs (VTEX Resource Names) identify resources and principals:
text
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.com
    matches all Gmail users.
VRN(VTEX资源名称)用于标识资源和主体:
text
vrn:{service}:{region}:{account}:{workspace}:{path}
  • 应用
    vrn:apps:*:*:*:app/{vendor}.{app-name}@{version}
  • 用户
    vrn:vtex.vtex-id:*:*:*:user/{email}
  • API密钥
    vrn:vtex.vtex-id:*:*:*:user/vtexappkey-{account}-{hash}
  • 通配符
    *
    匹配任意分段值。
    app/*
    匹配所有应用。
    user/*@gmail.com
    匹配所有Gmail用户。

Hard constraints

硬性约束

Constraint: Use resource-based policies when users or API keys need access

约束:当用户或API密钥需要访问时,必须使用基于资源的策略

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.
json
{
  "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.
json
// 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 403
基于角色的策略仅适用于应用间通信。如果用户(管理员或店铺前台)或集成(API密钥)需要调用您的端点,您必须
service.json
中使用基于资源的策略,并将路由设置为
"public": false
重要性 — 为用户或API密钥调用的路由配置基于角色的策略会导致这些调用者收到403 Forbidden错误,因为基于角色的策略不会验证用户/集成令牌。
检测方式 — 一个私有路由本应允许管理员用户或外部集成调用,但仅配置了
policies.json
,而
service.json
中没有
policies
数组。
正确示例 — 使用
service.json
中的基于资源策略实现用户/集成访问。
json
{
  "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@*"
          ]
        }
      ]
    }
  }
}
错误示例 — 仅为用户需要访问的路由配置
policies.json
json
// policies.json — 仅覆盖应用间访问,不支持用户访问
[
  {
    "name": "access-orders",
    "statements": [
      {
        "effect": "allow",
        "actions": ["GET"],
        "resources": ["vrn:my-app:*:*:*:/_v/private/my-app/orders"]
      }
    ]
  }
]
// 用户调用该路由仍会收到403错误

Constraint: 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.
json
{
  "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).
json
{
  "policies": [
    {
      "effect": "deny",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/*"]
    },
    {
      "effect": "allow",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/trusted.app@*"]
    }
  ]
}
当基于资源的策略中,允许和拒绝规则的主体存在重叠时,拒绝规则始终优先。需注意允许规则中的通配符与特定拒绝规则的交集。
重要性 — 为
app/*
设置宽泛的允许规则,同时为
app/vendor.bad-app@*
设置特定的拒绝规则,可正确阻止
bad-app
。但反过来——宽泛的拒绝规则加特定的允许规则——会阻止所有请求,包括您想要允许的请求。
检测方式 — 同一路由存在多个策略条目,且效果和主体存在冲突。
正确示例 — 先宽泛允许,再特定拒绝。
json
{
  "policies": [
    {
      "effect": "allow",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/*"]
    },
    {
      "effect": "deny",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/untrusted.app@*"]
    }
  ]
}
错误示例 — 先宽泛拒绝,再尝试特定允许(允许规则会被覆盖)。
json
{
  "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
)

基于角色的策略(
policies.json

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
:
json
{
  "policies": [
    {
      "name": "resolve-graphql"
    }
  ]
}
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"
        ]
      }
    ]
  }
]
消费应用需在其
manifest.json
中声明该策略:
json
{
  "policies": [
    {
      "name": "resolve-graphql"
    }
  ]
}

Resource-based policy for mixed access

支持混合访问的基于资源策略

json
{
  "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-*"
          ]
        }
      ]
    }
  }
}
json
{
  "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

GraphQL
@auth
指令

For GraphQL endpoints, use the
@auth
directive for user-level authorization:
graphql
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
.
对于GraphQL端点,使用
@auth
指令实现用户级授权:
graphql
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")
}
@auth
指令会检查调用者的License Manager角色是否包含指定的
productCode
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.
  • 基于角色的路由对用户返回403 — 路由仅配置了
    policies.json
    ;用户和API密钥会收到403错误,因为基于角色的策略不适用于他们。
  • 过度宽泛的
    public: true
    设置
    — 本应设为私有路由却被设为公开。任何人无需认证即可调用。
  • 消费应用清单中缺失策略 — 应用尝试调用受基于角色策略保护的路由,但未在其
    manifest.json
    中声明该策略。导致403错误。
  • VRN拼写错误 — VRN中的供应商、应用名称或主体格式拼写错误。匹配失败会静默发生,导致403错误。
  • 拒绝规则中使用通配符 — 为
    app/*
    设置宽泛的拒绝规则会阻止所有应用,包括可信应用。拒绝规则优先级更高。
  • GraphQL变更未添加
    @auth
    — 可修改数据的变更未设置角色检查。

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": false
    with 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
    @auth
    directives with correct
    productCode
    and
    resourceCode
    ?
  • Are wildcard principals scoped as narrowly as possible?
  • 访问控制类型(基于角色 vs 基于资源)是否与调用者类型(应用 vs 用户/集成)匹配?
  • 私有路由是否设置为
    "public": false
    并配置了合适的策略?
  • 针对主体类型(应用、用户、API密钥)的VRN格式是否正确?
  • 消费应用是否在其
    manifest.json
    中声明了所需的基于角色策略?
  • 是否谨慎使用拒绝规则(它们会覆盖重叠主体的允许规则)?
  • GraphQL变更是否添加了带有正确
    productCode
    resourceCode
    @auth
    指令?
  • 通配符主体的范围是否尽可能缩小?

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
    @auth
    directive details
  • vtex-io-service-paths-and-cdn — Route prefix patterns
  • vtex-io-service-apps — 服务类、客户端和路由配置
  • vtex-io-app-contract — 清单、构建器和策略声明
  • vtex-io-graphql-api — GraphQL Schema和
    @auth
    指令细节
  • vtex-io-service-paths-and-cdn — 路由前缀模式

Reference

参考资料