认证 API¶
所有认证接口位于 /auth 路径前缀下,负责用户注册、登录、OAuth、会话管理和个人资料更新。
Base URL
本页所有接口的 Base URL 为 https://auth.iminklet.com。例:POST https://auth.iminklet.com/auth/register。
令牌滑动续签¶
访问令牌默认有效期 72 小时。当访问令牌剩余有效期不足 24 小时时,后端会在每次受保护接口的响应中自动附带一个新的访问令牌(通过 X-Renewed-Token 响应头)。客户端应检查此头部并在存在时替换本地存储的令牌。详情参见概览页的 X-Renewed-Token 说明。
接口列表¶
POST /auth/register¶
创建新用户账户。
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
email |
string | 是 | 有效的邮箱地址 |
username |
string | 是 | 唯一的用户名 |
password |
string | 是 | 至少 8 个字符 |
响应: 201 Created
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
"user": {
"id": "01912345-6789-7abc-def0-123456789abc",
"email": "user@example.com",
"username": "inkuser",
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:30:00Z"
}
}
错误:
| 状态码 | 原因 |
|---|---|
400 |
字段缺失或格式无效 |
409 |
邮箱或用户名已被占用 |
POST /auth/login¶
使用邮箱或用户名加密码进行认证。
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
identifier |
string | 是 | 邮箱地址或用户名 |
password |
string | 是 | 账户密码 |
响应: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
"user": {
"id": "01912345-6789-7abc-def0-123456789abc",
"email": "user@example.com",
"username": "inkuser",
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:30:00Z"
}
}
错误:
| 状态码 | 原因 |
|---|---|
400 |
字段缺失 |
401 |
凭据无效 |
POST /auth/refresh¶
使用有效的刷新令牌换取新的令牌对。旧的刷新令牌将被废止。
请求体:
响应: 200 OK
错误:
| 状态码 | 原因 |
|---|---|
401 |
刷新令牌无效、已过期或已被使用 |
令牌轮换
刷新令牌为一次性使用。每次调用此接口都会废止之前的刷新令牌并返回新的令牌对。如果刷新令牌被重复使用,作为安全措施,该用户的所有会话可能会被撤销。
POST /auth/logout¶
需要认证
撤销提供的刷新令牌,结束会话。
请求头:
请求体:
响应: 200 OK
错误:
| 状态码 | 原因 |
|---|---|
401 |
访问令牌缺失或无效 |
GET /auth/me¶
需要认证
获取当前认证用户的个人资料,包括关联的 OAuth 账户和订阅状态。
请求头:
响应: 200 OK
{
"id": "01912345-6789-7abc-def0-123456789abc",
"email": "user@example.com",
"username": "inkuser",
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:30:00Z",
"oauthAccounts": [
{
"provider": "google",
"providerUserId": "117382948271639",
"email": "user@gmail.com",
"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¶
需要认证
更新当前认证用户的个人资料。所有字段均为可选 --- 只需包含你要修改的字段。
请求头:
请求体:
{
"username": "newusername",
"password": "newSecurePassword456",
"currentPassword": "securePassword123"
}
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
username |
string | 否 | 新用户名 |
password |
string | 否 | 新密码(至少 8 个字符) |
currentPassword |
string | 条件必填 | 修改密码时必须提供当前密码 |
响应: 200 OK
{
"id": "01912345-6789-7abc-def0-123456789abc",
"email": "user@example.com",
"username": "newusername",
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-16T08:00:00Z"
}
错误:
| 状态码 | 原因 |
|---|---|
400 |
字段无效或修改密码时未提供 currentPassword |
401 |
访问令牌缺失或无效 |
409 |
用户名已被占用 |
GET /auth/sessions¶
需要认证
列出当前认证用户的所有活跃会话。
请求头:
响应: 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 |
请求头:
响应: 200 OK
错误:
| 状态码 | 原因 |
|---|---|
401 |
访问令牌缺失或无效 |
404 |
会话未找到或不属于当前用户 |
DELETE /auth/account¶
需要认证
永久删除当前认证用户的账号及其全部关联数据。此操作不可撤销。满足 App Store Guideline 5.1.1(v) 账号删除要求。
请求头:
请求体:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
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 |
发起登录流程的客户端平台 |
示例:
响应: 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 |
发起登录流程的客户端平台 |
示例:
响应: 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": "user@privaterelay.appleid.com",
"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(原生流程不可用) |