跳到正文
Indiana
控制台
文档
章节目录 / 服务端 API Key 鉴权

服务端 API Key 鉴权

创建服务端 Key,理解每次请求的鉴权、App/环境范围、权限、撤销与轮换。

SDK 0.0.1更新于

先分清三种身份

普通服务端 SDK 调用使用 gg_sk_ API Key。Key 固定到一个 App 和环境,Indiana 按权限验证每次请求。服务端读取或写入客户数据时,不需要先签发 Customer Token。

谁持有什么凭据
调用方凭据用途
企业后端GROOVE_SECRET_KEY / gg_sk_在 App/环境及权限内调用 Indiana
企业客户端customerToken / gg_ct_直接调用允许的客户接口;由企业后端签发
Indiana 控制台成员Supabase Auth登录控制台、创建 App 和 Key

谁持有什么凭据

  • 企业后端

    凭据
    GROOVE_SECRET_KEY / gg_sk_
    用途
    在 App/环境及权限内调用 Indiana
  • 企业客户端

    凭据
    customerToken / gg_ct_
    用途
    直接调用允许的客户接口;由企业后端签发
  • Indiana 控制台成员

    凭据
    Supabase Auth
    用途
    登录控制台、创建 App 和 Key

Customer 是 Indiana 的业务记录,通过 App、环境和 externalUserId 关联你产品里的用户;它不共用控制台的账号密码。企业自己的登录系统负责确认用户身份,Indiana Key 不证明用户已登录。本文先完成只用 Key 的服务端接入,再说明客户端会话。

1. 创建 Sandbox 服务端 Key

  • 使用对当前 App 有 apiKeys:write 权限的 Owner、Admin 或 Developer 登录,在 SDK 接入页选择 App 和 Sandbox。
  • 创建 Key,仅勾选本次首次请求需要的 customers:write。Key 原文只显示一次,立即保存到自己的后端配置。
  • 同页复制 base URL 和 App ID。base URL 填站点根地址,不追加 /api/v1。纯服务端请求无需配置浏览器来源。

Key 固定到一个 App/环境,有效期为 1–365 天。修改权限或到期时间时创建替代 Key;每个 App/环境最多保留 20 把有效 Key。

2. 安装、配置并完成首次请求

准备 Node.js 22 或更高版本。本指南固定使用已发布的 npm 0.0.1 beta,直接安装即可。

在应用根目录保存被 Git 忽略的 .env.local,替换下面的 App ID 和 Key;自建 Indiana 时使用自己的站点根地址。GROOVE_SECRET_KEY 只在后端读取,不加 NEXT_PUBLIC_ 前缀。

在同一目录新建 groove-server-check.mjs,完整复制下面代码。脚本会创建并保留一个 Sandbox 测试客户;不会签发客户 Token;若已配置默认套餐/自动试用政策,客户创建可能触发符合资格的分配。

首次请求完成

看到 requestId、Sandbox scope 和 customerId 表示 Indiana 已验证 Key 并完成数据库操作。重复运行仍返回同一 customerId,created 变为 false;这也是成功结果。初始化 SDK 没有网络校验,真正的鉴权发生在 API 请求时。

实际业务中,把固定测试 externalUserId 换成后端已经验证或业务任务已经授权的稳定用户 ID。不要把浏览器提交的 customerId 或用户 ID 直接当作授权。

TerminalShell
npm install @tinkerlandai/indiana-sdk@0.0.1
.env.localShell
# 从 SDK 接入页复制 Sandbox 配置;此文件须被 Git 忽略。GROOVE_BASE_URL=https://groove-grow.vercel.appGROOVE_PROJECT_ID=your-app-idGROOVE_SECRET_KEY=your-sandbox-server-key
groove-server-check.mjsJavaScript
import { createGrooveServer, GrooveError } from "@tinkerlandai/indiana-sdk/server"; class ConfigurationError extends Error {} function required(name) {  const value = process.env[name];  if (!value) throw new ConfigurationError("Missing environment variable: " + name);  return value;} try {  const groove = createGrooveServer({    baseUrl: required("GROOVE_BASE_URL"),    apiKey: required("GROOVE_SECRET_KEY"),    scope: {      projectId: required("GROOVE_PROJECT_ID"),      environment: "sandbox",    },  });   // 固定的 Sandbox 测试身份;重跑返回同一 Customer。  const { requestId, scope, customer, created } =    await groove.getOrCreateCustomer({      externalUserId: "docs-server-auth-check",    });   console.log(JSON.stringify({    requestId, scope, customerId: customer.id, created,  }, null, 2));} catch (error) {  if (error instanceof GrooveError) {    // 只打印排查字段,不打印 Key、Token 或完整异常。    console.error(JSON.stringify({      code: error.code, status: error.status, requestId: error.requestId,    }));  } else if (error instanceof ConfigurationError) {    console.error(error.message);  } else {    console.error("请求未完成。检查 Node 版本、环境文件和网络配置。");  }  process.exitCode = 1;}
TerminalShell
node --env-file=.env.local groove-server-check.mjs
成功输出示意JSON
{  "requestId": "<request-id>",  "scope": {    "projectId": "<your-app-id>",    "environment": "sandbox"  },  "customerId": "<customer-id>",  "created": true}

也可以直接通过 HTTP 调用

HTTP 与 SDK 使用相同 Key 和权限。先在终端环境中设置 GROOVE_BASE_URL、GROOVE_PROJECT_ID、GROOVE_SECRET_KEY,再执行以下命令;Node 的 --env-file 不会替终端导入变量。

成功返回 HTTP 200,JSON 包含 requestId、scope、created 和 customer。使用相同 App、环境和 externalUserId 重试返回原客户。

TerminalShell
curl -X POST "$GROOVE_BASE_URL/api/v1/customers" \  -H "Authorization: Bearer $GROOVE_SECRET_KEY" \  -H "X-Groove-Project: $GROOVE_PROJECT_ID" \  -H "X-Groove-Environment: sandbox" \  -H "Content-Type: application/json" \  -d '{"externalUserId":"docs-server-auth-check"}'

3. 按需要增加权限

常用 SDK 方法与权限
SDK 方法HTTP凭据权限
getOrCreateCustomerPOST /api/v1/customers服务端 Keycustomers:write
getCustomerPOST /api/v1/customers/get服务端 Keycustomers:read
readHoldingsPOST /api/v1/customer-holdings服务端 Key / 客户 Tokenentitlements:read
mutateBalancePOST /api/v1/balances/execute服务端 Keycredits:consume
createCustomerSessionPOST /api/v1/customer-sessions服务端 Keysessions:create

常用 SDK 方法与权限

  • getOrCreateCustomer

    HTTP
    POST /api/v1/customers
    凭据
    服务端 Key
    权限
    customers:write
  • getCustomer

    HTTP
    POST /api/v1/customers/get
    凭据
    服务端 Key
    权限
    customers:read
  • readHoldings

    HTTP
    POST /api/v1/customer-holdings
    凭据
    服务端 Key / 客户 Token
    权限
    entitlements:read
  • mutateBalance

    HTTP
    POST /api/v1/balances/execute
    凭据
    服务端 Key
    权限
    credits:consume
  • createCustomerSession

    HTTP
    POST /api/v1/customer-sessions
    凭据
    服务端 Key
    权限
    sessions:create

SDK 的 scope 应与 Key 所属 App/环境相同。X-Groove-Project 和 X-Groove-Environment 只核对预期范围,不能通过修改它们切换到未授权 App。直接 HTTP 可以省略;传入时必须成对。

退款除 credits:consume 外还需 credits:manage;Funnel start 需要 sessions:create 和 funnels:run。其余附加条件查对应接口。

相关:完整接口与权限

可选:让客户端直接访问 Indiana

如果业务只在后端调用 Indiana,上面的流程已经足够。浏览器需要读取当前客户数据或展示 Funnel 时,才增加客户会话流程。

  • 企业后端先用自己的登录系统确认用户,再映射该用户的 Customer。
  • 为 Key 增加 sessions:create,以及客户端实际需要的权限,例如 entitlements:read。
  • 后端调用 createCustomerSession(customer.id),只向已验证用户返回 customerToken / expiresAt,并设置 no-store。
  • 客户端 identify(customerToken) 后可以直接请求 Indiana;浏览器来源须配置在当前 App/环境允许列表中。

Customer Token 默认 10 分钟有效,且不超过签发 Key 到期时间。只下放 entitlements:read、funnels:run、billing:read、billing:write、offers:resolve、credits:read;每次请求重新检查签发 Key 和权限。游客会话只获得 funnels:run。

当前没有 Public SDK Key 加任意用户 ID 来建立客户身份的入口。普通 SDK 接口拒绝带 Origin 的服务端 Key 请求,不能把 Secret Key 放进浏览器;Funnel 的独立网关来源规则见 Funnel 文档。

相关:完整后端会话接口与浏览器接入文件

相关:Funnel 运行凭据与流程

首次请求失败时怎么排查

常见失败与下一步
HTTP / code优先检查
401 / UNAUTHENTICATEDKey 是否完整复制、被部署正确读取,是否撤销或到期。服务端 Key 失败不能靠刷新 Customer Token 解决。
403 / FORBIDDEN首次请求需要 customers:write;普通服务端请求不要携带浏览器 Origin。
403 / SCOPE_MISMATCHApp ID、sandbox/live 是否与 Key 所属范围一致。
400 / INVALID_REQUEST输入是否符合 DTO;范围 Header 是否成对;body 是否为 JSON。
500 / INTERNAL_ERROR; 503 / SERVICE_UNAVAILABLE记录 code、status、requestId 排查 Indiana 依赖或网络问题;写入重试保留原幂等身份与输入。
501 / NOT_CONFIGUREDIndiana 必需服务未配置,由服务端维护者修复。

常见失败与下一步

  • 401 / UNAUTHENTICATED

    优先检查
    Key 是否完整复制、被部署正确读取,是否撤销或到期。服务端 Key 失败不能靠刷新 Customer Token 解决。
  • 403 / FORBIDDEN

    优先检查
    首次请求需要 customers:write;普通服务端请求不要携带浏览器 Origin。
  • 403 / SCOPE_MISMATCH

    优先检查
    App ID、sandbox/live 是否与 Key 所属范围一致。
  • 400 / INVALID_REQUEST

    优先检查
    输入是否符合 DTO;范围 Header 是否成对;body 是否为 JSON。
  • 500 / INTERNAL_ERROR; 503 / SERVICE_UNAVAILABLE

    优先检查
    记录 code、status、requestId 排查 Indiana 依赖或网络问题;写入重试保留原幂等身份与输入。
  • 501 / NOT_CONFIGURED

    优先检查
    Indiana 必需服务未配置,由服务端维护者修复。

HTTP 错误为 { requestId, error: { code, message } }。SDK GrooveError 携带 code、status、requestId;SDK 不自动重试。上面的脚本只输出这些排查字段,不输出 Key 或 Token。

相关:完整错误说明

撤销和轮换 Key

  • 纯服务端轮换:创建同 App/环境的新 Key 并授予所需权限 → 更新后端配置,确认请求成功 → 撤销旧 Key。
  • 已接入客户端时,撤销旧 Key 前还要用新 Key 创建客户会话并更新客户端。renewCustomerSession 只接受原签发者,不能跨 Key 续签。
  • 撤销或到期后,Key 及其签发的客户 Token 在后续鉴权失败;不会自动回滚已提交操作。
  • SDK 不自动续签。客户 Token 到期时由宿主重新验证登录后签发或续签;grow.reset 只清内存,撤销会话须由后端显式调用 revokeCustomerSessions。

相关:客户会话生命周期与退出登录

附录:Indiana 怎样验证 Key

gg_sk_ 是通过 HTTPS 发送的随机 Bearer 凭据。创建时原文仅返回一次,数据库只保存 SHA-256 哈希;它不是 Supabase 用户 JWT,也不是每个请求正文的签名。

  • 每次请求解析 Bearer,计算 SHA-256,通过 grow_authenticate_sdk 查凭据。
  • 检查未撤销、未到期及 App/环境记录,生成可信身份,再验证接口权限、范围、来源和输入。
  • 使用可信 scope 执行业务与对象归属校验,并验证响应。不跨请求缓存成功鉴权;验证失败不会执行对应业务。

附录:内部凭据与数据库权限

接入方只配置 GROOVE_SECRET_KEY。SUPABASE_SECRET_KEY 是 Indiana 自己的内部数据库凭据,接入方不需要持有;SDK 不提供数据库直连。Customer 存在 customers,Key 哈希存在 api_keys,会话哈希存在 customer_sessions。

控制台普通操作通过成员 Supabase Auth、当前成员权限和 RLS,普通写入使用 grow_console_writer。SDK 凭据网关及 scoped 业务 RPC 是单独记录的 service_role 例外,依靠可信 scope 和对象/业务校验,不能描述为受控制台成员 RLS 限制。

当前 Redis 请求限额策略为 observe,记录而不阻断;RATE_LIMITED 错误契约不能证明已经开启强制限流。

相关:服务端 SDK 方法参考