← [ BACK_TO_NOTES ]

GraphQL Auth Context

GraphQL Auth Context 把请求级可信身份与 request-scoped 依赖注入 Resolver;Authentication 回答“你是谁”,资源级 Authorization 仍应由 Service / Policy 结合业务数据判断。

#concept #growing #api #graphql #security

[!info] related notes

GraphQL Auth Context

一句话定义

GraphQL Context 是一次 operation 执行期间共享的请求级上下文;认证常把已验证身份放进 Context,而资源级授权仍应在真正访问资源的 Service / Policy 边界判断。

典型链路

HTTP request
  ↓ cookie / Authorization header
Authentication
  ↓ 得到可信 principal
GraphQL context
  ↓
Resolver
  ↓
Service / Policy authorization
  ↓
Repository / Database

例如:

context: async ({ req }) => {
  const user = await authenticate(req)

  return {
    currentUser: user,
    services,
    loaders: createRequestLoaders(),
  }
}

Resolver:

Query: {
  assessment: async (_p, { id }, ctx) => {
    if (!ctx.currentUser) {
      throw new Error('UNAUTHENTICATED')
    }

    return ctx.assessmentService.get({
      assessmentId: id,
      actorId: ctx.currentUser.id,
    })
  },
}

Service / Policy 再判断:

actorId = 42
assessmentId = 789
   ↓
读取资源 / ownership / organization / status
   ↓
User 42 是否允许读取 Assessment 789?

Authentication 与 Authorization

必须分开:

Authentication
= 你是谁?

Authorization
= 你能不能访问这个具体资源/字段?

因此一个非常实用的职责划分是:

Context
→ “当前请求是谁?”

Service / Policy
→ “这个人能不能对 Assessment 789 做这件事?”

Schema 里存在:

type Query {
  assessment(id: ID!): Assessment
}

只说明这个字段属于 API contract,不说明任何登录用户都能读取任意 assessment(id)。

为什么资源级 Authorization 不适合只放 Context

资源权限往往依赖实时业务数据:

assessment.user_id
organization membership
resource status
share permission
role / policy

Context 本身通常只携带 request facts,例如 currentUser.id = 42,它不会天然知道 Assessment 789 的 ownership。

所以不要把:

ctx.user.id === assessment.userId

这类业务授权逻辑散落在每个 Resolver 或 Context builder 里。

更好的设计:

Resolver
→ 把 actorId + resourceId 交给 Service
→ Service / Policy 读取必要事实并统一授权

这也让 REST、GraphQL、Job、CLI 等不同入口共享同一套授权规则。

为什么 GraphQL 特别容易出现授权遗漏

REST 常在 route/controller 粒度做权限检查;GraphQL 的字段可以被从不同 Query 路径组合出来:

Query.user → assessments
Query.assessment → owner
Query.me → latestAssessment

如果只在某个根 Query 做授权,而字段 resolver / service 没有资源 ownership 约束,同一敏感数据可能通过另一条路径被取出。

这就是 GraphQL 里的 BOLA/IDOR 风险形态之一。

Context 应放什么

适合:

  • authenticated identity / principal;
  • request id / trace id;
  • request-scoped DataLoaders;
  • service dependencies;
  • locale 等请求级事实。

不适合:

  • 可跨用户共享的可变授权状态;
  • 未验证 token 原文作为“已认证身份”;
  • 把所有业务状态都塞进 context;
  • 用 context 绕过 Service 层的资源权限规则;
  • 全局共享带用户数据的 DataLoader cache。

Context 与 DataLoader 的联系

DataLoader 常放在 Context,不是因为它属于 Authentication,而是因为两者都具有 request scope:

一次 GraphQL Request
  ↓
Context
  ├─ principal
  ├─ services
  └─ loaders
       ↓
本请求所有 Resolver 共享

请求结束后,这些 request-scoped loader memoization 应一起丢弃,避免跨请求数据污染。

推荐边界

HTTP middleware / Auth boundary
→ 验证 credential、生成 request facts

GraphQL Context
→ 携带可信 identity + request-scoped dependencies

Resolver
→ GraphQL API adapter,传递 actor/resource 参数

Service / Domain Policy
→ 最终判断是否允许访问/修改业务资源

Repository / Database
→ 提供授权和业务判断所需事实

核心判断

GraphQL Schema 约束的是“这个字段能不能被问”,Context 说明“当前请求是谁”,Service / Policy 决定“这个调用者能不能得到或修改这个真实资源”。

三个问题不能混成一个。

创建于 2026/9/11 更新于 2026/9/15
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本