跳转至

认证 API

所有认证接口位于 /auth 路径前缀下,负责用户注册、登录、OAuth、会话管理和个人资料更新。

Base URL

本页所有接口的 Base URLhttps://auth.iminklet.com。例:POST https://auth.iminklet.com/auth/register

令牌滑动续签

访问令牌默认有效期 72 小时。当访问令牌剩余有效期不足 24 小时时,后端会在每次受保护接口的响应中自动附带一个新的访问令牌(通过 X-Renewed-Token 响应头)。客户端应检查此头部并在存在时替换本地存储的令牌。详情参见概览页的 X-Renewed-Token 说明

接口列表


POST /auth/register

创建新用户账户。

请求体:

{
  "email": "[email protected]",
  "username": "inkuser",
  "password": "securePassword123"
}
字段 类型 必填 描述
email string 有效的邮箱地址
username string 唯一的用户名
password string 至少 8 个字符

响应: 201 Created

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
  "user": {
    "id": "01912345-6789-7abc-def0-123456789abc",
    "email": "[email protected]",
    "username": "inkuser",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-01-15T10:30:00Z"
  }
}

错误:

状态码 原因
400 字段缺失或格式无效
409 邮箱或用户名已被占用

POST /auth/login

使用邮箱或用户名加密码进行认证。

请求体:

{
  "identifier": "[email protected]",
  "password": "securePassword123"
}
字段 类型 必填 描述
identifier string 邮箱地址或用户名
password string 账户密码

响应: 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
  "user": {
    "id": "01912345-6789-7abc-def0-123456789abc",
    "email": "[email protected]",
    "username": "inkuser",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-01-15T10:30:00Z"
  }
}

错误:

状态码 原因
400 字段缺失
401 凭据无效

POST /auth/refresh

使用有效的刷新令牌换取新的令牌对。旧的刷新令牌将被废止。

请求体:

{
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl..."
}

响应: 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "bmV3IHJlZnJlc2ggdG9r..."
}

错误:

状态码 原因
401 刷新令牌无效、已过期或已被使用

令牌轮换

刷新令牌为一次性使用。每次调用此接口都会废止之前的刷新令牌并返回新的令牌对。如果刷新令牌被重复使用,作为安全措施,该用户的所有会话可能会被撤销。


POST /auth/logout

需要认证

撤销提供的刷新令牌,结束会话。

请求头:

Authorization: Bearer {accessToken}

请求体:

{
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl..."
}

响应: 200 OK

{
  "message": "logged out"
}

错误:

状态码 原因
401 访问令牌缺失或无效

GET /auth/me

需要认证

获取当前认证用户的个人资料,包括关联的 OAuth 账户和订阅状态。

请求头:

Authorization: Bearer {accessToken}

响应: 200 OK

{
  "id": "01912345-6789-7abc-def0-123456789abc",
  "email": "[email protected]",
  "username": "inkuser",
  "createdAt": "2026-01-15T10:30:00Z",
  "updatedAt": "2026-01-15T10:30:00Z",
  "oauthAccounts": [
    {
      "provider": "google",
      "providerUserId": "117382948271639",
      "email": "[email protected]",
      "linkedAt": "2026-01-20T14:00:00Z"
    }
  ],
  "subscription": {
    "status": "active",
    "plan": "pro",
    "interval": "monthly",
    "currentPeriodEnd": "2026-02-15T10:30:00Z",
    "cancelAtPeriodEnd": false
  }
}

提示

如果用户从未订阅过,subscription 字段为 null

错误:

状态码 原因
401 访问令牌缺失或无效

PUT /auth/me

需要认证

更新当前认证用户的个人资料。所有字段均为可选 --- 只需包含你要修改的字段。

请求头:

Authorization: Bearer {accessToken}

请求体:

{
  "username": "newusername",
  "password": "newSecurePassword456",
  "currentPassword": "securePassword123"
}
字段 类型 必填 描述
username string 新用户名
password string 新密码(至少 8 个字符)
currentPassword string 条件必填 修改密码时必须提供当前密码

响应: 200 OK

{
  "id": "01912345-6789-7abc-def0-123456789abc",
  "email": "[email protected]",
  "username": "newusername",
  "createdAt": "2026-01-15T10:30:00Z",
  "updatedAt": "2026-01-16T08:00:00Z"
}

错误:

状态码 原因
400 字段无效或修改密码时未提供 currentPassword
401 访问令牌缺失或无效
409 用户名已被占用

GET /auth/sessions

需要认证

列出当前认证用户的所有活跃会话。

请求头:

Authorization: Bearer {accessToken}

响应: 200 OK

[
  {
    "id": "01912345-0000-7abc-def0-000000000001",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
    "ip": "192.168.1.100",
    "createdAt": "2026-01-15T10:30:00Z",
    "lastUsedAt": "2026-01-16T08:00:00Z"
  },
  {
    "id": "01912345-0000-7abc-def0-000000000002",
    "userAgent": "InkletDesktop/1.0",
    "ip": "10.0.0.50",
    "createdAt": "2026-01-14T09:00:00Z",
    "lastUsedAt": "2026-01-15T22:00:00Z"
  }
]

错误:

状态码 原因
401 访问令牌缺失或无效

DELETE /auth/sessions/{id}

需要认证

通过 ID 撤销指定会话。这将废止与该会话关联的刷新令牌。

路径参数:

参数 描述
id 会话 UUID

请求头:

Authorization: Bearer {accessToken}

响应: 200 OK

{
  "message": "session revoked"
}

错误:

状态码 原因
401 访问令牌缺失或无效
404 会话未找到或不属于当前用户

DELETE /auth/account

需要认证

永久删除当前认证用户的账号及其全部关联数据。此操作不可撤销。满足 App Store Guideline 5.1.1(v) 账号删除要求。

请求头:

Authorization: Bearer {accessToken}

请求体:

{
  "password": "currentPassword123",
  "confirm": true
}
字段 类型 必填 描述
confirm boolean 必须为 true,作为明确的删除意愿确认
password string 条件必填 设置过密码的用户需提供当前密码;仅使用 OAuth(无密码)的用户无需此字段

响应: 204 No Content

删除成功,响应无正文。

行为:

  • 在单个事务中硬删除该用户所有关联数据:sessions、oauth_accounts、subscriptions、files、raw_upload_items、render_tasks、content_blocks、该用户设备的 pushes 以及 users 表中的用户行
  • 设备仅做解绑,不删除——解绑后设备恢复可认领状态(设备为共享硬件)
  • 删除前:撤销 Apple Sign in 授权、取消有效的 Stripe 订阅

错误:

状态码 原因
400 confirm 不为 true 或请求体格式无效
401 访问令牌缺失或无效,或密码错误
404 用户已不存在(接口幂等)
500 内部错误(如 Apple 撤权或 Stripe 取消失败)

不可撤销操作

此接口会永久删除账号及其全部数据,无法恢复。客户端在调用前务必向用户二次确认。


OAuth 接口

Inklet 支持 Google 登录和 Apple 登录。OAuth 流程使用服务端重定向。

GET /auth/oauth/google

发起 Google OAuth 登录。client 查询参数决定使用哪个重定向 URI。

查询参数:

参数 可选值 描述
client web, desktop, ios 发起登录流程的客户端平台

示例:

GET /auth/oauth/google?client=web

响应: 302 Found --- 重定向至 Google 的 OAuth 授权页面。


GET /auth/oauth/google/callback

处理来自 Google 的 OAuth 回调。此接口不由客户端直接调用 --- 用户授权后由 Google 重定向至此。

行为:

  • 如果该 Google 邮箱尚未注册,则创建新账户
  • 如果邮箱匹配已有用户,则关联 Google 账户
  • 通过重定向返回令牌(令牌作为查询参数或 fragment,取决于客户端类型)

GET /auth/oauth/apple

发起 Apple OAuth 登录。

查询参数:

参数 可选值 描述
client web, desktop, ios 发起登录流程的客户端平台

示例:

GET /auth/oauth/apple?client=web

响应: 302 Found --- 重定向至 Apple 的 OAuth 授权页面。


POST /auth/oauth/apple/callback

处理来自 Apple 的 OAuth 回调。Apple 使用 form_post 响应模式,因此此接口接收的是包含表单编码数据的 POST 请求,而非查询参数。

Apple OAuth 的不同之处

与 Google 不同,Apple 以 POST 请求发送回调,请求体为 application/x-www-form-urlencoded 格式。Apple 还仅在首次授权时提供用户姓名 --- 后续登录只包含邮箱。

行为:

  • 如果该 Apple 邮箱尚未注册,则创建新账户
  • 如果邮箱匹配已有用户,则关联 Apple 账户
  • 通过重定向返回令牌

POST /auth/oauth/apple/native

iOS 原生 App 的 Apple 登录接口。与 Web 回调流程不同,iOS App 在设备端完成 Apple 认证后,直接将凭据 POST 到此接口,后端验证 Apple Identity Token 后返回 Inklet 令牌对(与 POST /auth/login 响应结构相同,无重定向)。

请求体:

{
  "identityToken": "eyJraWQiOiJXNldjT0tCIiwiYWxnIjoiRVMyNTYifQ...",
  "authorizationCode": "c0e0a10d8f8f44b...",
  "nonce": "abc123",
  "fullName": {
    "givenName": "张",
    "familyName": "三"
  }
}
字段 类型 必填 描述
identityToken string Apple 签发的 JWT Identity Token,后端使用 Apple 公钥验证签名
authorizationCode string 一次性授权码,用于在后端换取 Apple Refresh Token(供账户撤销使用)
nonce string 客户端提交的防重放随机数;若提供,必须与 Identity Token 中的 nonce 一致
fullName.givenName string 用户名字(仅首次授权时 Apple 返回)
fullName.familyName string 用户姓氏(仅首次授权时 Apple 返回)

响应: 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
  "user": {
    "id": "01912345-6789-7abc-def0-123456789abc",
    "email": "[email protected]",
    "username": "zhangsan1a2b3c4d",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-01-15T10:30:00Z"
  }
}

首次与后续登录

Apple 仅在用户首次授权时返回姓名。后续登录 fullName 为空,用户名将从邮箱本地部分自动生成并附加随机后缀。

错误:

状态码 原因
400 请求体无效或 identityToken 缺失
401 Apple Identity Token 签名无效或 nonce 不匹配
501 后端未配置 APPLE_BUNDLE_ID(原生流程不可用)