authentication

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Authentication

认证

Overview

概述

Spuree is an agent-friendly cloud storage. Projects contain folders (nestable) and files at any level. Authenticate with a JWT token or API key to access all V1 endpoints.
Tokens follow OAuth2 conventions and are issued as NextAuth-compatible JWTs.
Use this skill when an agent needs to:
  • Set up API access for the first time
  • Log in with email and password to get an access token
  • Refresh an expired access token using a refresh token
  • Exchange an authorization code for tokens (browser SSO flow)
  • Create, list, or revoke API keys
Spuree是一款对Agent友好的云存储服务。项目包含可嵌套的文件夹和任意层级的文件。通过JWT令牌或API密钥进行认证,即可访问所有V1端点。
令牌遵循OAuth2规范,以兼容NextAuth的JWT格式颁发。
当Agent需要执行以下操作时,可使用本技能:
  • 首次设置API访问权限
  • 使用邮箱和密码登录以获取访问令牌
  • 使用刷新令牌刷新过期的访问令牌
  • 交换授权码以获取令牌(浏览器SSO流程)
  • 创建、列出或撤销API密钥

Getting Started

快速开始

First-time setup — three options:
首次设置有三种可选方式:

Option A: User creates API key from web UI

选项A:用户通过Web UI创建API密钥

  1. Create a Spuree account at studio.spuree.com
  2. Go to studio.spuree.com/api-keys and create a key. Save it immediately — the key is only shown once.
  3. Set the environment variable:
    bash
    export SPUREE_API_KEY="<your-api-key>"
  1. studio.spuree.com创建Spuree账户
  2. 前往studio.spuree.com/api-keys创建密钥。请立即保存——密钥仅会显示一次。
  3. 设置环境变量:
    bash
    export SPUREE_API_KEY="<your-api-key>"

Option B: Agent logs in with email and password

选项B:Agent使用邮箱和密码登录

If the user provides their email and password, the agent can set up its own API key:
  1. Log in with
    POST /auth/token
    to get a temporary JWT
  2. Create an API key with
    POST /v1/api-keys
  3. Store the key in
    $SPUREE_API_KEY
如果用户提供邮箱和密码,Agent可自行设置API密钥:
  1. 通过
    POST /auth/token
    登录以获取临时JWT
  2. 通过
    POST /v1/api-keys
    创建API密钥
  3. 将密钥存储在
    $SPUREE_API_KEY

Option C: Agent opens browser for SSO login

选项C:Agent打开浏览器进行SSO登录

The agent can authenticate without the user sharing their password:
  1. Start a local HTTP server on an ephemeral port (49152–65535) to receive the callback.
  2. Open the user's browser to the Spuree sign-in page with
    source=api
    and the port:
    https://studio.spuree.com/auth/signin?source=api&port=<port>
    The user logs in via Google SSO (or email/password) in the browser.
  3. After login, Spuree redirects to
    http://localhost:<port>/callback?token=<exchange-code>
    . The agent receives the exchange code from this callback (valid for 60 seconds).
  4. Exchange the code for a JWT:
    bash
    curl -X POST "https://studio.spuree.com/api/auth/token/exchange" \
      -H "Content-Type: application/json" \
      -d '{"code": "<exchange-code>"}'
  5. Create an API key with the JWT (
    POST /v1/api-keys
    ) and store it in
    $SPUREE_API_KEY
    .

Once
$SPUREE_API_KEY
is set, the agent authenticates with
X-API-Key: $SPUREE_API_KEY
on all requests. No login or token refresh needed.
Verify it works — ask your agent:
"List my Spuree projects"
The agent should show you which projects you have access to. If it works, your API key is set up correctly.
Try it out — ask your agent something like:
"Upload this file to my Spuree project"
Agent无需用户共享密码即可完成认证:
  1. 在临时端口(49152–65535)启动本地HTTP服务器以接收回调。
  2. 打开用户浏览器访问Spuree登录页面,携带
    source=api
    和端口参数:
    https://studio.spuree.com/auth/signin?source=api&port=<port>
    用户在浏览器中通过Google SSO(或邮箱/密码)完成登录。
  3. 登录完成后,Spuree会重定向至
    http://localhost:<port>/callback?token=<exchange-code>
    。Agent从该回调中获取交换码(有效期60秒)。
  4. 交换码换取JWT:
    bash
    curl -X POST "https://studio.spuree.com/api/auth/token/exchange" \
      -H "Content-Type: application/json" \
      -d '{"code": "<exchange-code>"}'
  5. 使用JWT创建API密钥(
    POST /v1/api-keys
    )并存储在
    $SPUREE_API_KEY
    中。

设置好
$SPUREE_API_KEY
后,Agent在所有请求中使用
X-API-Key: $SPUREE_API_KEY
进行认证,无需登录或刷新令牌。
验证是否生效——向你的Agent询问:
"列出我的Spuree项目"
Agent应展示你有权访问的项目。如果正常返回,说明你的API密钥设置正确。
尝试使用——向你的Agent发送类似请求:
"将此文件上传至我的Spuree项目"

Base URLs

基础URL

Spuree uses two hosts:
HostPurposeEndpoints
https://studio.spuree.com/api
Authentication (login, refresh, exchange)
/auth/token
,
/auth/token/refresh
,
/auth/token/exchange
https://data.spuree.com/api
All V1 data APIs (projects, files, etc.)
/v1/projects
,
/v1/files
,
/v1/api-keys
, ...
All other skills in this repo use
https://data.spuree.com/api
. Only the token endpoints below use
studio.spuree.com
.
Spuree使用两个主机地址:
主机地址用途端点
https://studio.spuree.com/api
认证(登录、刷新、交换)
/auth/token
,
/auth/token/refresh
,
/auth/token/exchange
https://data.spuree.com/api
所有V1数据API(项目、文件等)
/v1/projects
,
/v1/files
,
/v1/api-keys
, ...
本仓库中的所有其他技能均使用
https://data.spuree.com/api
。仅以下令牌端点使用
studio.spuree.com

Token Lifecycle

令牌生命周期

TokenLifetimeFormat
access_token
1 hourNextAuth JWT
refresh_token
30 daysOpaque hex string
exchange_code
60 secondsOpaque string
The
access_token
is what you pass as
Authorization: Bearer <access_token>
to V1 API endpoints.
Alternatively, you can create API keys for long-lived, non-interactive access. API keys are passed via
X-API-Key
header and can be scoped to specific organizations.
令牌有效期格式
access_token
1小时NextAuth JWT
refresh_token
30天不透明十六进制字符串
exchange_code
60秒不透明字符串
access_token
需要以
Authorization: Bearer <access_token>
的形式传递给V1 API端点。
另外,你可以创建API密钥以实现长期、非交互式访问。API密钥通过
X-API-Key
头传递,可限定在特定组织范围内。

Token Response Format

令牌响应格式

All three endpoints return the same OAuth2-compliant response:
json
{
  "access_token": "eyJhbGciOiJkaXIiLCJlbmMiOi...",
  "refresh_token": "a1b2c3d4e5f6...",
  "expires_in": 3600,
  "user": {
    "id": "64a7b8c9d1e2f3a4b5c6d7e8",
    "email": "user@example.com",
    "name": "User Name",
    "image": "https://...",
    "organizationId": "64a7b8c9d1e2f3a4b5c6d7f0",
    "role": "admin",
    "workspaces": [
      {
        "workspaceId": "64a7b8c9d1e2f3a4b5c6d7f1",
        "workspaceName": "My Workspace",
        "role": "owner"
      }
    ]
  }
}
三个端点均返回符合OAuth2规范的响应:
json
{
  "access_token": "eyJhbGciOiJkaXIiLCJlbmMiOi...",
  "refresh_token": "a1b2c3d4e5f6...",
  "expires_in": 3600,
  "user": {
    "id": "64a7b8c9d1e2f3a4b5c6d7e8",
    "email": "user@example.com",
    "name": "User Name",
    "image": "https://...",
    "organizationId": "64a7b8c9d1e2f3a4b5c6d7f0",
    "role": "admin",
    "workspaces": [
      {
        "workspaceId": "64a7b8c9d1e2f3a4b5c6d7f1",
        "workspaceName": "My Workspace",
        "role": "owner"
      }
    ]
  }
}

Endpoints

端点

POST /auth/token

POST /auth/token

Log in with email and password.
Description: Validates credentials and returns an access token and refresh token. Rate limited to 10 requests per minute per IP.
Request Body:
FieldTypeRequiredDescription
email
stringYesUser's email address
password
stringYesUser's password
Status Codes:
CodeDescription
200Login successful, tokens returned
400Invalid request body or missing fields
401Invalid email or password, or account locked
429Rate limit exceeded (10 req/min)
500Internal server error
Error Messages (401):
MessageCause
Invalid email or password
Wrong credentials
Password is not set for this user
User registered via OAuth only
Account is temporarily locked...
Too many failed attempts (5 failures → 15 min lock)
Example:
bash
curl -X POST "https://studio.spuree.com/api/auth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "mypassword"
  }'

使用邮箱和密码登录。
**描述:**验证凭据并返回访问令牌和刷新令牌。每个IP每分钟最多允许10次请求。
请求体:
字段类型是否必填描述
email
string用户邮箱地址
password
string用户密码
状态码:
状态码描述
200登录成功,返回令牌
400请求体无效或字段缺失
401邮箱或密码无效,或账户被锁定
429请求超出速率限制(10次/分钟)
500内部服务器错误
错误信息(401):
信息原因
Invalid email or password
凭据错误
Password is not set for this user
用户仅通过OAuth注册
Account is temporarily locked...
失败尝试过多(5次失败→锁定15分钟)
示例:
bash
curl -X POST "https://studio.spuree.com/api/auth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "mypassword"
  }'

POST /auth/token/refresh

POST /auth/token/refresh

Refresh an expired access token.
Description: Exchanges a valid refresh token for a new access token and refresh token pair. The old refresh token is atomically revoked to prevent reuse. Rate limited to 10 requests per minute per IP.
Request Body:
FieldTypeRequiredDescription
refresh_token
stringYesThe refresh token from a previous login or refresh
Status Codes:
CodeDescription
200New tokens issued
400Missing refresh token
401Invalid or expired refresh token
429Rate limit exceeded
500Internal server error
Example:
bash
curl -X POST "https://studio.spuree.com/api/auth/token/refresh" \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "a1b2c3d4e5f6..."
  }'
Notes:
  • Each refresh token can only be used once. After refresh, use the new
    refresh_token
    for subsequent refreshes.
  • If a refresh token is reused (already revoked), it returns 401.

刷新过期的访问令牌。
**描述:**使用有效的刷新令牌换取新的访问令牌和刷新令牌对。旧的刷新令牌会被自动撤销以防止重复使用。每个IP每分钟最多允许10次请求。
请求体:
字段类型是否必填描述
refresh_token
string之前登录或刷新获取的刷新令牌
状态码:
状态码描述
200颁发新令牌
400缺少刷新令牌
401刷新令牌无效或过期
429请求超出速率限制
500内部服务器错误
示例:
bash
curl -X POST "https://studio.spuree.com/api/auth/token/refresh" \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "a1b2c3d4e5f6..."
  }'
注意:
  • 每个刷新令牌仅能使用一次。刷新后,后续刷新需使用新的
    refresh_token
  • 如果刷新令牌被重复使用(已撤销),会返回401。

POST /auth/token/exchange

POST /auth/token/exchange

Exchange an authorization code for tokens.
Description: Exchanges a one-time authorization code for an access token and refresh token. Used by agents and desktop apps that authenticate via the browser (see Getting Started Option C). The login flow starts at
studio.spuree.com/auth/signin?source=api
— after the user completes login, the exchange code is delivered to the agent's local callback server. Rate limited to 10 requests per minute per IP.
Request Body:
FieldTypeRequiredDescription
code
stringYesThe authorization exchange code
Status Codes:
CodeDescription
200Tokens issued
400Missing exchange code
401Invalid or expired exchange code
429Rate limit exceeded
500Internal server error
Example:
bash
curl -X POST "https://studio.spuree.com/api/auth/token/exchange" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "exchange-code-here"
  }'
Notes:
  • Exchange codes expire after 60 seconds.
  • Each code can only be used once.
交换授权码以获取令牌。
**描述:**使用一次性授权码换取访问令牌和刷新令牌。供Agent和桌面应用通过浏览器认证使用(见快速开始选项C)。登录流程从
studio.spuree.com/auth/signin?source=api
开始——用户完成登录后,交换码会发送至Agent的本地回调服务器。每个IP每分钟最多允许10次请求。
请求体:
字段类型是否必填描述
code
string授权交换码
状态码:
状态码描述
200颁发令牌
400缺少交换码
401交换码无效或过期
429请求超出速率限制
500内部服务器错误
示例:
bash
curl -X POST "https://studio.spuree.com/api/auth/token/exchange" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "exchange-code-here"
  }'
注意:
  • 交换码有效期为60秒。
  • 每个交换码仅能使用一次。

API Keys

API密钥

API keys provide long-lived authentication for automated workflows. They are scoped to a user and optionally restricted to specific organizations.
All V1 endpoints accept either
Authorization: Bearer <jwt>
or
X-API-Key: <api-key>
. When both are provided, JWT takes priority.
API密钥为自动化工作流提供长期认证。它们与用户绑定,可选择性限定在特定组织范围内。
所有V1端点均接受
Authorization: Bearer <jwt>
X-API-Key: <api-key>
。当两者同时提供时,JWT优先级更高。

POST /v1/api-keys

POST /v1/api-keys

Create a new API key.
Auth: Requires JWT (Bearer token only, not API key).
Request Body:
FieldTypeRequiredDescription
name
stringYesDescriptive name for the key
scopes
objectNo
{ "organizations": ["orgId1", ...] }
— restrict to specific orgs. Omit for all orgs.
expiresAt
datetimeNoExpiration timestamp (ISO 8601). Omit for no expiry.
Response:
json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "CI Pipeline Key",
  "key": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
  "createdAt": "2024-01-15T10:00:00Z",
  "expiresAt": null,
  "lastUsedAt": null,
  "scopes": { "organizations": ["64a7b8c9d1e2f3a4b5c6d7f0"] },
  "status": "active"
}
Important: The
key
field is only returned once at creation. Store it securely.
Example:
bash
curl -X POST "https://data.spuree.com/api/v1/api-keys" \
  -H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI Pipeline Key",
    "scopes": { "organizations": ["64a7b8c9d1e2f3a4b5c6d7f0"] }
  }'

创建新的API密钥。
**认证:**需要JWT(仅支持Bearer令牌,不支持API密钥)。
请求体:
字段类型是否必填描述
name
string密钥的描述性名称
scopes
object
{ "organizations": ["orgId1", ...] }
——限定在特定组织范围内。省略则允许访问所有组织。
expiresAt
datetime过期时间戳(ISO 8601格式)。省略则永不过期。
响应:
json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "CI Pipeline Key",
  "key": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
  "createdAt": "2024-01-15T10:00:00Z",
  "expiresAt": null,
  "lastUsedAt": null,
  "scopes": { "organizations": ["64a7b8c9d1e2f3a4b5c6d7f0"] },
  "status": "active"
}
重要提示:
key
字段仅在创建时返回一次。请安全存储。
示例:
bash
curl -X POST "https://data.spuree.com/api/v1/api-keys" \
  -H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI Pipeline Key",
    "scopes": { "organizations": ["64a7b8c9d1e2f3a4b5c6d7f0"] }
  }'

GET /v1/api-keys

GET /v1/api-keys

List all active API keys for the authenticated user.
Auth: Requires JWT (Bearer token only).
Response:
json
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "CI Pipeline Key",
    "createdAt": "2024-01-15T10:00:00Z",
    "expiresAt": null,
    "lastUsedAt": "2024-03-10T14:30:00Z",
    "scopes": { "organizations": ["64a7b8c9d1e2f3a4b5c6d7f0"] },
    "status": "active"
  }
]
Example:
bash
curl "https://data.spuree.com/api/v1/api-keys" \
  -H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"

列出已认证用户的所有活跃API密钥。
**认证:**需要JWT(仅支持Bearer令牌)。
响应:
json
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "CI Pipeline Key",
    "createdAt": "2024-01-15T10:00:00Z",
    "expiresAt": null,
    "lastUsedAt": "2024-03-10T14:30:00Z",
    "scopes": { "organizations": ["64a7b8c9d1e2f3a4b5c6d7f0"] },
    "status": "active"
  }
]
示例:
bash
curl "https://data.spuree.com/api/v1/api-keys" \
  -H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"

DELETE /v1/api-keys/{key_id}

DELETE /v1/api-keys/{key_id}

Revoke an API key (soft delete).
Auth: Requires JWT (Bearer token only).
Response:
json
{ "success": true }
Example:
bash
curl -X DELETE "https://data.spuree.com/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"

撤销API密钥(软删除)。
**认证:**需要JWT(仅支持Bearer令牌)。
响应:
json
{ "success": true }
示例:
bash
curl -X DELETE "https://data.spuree.com/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"

Token Storage

令牌存储

Store tokens in environment variables using these standard names:
SPUREE_ACCESS_TOKEN=eyJhbGci...    # JWT access token (1 hour)
SPUREE_REFRESH_TOKEN=a1b2c3d4...   # For refreshing access token (30 days)
SPUREE_API_KEY=a1b2c3d4...             # API key (64-char hex, long-lived)
All Spuree skills reference these variable names. V1 endpoints accept either
Authorization: Bearer $SPUREE_ACCESS_TOKEN
or
X-API-Key: $SPUREE_API_KEY
.
使用以下标准名称将令牌存储在环境变量中:
SPUREE_ACCESS_TOKEN=eyJhbGci...    # JWT访问令牌(有效期1小时)
SPUREE_REFRESH_TOKEN=a1b2c3d4...   # 用于刷新访问令牌(有效期30天)
SPUREE_API_KEY=a1b2c3d4...             # API密钥(64位十六进制,长期有效)
所有Spuree技能均引用这些变量名称。V1端点接受
Authorization: Bearer $SPUREE_ACCESS_TOKEN
X-API-Key: $SPUREE_API_KEY

Common Patterns

常见模式

Agent Login Flow

Agent登录流程

  1. Obtain tokens with email and password:
    POST /auth/token → { access_token, refresh_token, expires_in, user }
  2. Use access token for V1 API calls:
    Authorization: Bearer {access_token}
  3. Refresh before the token expires (every ~55 minutes):
    POST /auth/token/refresh → { access_token, refresh_token, ... }
  1. 获取令牌:使用邮箱和密码登录:
    POST /auth/token → { access_token, refresh_token, expires_in, user }
  2. 使用访问令牌:调用V1 API:
    Authorization: Bearer {access_token}
  3. 刷新令牌:在令牌过期前刷新(约每55分钟一次):
    POST /auth/token/refresh → { access_token, refresh_token, ... }

Token Refresh Strategy

令牌刷新策略

  • expires_in
    is
    3600
    (1 hour). Refresh proactively at ~55 minutes to avoid failed requests.
  • Always store and use the latest
    refresh_token
    — old ones are revoked after use.
  • If refresh fails with 401, the user must log in again with email/password.
  • expires_in
    3600
    (1小时)。建议提前约55分钟刷新令牌,避免请求失败。
  • 始终存储并使用最新的
    refresh_token
    ——旧令牌在使用后会被撤销。
  • 如果刷新请求返回401失败,用户需重新使用邮箱/密码登录。

API Key for Automation

自动化场景使用API密钥

For CI/CD pipelines or long-running agents that can't refresh tokens:
  1. Log in to get a JWT
  2. Create an API key scoped to the needed organizations:
    POST /v1/api-keys → { key: "a1b2c3d4..." }
  3. Use the API key for all subsequent requests:
    X-API-Key: a1b2c3d4...
API keys don't expire by default (unless
expiresAt
is set) and don't need refreshing.
对于CI/CD流水线或无法刷新令牌的长期运行Agent:
  1. 登录获取JWT
  2. 创建API密钥并限定在所需组织范围内:
    POST /v1/api-keys → { key: "a1b2c3d4..." }
  3. 使用API密钥进行所有后续请求:
    X-API-Key: a1b2c3d4...
API密钥默认永不过期(除非设置了
expiresAt
),无需刷新。

Error Handling

错误处理

ErrorCauseResolution
401 (invalid credentials)Wrong email or passwordVerify credentials
401 (account locked)5 failed login attemptsWait 15 minutes, then retry
401 (invalid refresh token)Token expired, revoked, or reusedLog in again with email/password
401 (invalid exchange code)Code expired or already usedRequest a new exchange code
429 (rate limit)More than 10 requests/min from same IPWait and retry with backoff
错误原因解决方法
401(凭据无效)邮箱或密码错误验证凭据正确性
401(账户锁定)5次登录失败尝试等待15分钟后重试
401(刷新令牌无效)令牌过期、已撤销或重复使用使用邮箱/密码重新登录
401(交换码无效)交换码过期或已使用请求新的交换码
429(速率限制)同一IP每分钟请求超过10次等待后重试,实现指数退避

Rate Limits

速率限制

All authentication endpoints share the same rate limit: 10 requests per minute per IP. Implement exponential backoff when receiving 429 responses.
所有认证端点共享相同的速率限制:每个IP每分钟最多10次请求。收到429响应时,请实现指数退避策略。