mcpField
V
vtex-io-rbac

vtex-io-rbac

github.com/vtex安全47740 次安装2 周前更新
代码仓库

关于 vtex-io-rbac

面向 VTEX IO 应用开发者的访问控制技能,说明如何根据调用方选择基于角色或资源的策略。内容涵盖 REST、GraphQL、VRN、授权配置及常见 403 问题排查。

主要功能

  1. 01区分基于角色与基于资源的策略
  2. 02配置 REST 路由访问策略
  3. 03使用 GraphQL @auth 指令授权
  4. 04编写用于标识资源和调用方的 VRN
  5. 05排查策略配置导致的 403 错误

适用场景

  1. 01限制 REST 端点仅由指定应用调用
  2. 02允许用户或 API 密钥访问私有路由
  3. 03为 GraphQL 查询和变更添加授权
  4. 04检查通配符策略引发的访问拒绝

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vtex/skills --skill vtex-io-rbac

安装后在 Claude Code 中输入/vtex-io-rbac确认已加载。

详细介绍

VTEX IO 访问控制(RBAC)

适用场景

当你需要控制谁可以访问 VTEX IO 应用的路由和资源时,使用此技能:

  • 在基于角色(policies.json)和基于资源(service.json 策略)的访问控制之间做出选择
  • 保护 REST 端点,确保只有特定应用、用户或 API 密钥可以调用
  • 使用 @auth 指令设置 GraphQL 授权
  • 了解用于声明主体的 VRN(VTEX Resource Name)语法
  • 排查由缺少或配置错误的策略导致的 403 Forbidden 错误

请勿在以下情况使用此技能:

  • 常规服务架构(使用 vtex-io-service-apps)
  • PCI 合规性和支付安全(使用 payment-pci-security)
  • 路由前缀和 CDN 行为(使用 vtex-io-service-paths-and-cdn)

决策规则

基于角色的策略与基于资源的策略
基于角色(policies.json)基于资源(service.json 策略)
谁可以调用?仅限其他 IO 应用(代表自身或代表其他应用)应用、用户和集成(API 密钥)
API 类型GraphQL 和 REST仅 REST
调用方如何获得访问权限必须在其 manifest.json 中声明所需策略无需声明策略;使用身份验证令牌调用即可
配置位置应用根目录中的 policies.jsonservice.json 中路由定义内的 policies 数组
适用场景暴露 GraphQL 端点;仅面向应用间调用者暴露 REST 端点控制用户、API 密钥或特定应用对 REST 端点的访问
选择合适的方法
  • GraphQL 端点 → 使用基于角色的策略(policies.json),和/或在架构中使用 @auth 指令进行用户级授权。
  • 仅由其他 IO 应用调用的 REST 端点 → 使用基于角色的策略(policies.json)。消费方应用必须在其 manifest.json 中声明该策略。
  • 由用户或 API 密钥调用的 REST 端点 → 在 service.json 中使用基于资源的策略。将路由设为 "public": false 并定义主体。
  • 公共 REST 端点(无需身份验证) → 在 service.json 中设置 "public": true。无需策略,但请注意,这意味着任何人都可以调用。
VRN 语法

VRN(VTEX Resource Name)用于标识资源和主体:

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 用户。

硬性约束

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

基于角色的策略仅适用于应用到应用的通信。如果用户(管理员或店面用户)或集成(API 密钥)需要调用你的端点,必须在 service.json 中使用基于资源的策略,并将路由设置为 "public": false。

原因 — 如果为用户或 API 密钥调用的路由配置基于角色的策略,这些调用者会收到 403 Forbidden,因为基于角色的策略不会评估用户或集成令牌。

检测方式 — 应允许管理员用户或外部集成调用的私有路由,仅配置了 policies.json,而 service.json 中没有 policies 数组。

正确做法 — 在 service.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。

// 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
约束:拒绝策略优先于允许策略

当基于资源的策略中的 allow 和 deny 规则存在重叠主体时,始终以 deny 为准。请谨慎处理与特定拒绝规则相交的允许规则中的通配符。

原因 — app/* 的宽泛 allow 与针对 app/vendor.bad-app@* 的特定 deny 组合时,会正确阻止 bad-app。但反过来,宽泛的 deny 配合特定的 allow 会阻止所有内容,包括你想允许的内容。

检测方式 — 同一路由存在多个效果冲突且主体重叠的策略条目。

正确做法 — 广泛允许,具体拒绝。

{
  "policies": [
    {
      "effect": "allow",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/*"]
    },
    {
      "effect": "deny",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/untrusted.app@*"]
    }
  ]
}

错误做法 — 广泛拒绝,再尝试具体允许(允许规则会被覆盖)。

{
  "policies": [
    {
      "effect": "deny",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/*"]
    },
    {
      "effect": "allow",
      "actions": ["POST"],
      "principals": ["vrn:apps:*:*:*:app/trusted.app@*"]
    }
  ]
}

推荐模式

基于角色的策略(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"
        ]
      }
    ]
  }
]

调用方应用在其 manifest.json 中声明策略:

{
  "policies": [
    {
      "name": "resolve-graphql"
    }
  ]
}
混合访问的基于资源的策略
{
  "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 指令

对于 GraphQL 端点,使用 @auth 指令进行用户级授权:

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 指令会检查调用方在指定 productCode 和 resourceCode 下的 License Manager 角色。

常见故障模式

  • 基于角色的路由对用户返回 403 — 路由仅配置了 policies.json;用户和 API 密钥会收到 403,因为基于角色的策略不适用于他们。
  • public: true 范围过宽 — 路由应为私有,却被设置为公开。任何人都可以在未进行身份验证的情况下调用它。
  • 调用方清单中缺少策略 — 应用尝试调用受基于角色的策略保护的路由,却未在其 manifest.json 中声明该策略,因此会返回 403。
  • VRN 拼写错误 — VRN 中的供应商、应用名称或主体格式拼写错误。匹配会静默失败,导致返回 403。
  • deny 中使用通配符 — 使用 app/* 的宽泛拒绝规则会阻止所有应用,包括受信任的应用。拒绝规则优先。
  • GraphQL mutation 缺少 @auth — 修改数据的 mutation 无需角色检查即可访问。

审查清单

  • 访问控制类型(基于角色还是基于资源)是否适用于调用方(应用还是用户/集成)?
  • 私有路由是否设置为 "public": false 并配置了适当的策略?
  • VRN 的主体类型(应用、用户、API 密钥)格式是否正确?
  • 调用方应用是否在其 manifest.json 中声明了所需的基于角色的策略?
  • 是否谨慎使用拒绝规则(它们会对主体重叠的允许规则产生覆盖)?
  • GraphQL mutation 是否配置了 @auth 指令,且 productCode 和 resourceCode 正确?
  • 通配符主体的范围是否尽可能小?

相关技能

参考资料

评论

支持 Markdown