mcpField
E
eve-auth-and-secrets

eve-auth-and-secrets

github.com/incept5安全0332 次安装17 周前更新
代码仓库

关于 eve-auth-and-secrets

Eve Auth and Secrets 是面向 Eve 用户的身份验证、凭据、密钥和范围访问管理工作流技能。内容涵盖身份、邀请、OAuth 令牌同步、访问组及应用 SSO 的命令行操作和配置。

主要功能

  1. 01登录并检查令牌权限
  2. 02管理身份、邀请和访问申请
  3. 03为机器人和服务账户签发令牌
  4. 04将 Claude 和 Codex 凭据同步到指定范围的密钥
  5. 05定义访问组和范围策略
  6. 06配置基于访问组的数据库 RLS

适用场景

  1. 01排查 Eve 身份验证失败
  2. 02邀请用户或申请组织访问权限
  3. 03为作业或项目同步 AI 工具凭据
  4. 04按访问组和资源范围限制数据访问

安装

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

$npx skills add https://github.com/incept5/eve-skillpacks --skill eve-auth-and-secrets

安装后在 Claude Code 中输入/eve-auth-and-secrets确认已加载。

附带文件references/app-sso-integration.md

详细介绍

Eve 身份验证和密钥

使用此工作流登录 Eve 并管理应用的密钥。

使用场景

  • 设置新的项目配置文件
  • 身份验证失败
  • 添加或轮换密钥
  • 部署期间的密钥插值错误
  • 设置身份提供方或组织邀请
  • 为部署在 Eve 上的应用添加 SSO 登录
  • 设置访问组和具备范围限制的数据平面授权
  • 为环境数据库配置基于用户组的 RLS

身份验证

eve auth login
eve auth login --ttl 30                # custom token TTL (1-90 days)
eve auth status
挑战-响应流程

Eve 使用挑战-响应身份验证。默认提供方为 github_ssh:

  1. 客户端发送 SSH 公钥指纹
  2. 服务端返回挑战(随机字节)
  3. 客户端使用私钥对挑战签名
  4. 服务端验证签名并签发 JWT
令牌类型
类型签发方式使用场景
用户令牌eve auth login交互式 CLI 会话
作业令牌Worker 自动签发作业内的 Agent 执行
铸造令牌eve auth mint机器人/服务账户

JWT 负载包括 sub(用户 ID)、org_id、scope 和 exp。通过 JWKS 端点验证令牌:GET /auth/jwks。

角色和组织成员身份变更会立即生效——服务端根据实时数据库成员关系解析权限,而非使用过期的 JWT 声明。当请求包含 project_id 但不包含 org_id 时,权限守卫会根据项目所属组织推导组织上下文。

权限

查看当前令牌可以执行的操作:

eve auth permissions

注册其他身份,以支持多提供方访问:

curl -X POST "$EVE_API_URL/auth/identities" -H "Authorization: Bearer $TOKEN" \
  -d '{"provider": "nostr", "external_id": "<pubkey>"}'

身份提供方

Eve 支持可插拔的身份提供方。身份验证守卫会先尝试 Bearer JWT,然后尝试提供方专属的请求身份验证。

提供方身份验证方式使用场景
github_sshSSH 挑战-响应默认 CLI 登录
nostrNIP-98 请求身份验证 + 挑战-响应原生使用 Nostr 的用户
Nostr 身份验证

两种方式:

  • 挑战-响应:与 SSH 类似,但使用 Nostr 密钥签名。使用 eve auth login --provider nostr。
  • NIP-98 请求身份验证:每个 API 请求都使用 Kind 27235 事件签名。无状态,无需存储令牌。

组织邀请

通过 CLI 或 API 邀请外部用户:

# Invite with SSH key registration (registers key so the user can log in immediately)
eve admin invite --email [email protected] --ssh-key ~/.ssh/id_ed25519.pub --org org_xxx

# Invite with GitHub identity
eve admin invite --email [email protected] --github ghuser --org org_xxx

# Invite with web-based auth (Supabase)
eve admin invite --email [email protected] --web --org org_xxx

# API: invite targeting a Nostr pubkey
curl -X POST "$EVE_API_URL/auth/invites" -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"org_id": "org_xxx", "role": "member", "provider_hint": "nostr", "identity_hint": "<pubkey>"}'

如果未指定身份验证方式(--github、--ssh-key 或 --web),CLI 会警告该用户将无法登录。用户之后可以通过 eve auth request-access --org "Org Name" --ssh-key ~/.ssh/id_ed25519.pub --wait 自行注册。

身份通过验证后,Eve 会自动创建其账户和组织成员身份。

对于由应用驱动的入驻流程,请使用组织范围的邀请 API,而非旧版管理员邀请流程:

# Create an org-scoped Supabase invite with a return URL for the app
curl -X POST "$EVE_API_URL/orgs/org_xxx/invites" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "role": "member",
    "redirect_to": "https://app.example.com/invite/complete",
    "app_context": { "project_id": "proj_123" }
  }'

# Search existing org members for an assignee picker
curl "$EVE_API_URL/orgs/org_xxx/members/search?q=ali" \
  -H "Authorization: Bearer $USER_TOKEN"

使用具有 orgs:invite 权限的用户令牌创建或列出这些邀请,并使用 orgs:members:read 权限查找成员。邀请邮件应跳转到 GoTrue 的 /verify 路径,而不是直接跳转到 OAuth 回调。如果 SSO 交换期间自动应用邀请,Eve 会返回 invite_redirect_to,以便 SSO 回调将用户带回目标应用,即使邮件提供方会移除嵌套的重定向参数也不例外。当前的邀请入驻流程会先建立 SSO 会话,然后将用户引导至 /set-password,再重定向到应用。

应用品牌邀请邮件

项目可在清单中使用 x-eve.branding 启用应用品牌邀请。主题、正文和 From: 显示名称都会体现应用身份;其他应用则使用默认的 “Eve Horizon”。

x-eve:
  branding:
    app_name: "ACME Portal"
    app_logo_url: "https://app.example.com/assets/logo.svg"   # https-only
    primary_color: "#1f6feb"
    email_from_name: "ACME Portal"
    reply_to_email: "[email protected]"
    support_email: "[email protected]"
    support_url: "https://example.com/help"

编辑后运行 eve project sync。使用 eve org invite <email> --org <org_id> --project <project_id> 发送的邀请会使用项目品牌。第一阶段的发件人地址仍使用平台默认值,只有显示名称会变化。魔法链接登录邮件也会使用同一品牌模板,区别仅在文案(“接受邀请”与“登录”)。

令牌铸造(管理员)

为机器人/服务用户铸造令牌,无需 SSH 登录:

# Mint token for a bot user (creates user + membership if needed)
eve auth mint --email [email protected] --org org_xxx

# With custom TTL (1-90 days, default: server configured)
eve auth mint --email [email protected] --org org_xxx --ttl 90

# Scope to project with admin role
eve auth mint --email [email protected] --project proj_xxx --role admin

打印当前访问令牌(适用于脚本):

eve auth token

自助申请访问权限

没有邀请的用户可以申请访问权限:

eve auth request-access --org "My Company" --email [email protected]
eve auth request-access --org "My Company" --ssh-key ~/.ssh/id_ed25519.pub
eve auth request-access --status <request_id>

管理员可通过以下命令批准或拒绝申请:

eve admin access-requests list
eve admin access-requests approve <request_id>
eve admin access-requests reject <request_id> --reason "..."

列表响应使用规范的 { "data": [...] } 封装格式。

批准操作是原子的(在单个数据库事务中完成),且具有幂等性——重新批准已完成的申请会返回现有记录。如果指纹已注册,Eve 会复用该身份的所有者。如果存在与所请求的 slug 和名称匹配的旧版不完整组织,Eve 会在批准时复用该组织。失败的尝试不会留下不完整状态。

凭据检查

验证本地 AI 工具凭据:

eve auth creds                # Show Claude + Codex cred status
eve auth creds --claude       # Only Claude
eve auth creds --codex        # Only Codex

输出包含令牌类型(setup-token 或 oauth)、预览和过期时间。使用此命令可在同步前确认令牌状态正常。

OAuth 令牌同步

将本地 Claude/Codex OAuth 令牌同步到 Eve 密钥中,以供 Agent 使用。作用域优先级:项目 > 组织 > 用户。

eve auth sync                       # Sync to user-level (default)
eve auth sync --org org_xxx         # Sync to org-level (shared across org projects)
eve auth sync --project proj_xxx    # Sync to project-level (scoped to one project)
eve auth sync --dry-run             # Preview without syncing

此操作会在所请求的作用域设置 CLAUDE_CODE_OAUTH_TOKEN / CLAUDE_OAUTH_REFRESH_TOKEN(Claude)以及 CODEX_AUTH_JSON_B64(Codex/Code)。

Claude 令牌类型
令牌前缀类型有效期建议
sk-ant-oat01-*setup-token(长期有效)长期有效作业和自动化的首选
其他 sk-ant-*oauth(短期有效)约 15 小时用于交互式开发;使用 claude setup-token 重新生成

同步短期 OAuth 令牌时,eve auth sync 会发出警告。运行 eve auth creds 可在同步前检查令牌类型。

自动写回 Codex/Code 令牌

每次调用执行框架后,工作器都会检查 Codex/Code CLI 是否在会话期间刷新了 auth.json。如果令牌发生变化,系统会自动将其写回来源密钥的作用域(用户/组织/项目),确保下一个作业使用最新令牌启动。此过程透明且不会影响正常运行——写回失败只会记录警告,不会影响作业结果。

对于 Codex/Code 凭据,同步会比较 tokens.expires_at,从 ~/.codex/auth.json 和 ~/.code/auth.json 中选取最新的令牌。

访问组 + 作用域访问

访问组是一级授权原语,用于划分数据平面访问权限(组织文件系统、组织文档、环境数据库)。创建访问组、添加成员,并通过带作用域约束的方式绑定角色:

# Create a group
eve access groups create --org org_xxx --slug eng-team --name "Engineering"

# Add members
eve access groups members add eng-team --org org_xxx --user user_abc
eve access groups members add eng-team --org org_xxx --service-principal sp_xxx

# Bind a role with scoped access
eve access bind --org org_xxx --group grp_xxx --role data-reader \
  --scope-json '{"orgfs":{"allow_prefixes":["/shared/"]},"envdb":{"schemas":["public"]}}'

# Check effective access
eve access memberships --org org_xxx --user user_abc
作用域类型
资源作用域字段示例
组织文件系统orgfs.allow_prefixes、orgfs.read_only_prefixes"/shared/"、"/reports/"
组织文档orgdocs.allow_prefixes、orgdocs.read_only_prefixes"/pm/features/"
环境数据库envdb.schemas、envdb.tables"public"、"analytics_*"
访问组感知的 RLS

为环境数据库中的基于访问组的行级安全性搭建 RLS 辅助函数:

eve db rls init --with-groups

此操作会创建 SQL 辅助函数(app.current_user_id()、app.current_group_ids()、app.has_group()),用于读取 Eve 运行时设置的会话上下文。在 RLS 策略中使用这些函数:

CREATE POLICY notes_group_read ON notes FOR SELECT
  USING (group_id = ANY(app.current_group_ids()));
成员关系检查

检查主体的完整有效访问权限——基础组织/项目角色、访问组成员关系、已解析的绑定以及合并后的作用域:

eve access memberships --org org_xxx --user user_abc
eve access memberships --org org_xxx --service-principal sp_xxx

响应包含 effective_scopes(所有绑定合并后的作用域)、effective_permissions,以及每个绑定的 matched_via(直接匹配或通过访问组匹配)。

特定资源访问检查

检查并说明对特定数据平面资源的访问权限:

eve access can orgfs:read /shared/reports --org org_xxx
eve access explain orgfs:write /shared/reports --org org_xxx --user user_abc

响应包含 scope_required、scope_matched,以及每项授权的 scope_reason,用于说明绑定为何与请求的资源路径匹配或不匹配。

策略即代码(v2)

在 .eve/access.yaml 中声明访问组、角色和带作用域的绑定。使用 version: 2:

version: 2
access:
  groups:
    eng-team:
      name: Engineering Team
      description: Scoped access for engineering collaborators
      members:
        - type: user
          id: user_abc
  roles:
    app_editor:
      scope: org
      permissions:
        - orgdocs:read
        - orgdocs:write
        - orgfs:read
        - envdb:read
  bindings:
    - subject: { type: group, id: eng-team }
      roles: [app_editor]
      scope:
        orgdocs: { allow_prefixes: ["/groups/app/**"] }
        orgfs: { allow_prefixes: ["/groups/app/**"] }
        envdb: { schemas: ["app"] }

验证、规划并同步:

eve access validate --file .eve/access.yaml
eve access plan --file .eve/access.yaml --org org_xxx
eve access sync --file .eve/access.yaml --org org_xxx

同步是声明式的:它会创建、更新并清理访问组、成员、角色和绑定,使其与 YAML 保持一致。作用域配置无效时会快速失败,不会应用任何变更。绑定主体可以是 user、service_principal 或 group。

密钥轮换

轮换 JWT 签名密钥:

  1. 在现有密钥旁设置 EVE_AUTH_JWT_SECRET_NEW
  2. 服务启动后会使用新密钥签名,但在宽限期内接受新旧两种密钥
  3. 宽限期(EVE_AUTH_KEY_ROTATION_GRACE_HOURS)结束后,移除旧密钥
  4. 紧急轮换:仅设置新密钥(立即使所有现有令牌失效)

App SSO 集成

使用两个共享软件包:@eve-horizon/auth(后端)和 @eve-horizon/auth-react(前端),即可将 Eve SSO 登录添加到任何由 Eve 部署的 App。平台会自动向已部署的服务注入 EVE_SSO_URL、EVE_ORG_ID、EVE_PROJECT_ID 和 EVE_API_URL。

Magic-Link 登录选择启用(无密码 App)

App 可以通过 x-eve.auth.login_method: magic_link 选择启用无密码浏览器登录。SSO 登录页面会采用项目品牌样式,并显示电子邮件 Magic-Link 登录,而不是用户名/密码登录。

x-eve:
  auth:
    login_method: magic_link              # or password_or_magic_link, password
    self_signup: false                    # unknown emails get generic success, no email
    invite_requires_password: false       # invite callback skips /set-password

Magic-Link 邮件由 Eve API 通过 POST /auth/magic-link 发送(而不是直接通过 GoTrue 发送),这样平台就能执行项目策略、在邀请邮件中共用 x-eve.branding 模板,并避免账号枚举。没有 x-eve.auth 的项目会保留旧版 SSO 行为。使用 eve org invite <email> --org <org_id> --project <project_id> 创建新用户。

Magic-Link 确认中间页(安全)

Eve 渲染的 Magic-Link 和邀请邮件会嵌入一个包装 URL(https://sso/m/mlw_<id>),而不是原始 GoTrue 验证 URL。电子邮件安全扫描器(Defender SafeLinks、Mimecast、Proofpoint、Barracuda)会访问邮件中的每个 URL,否则就会在人点击之前消耗一次性 OTP。包装页会显示带有品牌样式的“确认登录 / 接受邀请”页面;只有点击按钮后发出的 POST 请求才会显示 GoTrue URL 并执行 302 重定向。将此视为平台保障——App 端无需进行任何处理。

基于域名的注册(路径 C 自动关联)

预先批准电子邮件域名,让任何拥有匹配地址的人都能通过 Magic-Link 登录,而无需逐个邀请。首次成功登录时,平台会将其关联为规则 target_org 的 member。一个项目可以将不同域名路由到不同组织。

x-eve:
  auth:
    login_method: magic_link
    invite_requires_password: false
    org_access:
      mode: allowlist
      allowed_orgs: [org_Acme, org_Partner, org_Retailer]
      domain_signup:
        enabled: true
        domains:
          - { domain: example.com,  target_org: org_Acme, role: member }
          - { domain: partner.example,    target_org: org_Partner }
          - { domain: retailer.example, target_org: org_Retailer }

规则按声明顺序依次检查——第一个匹配项优先,因此应先声明更具体的模式。每条规则的 target_org 必须出现在 allowed_orgs 中。可以声明免费电子邮件服务提供商(free-mail.example),但会产生清单一致性警告。明确的待处理邀请优先于基于域名的注册(路径 B 优先于路径 C)。移除规则会阻止新用户注册,但不会追溯移除现有成员关系——使用 eve org members remove 移除这些成员。

通过事件主干审计:auth.domain_signup.invite_created 和 auth.domain_signup.member_attached 会携带 org_id、matched_rule 和 matched_domain。

App 组织访问和管理员邀请

默认情况下,App 仅允许访问项目所有者组织。使用 mode: allowlist 声明哪些客户组织可以使用该 App,并启用 App 内管理员邀请,以发送带有品牌样式的 Magic-Link 入门邮件:

x-eve:
  auth:
    org_access:
      mode: allowlist
      allowed_orgs: [org_customer123, customer-slug]
      invite:
        enabled: true
        admin_roles: [admin, owner]
        invited_role: member          # fixed; app invites cannot create admins

端点:GET /auth/app-access 返回用户获准访问的组织(以及用户可以向哪些组织发送邀请);POST /auth/app-invites 允许组织管理员/所有者邀请普通成员,并向其发送带有项目品牌样式的电子邮件。对于跨组织 App,请在后端使用 eveAppUserAuth(),而不是 eveUserAuth()——它会查询 /auth/app-access,并从 X-Eve-Org-Id、?eve_org_id= 或第一个获准组织中选择组织。

项目范围的重定向允许列表(自定义域名应用)

默认情况下,SSO 代理只接受指向集群域名下的目标。部署在自有域名上的应用必须声明其来源:

x-eve:
  auth:
    allowed_redirect_origins:
      - https://app.example.com
      - https://www.example.com

条目只能包含来源(scheme://host[:port]);在清单验证时会拒绝路径、查询参数和片段。GET /auth/app-context 返回的最终允许列表由以下内容合并而成:(1) 清单中显式声明的条目,(2) 项目自身符合条件的自定义域名(custom_domains 中具有 environment_id,且状态为 dns_verified/cert_provisioning/active 的行),以及 (3) allowed_orgs 中各组织所属项目的跨组织自定义域名。可使用 eve project auth-context <project_id> 查看。

对于非集群来源,此设置取代硬编码的 EVE_DEFAULT_DOMAIN 允许列表。代理会将该列表用于 /callback 中的 redirect_to 验证,以及 /session 和 /logout 的 CORS。@eve-horizon/auth-react 提供程序会在会话和注销请求中自动传递 project_id,以便正确设置跨站 Cookie 的作用域。

自定义域名上的 SameSite=None(平台保证)

使用 EVE_SSO_SECURE_COOKIES=true 部署 SSO 时,代理会将 eve_sso_rt 和 eve_sso Cookie 设置为 Secure; SameSite=None。当应用使用自定义域名时,这是 React 提供程序的跨站 fetch('/session', { credentials: 'include' }) 探测请求携带 Cookie 所必需的。本地 k3d(http://*.lvh.me)仍使用 SameSite=Lax。应用无需再自行配置。

限制仅允许已批准的电子邮件域名自行注册

设置环境变量 EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS(以逗号分隔)后,SSO 服务会根据电子邮件域名限制 /auth/signup 和 /auth/magiclink。未设置时允许所有域名(默认行为)。限制生效时,SSO 登录页面的注册标签页会显示域名提示。

# On the SSO deployment, set:
EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS=acme.com,partner.io

被拒绝的请求会返回 HTTP 422,错误为 error: email_domain_not_allowed。可使用此设置让公开的 SSO 端点仅接受来自已获准域名的邀请注册,而无需完全禁用自行注册。现有账户和管理员邀请不受影响。

后端(@eve-horizon/auth)

安装:npm install @eve-horizon/auth

新应用默认使用统一中间件:

导出项行为
eveAuth()非阻塞中间件。验证用户或 Agent 令牌,并将规范化身份附加到 req.eveIdentity。
eveIdentityGuard()如果未设置 req.eveIdentity,则返回 401。将其用于受保护的路由。
eveAuthConfig()返回 { sso_url, eve_api_url, ... } 的处理程序,数据来自自动注入的环境变量。前端通过获取此配置来发现 SSO。
eveAuthMe()为 React SDK 和自定义客户端提供的 /auth/me 处理程序。

仅当应用明确需要仅处理用户或仅处理 Agent 时,才保留旧版拆分中间件:

导出项行为
eveUserAuth()仅处理用户的非阻塞中间件。附加 req.eveUser: { id, email, orgId, role }。
eveAuthGuard()如果未设置 req.eveUser,则返回 401。
eveAuthMiddleware()用于 Agent/作业令牌的阻塞中间件。附加包含完整 EveTokenClaims 的 req.agent。验证失败时返回 401。
verifyEveToken(token)基于 JWKS 的本地验证(缓存 15 分钟)。返回 EveTokenClaims。
verifyEveTokenRemote(token)通过 /auth/token/verify 进行 HTTP 验证。始终获取最新状态。

Express 设置(约 3 行):

import { eveAuth, eveIdentityGuard, eveAuthConfig, eveAuthMe } from '@eve-horizon/auth';

app.use(eveAuth());
app.get('/auth/config', eveAuthConfig());
app.get('/auth/me', eveAuthMe());               // Full response for React SDK
app.use('/api', eveIdentityGuard());

req.eveIdentity 会规范化这两种令牌类型:

  • 用户令牌:id、email、orgId、role、permissions、isAgent: false
  • Agent/作业令牌:jobId、agentSlug、格式为 {agent_slug}@eve.agent 的稳定 email、permissions、isAgent: true

对于 RLS、审计日志和应用级路由,请使用 agentSlug 或稳定的 Agent 电子邮件地址。不要使用 {job_id}@eve.agent 作为 Agent 身份标识;这种旧模式按作业生成,且不稳定。

NestJS 设置:在 main.ts 中全局应用 eveAuth(),然后使用精简的守卫封装:

// main.ts
import { eveAuth } from '@eve-horizon/auth';
app.use(eveAuth());

// auth.guard.ts -- thin NestJS adapter
@Injectable()
export class EveGuard implements CanActivate {
  canActivate(ctx: ExecutionContext): boolean {
    const req = ctx.switchToHttp().getRequest();
    if (!req.eveIdentity) throw new UnauthorizedException();
    return true;
  }
}

// auth-config.controller.ts
@Controller()
export class AuthConfigController {
  private handler = eveAuthConfig();

  @Get('auth/config')
  getConfig(@Req() req, @Res() res) { this.handler(req, res); }
}

验证策略:eveAuth() 和 eveUserAuth() 默认使用 'local'(JWKS,缓存 15 分钟)。若要让成员资格状态立即保持最新,可使用 strategy: 'remote',每次请求的延迟约为 50 ms。

自定义角色映射:如果应用需要超出 Eve 的 owner/admin/member 范围的角色,可在 eveAuth() 之后进行转换:

app.use((req, _res, next) => {
  if (req.eveIdentity && !req.eveIdentity.isAgent) {
    req.user = {
      ...req.eveIdentity,
      appRole: req.eveIdentity.role === 'member' ? 'viewer' : 'admin',
    };
  }
  next();
});
前端(@eve-horizon/auth-react)

安装:npm install @eve-horizon/auth-react

导出项用途
EveAuthProvider上下文提供程序。初始化会话:检查 sessionStorage、探测 SSO /session 并缓存令牌。
useEveAuth()Hook:{ user, loading, error, config, loginWithSso, loginWithToken, logout }
EveLoginGate已认证时渲染子组件,否则显示登录表单。
EveLoginForm内置 SSO + 粘贴令牌登录界面。
createEveClient(baseUrl?)带有自动 Bearer 注入功能的 Fetch 封装。

简单设置——EveLoginGate 会处理加载、登录和已认证状态:

import { EveAuthProvider, EveLoginGate } from '@eve-horizon/auth-react';

<EveAuthProvider apiUrl="/api">
  <EveLoginGate>
    <ProtectedApp />
  </EveLoginGate>
</EveAuthProvider>

自定义身份验证门——使用 useEveAuth(),全面控制加载、登录和错误状态:

import { EveAuthProvider, useEveAuth } from '@eve-horizon/auth-react';

function AuthGate() {
  const { user, loading, loginWithToken, loginWithSso, logout } = useEveAuth();
  if (loading) return <Spinner />;
  if (!user) return <LoginPage onSso={loginWithSso} onToken={loginWithToken} />;
  return <App user={user} onLogout={logout} />;
}

export default () => (
  <EveAuthProvider apiUrl="/api">
    <AuthGate />
  </EveAuthProvider>
);

带身份验证的 API 调用:使用 createEveClient() 自动注入 Bearer 令牌:

import { createEveClient } from '@eve-horizon/auth-react';
const client = createEveClient('/api');
const res = await client.fetch('/data');
从自定义身份验证迁移

SDK 可用约 50 行代码取代手写的约 700–800 行身份验证代码。删除自定义 JWKS/令牌验证、Bearer 提取中间件、SSO URL 发现、会话探测逻辑、令牌存储辅助代码和登录表单。保留应用专属的角色映射和本地密码身份验证。

完整的迁移检查清单、类型参考、令牌生命周期和高级模式(SSE 身份验证、粘贴令牌模式、令牌过期状态),请参阅 references/app-sso-integration.md。

已部署服务的服务令牌

每个已部署服务都会自动注入 EVE_SERVICE_TOKEN(有效期为 90 天的 RS256 JWT,type: service),用于通过服务器到服务器的调用访问 Eve API。部署器会在每次部署时签发此令牌,应用不再需要手动设置此密钥。

令牌默认具有只读权限(projects:read、jobs:read、threads:read、envs:read、secrets:read、builds:read、pipelines:read、agents:read、events:read)。需要写入权限的应用可在清单中显式声明其他权限:

services:
  api:
    x-eve:
      permissions: [jobs:write, events:write, threads:write]

用于应用 -> Eve API 调用(创建作业、发送事件、更新线程)。完整的架构和调用模式请参阅 eve-read-eve-docs/references/secrets-auth.md 和 eve-read-eve-docs/references/manifest.md。

BYOK 模式(LLM API 密钥)

Eve 不代理推理流量。所有模型访问都采用 BYOK(自带密钥)模式:运行框架和应用通过密钥提供自己的 API 密钥,并直接调用服务提供商。

将 LLM 服务提供商密钥保存为项目密钥:

eve secrets set ANTHROPIC_API_KEY "sk-ant-xxx" --project proj_xxx
eve secrets set OPENAI_API_KEY "sk-xxx" --project proj_xxx
eve secrets set OPENAI_BASE_URL "https://my-vllm.runpod.ai/v1" --project proj_xxx

运行框架会自动解析这些密钥。对于自托管模型(通过 Tailscale 使用 vLLM、LM Studio),请将基础 URL 和 API 密钥设为密钥——Eve 通过私有端点提供连接能力(请参阅 eve-deploy-debugging),而不是托管推理层。

每个组织自带 OAuth 凭据(BYOA)

每个组织都为 Google Drive、Slack 和其他集成提供自己的 OAuth 应用凭据。集群层面不使用共享密钥。

# View setup instructions (redirect URIs, required scopes)
eve integrations setup-info google-drive
eve integrations setup-info slack

# Register OAuth app credentials
eve integrations configure google-drive \
  --client-id "xxx.apps.googleusercontent.com" \
  --client-secret "GOCSPX-xxx" \
  --label "Acme Corp Google Drive"

eve integrations configure slack \
  --client-id "12345.67890" \
  --client-secret "abc123" \
  --signing-secret "def456" \
  --app-id "A0123ABC" \
  --label "Acme Corp Slack Bot"

# View current config (secrets redacted)
eve integrations config google-drive

# Then connect as before (uses per-org credentials)
eve integrations connect google-drive
eve integrations connect slack

优势:每个组织的凭据相互隔离、可自定义授权同意页面品牌、速率限制彼此独立,且不存在共享密钥导致的影响范围扩大问题。

项目角色解析

角色和组织成员关系变更会立即生效——服务器根据实时数据库成员关系解析权限,而非使用过时的 JWT 声明。当请求包含 project_id 但不包含 org_id 时,权限守卫会根据项目所属组织推导组织上下文。

Auth SDK(@eve-horizon/auth)通过 eveUserAuth() 中间件提供此功能。需要确保成员关系即时更新时,请使用 strategy: 'remote'。

项目密钥

# Set a secret
eve secrets set API_KEY "your-api-key" --project proj_xxx

# List keys (no values)
eve secrets list --project proj_xxx

# Delete a secret
eve secrets delete API_KEY --project proj_xxx

# Import from file
eve secrets import .env --project proj_xxx
密钥插值

在 .eve/manifest.yaml 中使用 ${secret.KEY} 引用密钥:

services:
  api:
    environment:
      API_KEY: ${secret.API_KEY}
清单验证

部署前验证所有必需密钥均已设置:

eve manifest validate --validate-secrets    # check secret references
eve manifest validate --strict              # fail on missing secrets
本地密钥文件

进行本地开发时,创建 .eve/dev-secrets.yaml(已加入 gitignore):

secrets:
  default:
    API_KEY: local-dev-key
    DB_PASSWORD: local-password
  staging:
    DB_PASSWORD: staging-password
工作器注入

执行作业时,已解析的密钥会作为环境变量注入工作器容器。文件类型的密钥会写入磁盘,并通过 EVE_SECRETS_FILE 引用。Agent 进程读取该文件后,文件会被删除。

Git 身份验证

工作器使用密钥访问代码仓库:

  • HTTPS:github_token 密钥 → Authorization: Bearer 标头
  • SSH:ssh_key 密钥 → 写入 ~/.ssh/,并通过 GIT_SSH_COMMAND 使用

Auth 邮件投递(SES)

所有品牌认证邮件(组织/应用邀请、应用范围的魔法链接、系统管理员 Supabase 邀请)都通过单个 MailerService 发送。当 SMTP 指向 SES(GOTRUE_SMTP_HOST=*.amazonaws.com 或 EVE_MAILER_CHECK_SUPPRESSION=true)时,邮件服务会在发送前添加 GetSuppressedDestination 检查,避免账户级抑制被误认为发送成功。

结果行为
地址已抑制抛出 EmailSuppressedError;不发送 SMTP 邮件
未找到继续发送
AWS 错误(IRSA、限流、网络)失败时放行——记录 mailer.suppression_check_failed,继续发送

调用方行为:sendEligibleMagicLink 会捕获 EmailSuppressedError 并返回通用成功结果(保留防止账户枚举的保护),同时记录 mail.suppressed_drop。邀请路径会重新抛出错误,以便管理员看到错误。

设置 EVE_SES_CONFIGURATION_SET 后,SES 会将 Bounce/Complaint/Delivery/Reject 事件路由到 SNS,SNS 随后向 /webhooks/ses-feedback 发送 POST 请求。Webhook 会验证 SNS 签名,检查 TopicArn 是否与 EVE_SES_FEEDBACK_TOPIC_ARN 匹配,并在 email_delivery_events 中为每个受影响的收件人持久化一行记录(通过 sha256(snsMessageId|eventType|recipient) 实现幂等)。

通过管理员 CLI 查看事件:

eve admin email bounces list
eve admin email bounces list --recipient [email protected]
eve admin email bounces list --event-type Bounce --limit 100 --json

从本地表只读数据;不会修改 SES。如需清除账户级抑制,请参阅 SES 抑制操作手册。

可在 API Pod 日志中搜索以下结构化日志事件:mailer.sent、mailer.smtp_failed、mailer.suppressed、mailer.suppression_check_failed、mail.suppressed_drop、sns.subscription_confirmed、sns.rejected、ses.feedback_persisted。

故障排除

问题解决方法
未通过身份验证运行 eve auth login
令牌已过期重新运行 eve auth login(令牌将在距离过期时间不足 5 分钟时自动刷新)
引导流程已完成对于现有用户,使用 eve auth login;对于新用户,使用 eve admin invite。在非生产环境的堆栈上,eve auth bootstrap 会自动尝试服务器恢复。邮箱错误时的恢复方式:eve auth bootstrap --email [email protected]
缺少密钥使用 eve secrets list 确认,并设置该密钥
插值错误检查 ${secret.KEY} 的拼写;运行 eve manifest validate --validate-secrets
Git 克隆失败检查是否已设置 github_token 或 ssh_key 密钥
服务无法访问 API确认已注入 EVE_API_URL(检查 eve env show)
范围访问被拒绝运行 eve access explain <permission> <resource> --org <org> 查看范围匹配详情。检查绑定的范围约束是否包含目标路径/架构。内置角色(owner/admin/member)带有通配符 envdb 范围,因此这些角色遇到 envdb 拒绝时,问题出在权限集,而非缺少范围
显示的角色有误角色根据实时 DB 成员关系解析。运行 eve auth permissions 查看有效角色。如果属于多个组织,请运行 eve auth status 查看各组织的成员关系列表
作业中的 Claude 令牌有效期较短运行 eve auth creds 检查令牌类型。如果是 oauth(而非 setup-token),请使用 claude setup-token 重新生成,然后运行 eve auth sync 重新同步
Codex 令牌在作业之间过期自动回写机制应会刷新令牌。如果没有刷新,请重新运行 eve auth sync。检查 ~/.codex/auth.json 或 ~/.code/auth.json 中是否有新令牌
应用 SSO 无法使用确认已注入 EVE_SSO_URL(运行 eve env show)。本地开发时,手动设置 EVE_SSO_URL、EVE_ORG_ID 和 EVE_API_URL
应用令牌中的组织成员关系过期默认 TTL 为 1 天。若要立即检查成员关系,请在 eveUserAuth() 中使用 strategy: 'remote'
事件响应(密钥泄露)

如果密钥可能已泄露:

  1. 遏制:立即通过 eve secrets set 轮换密钥
  2. 失效:重新部署受影响的环境
  3. 审计:检查最近使用过该密钥的作业:eve job list
  4. 恢复:在源平台(GitHub、AWS 等)生成新凭据
  5. 记录:记录事件并更新轮换流程

评论

支持 Markdown