Skip to content

认证 API

用户认证和授权相关的 API 端点。

端点概览

方法端点描述认证
POST/auth/login用户登录
POST/auth/register用户注册
POST/auth/logout用户登出
POST/auth/github/authorize-url获取 GitHub OAuth URL
GET/auth/github/callbackGitHub OAuth 回调
POST/auth/password/forgot忘记密码
POST/auth/password/reset重置密码
GET/me获取当前用户信息
PATCH/me更新当前用户信息
POST/me/password修改密码

用户登录

POST /api/v1/auth/login

使用邮箱/用户名和密码登录。

请求

http
POST /api/v1/auth/login
Content-Type: application/json

{
  "identifier": "user@example.com",
  "password": "your_password",
  "turnstile_token": "optional_cloudflare_token"
}

参数说明:

字段类型必填说明
identifierstring邮箱或用户名
passwordstring密码(至少8位)
turnstile_tokenstringCloudflare Turnstile 验证令牌

响应

成功 (200 OK):

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2024-01-02T12:00:00Z",
  "user": {
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe",
    "email": "user@example.com",
    "role": "user",
    "status": "active",
    "last_login_at": "2024-01-01T12:00:00Z"
  }
}

错误响应:

  • 400 Bad Request: 参数格式错误
  • 401 Unauthorized: 用户名或密码错误
  • 403 Forbidden: 账户被暂停

示例

typescript
const login = async (identifier: string, password: string) => {
  const response = await fetch('/api/v1/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ identifier, password }),
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }
  
  const data = await response.json();
  localStorage.setItem('token', data.token);
  return data;
};

用户注册

POST /api/v1/auth/register

注册新用户账户。

请求

http
POST /api/v1/auth/register
Content-Type: application/json

{
  "username": "johndoe",
  "email": "user@example.com",
  "password": "securepassword123",
  "turnstile_token": "optional_cloudflare_token"
}

参数说明:

字段类型必填说明
usernamestring用户名(3-30位,字母数字下划线)
emailstring邮箱地址
passwordstring密码(至少8位)
turnstile_tokenstringCloudflare Turnstile 验证令牌

验证规则:

  • 用户名: 3-30 个字符,只能包含字母、数字、下划线和连字符
  • 邮箱: 有效的邮箱格式
  • 密码: 至少 8 个字符

响应

成功 (201 Created):

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2024-01-02T12:00:00Z",
  "user": {
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe",
    "email": "user@example.com",
    "role": "user",
    "status": "active"
  }
}

错误响应:

  • 400 Bad Request: 参数验证失败
  • 409 Conflict: 用户名或邮箱已存在

示例

typescript
const register = async (username: string, email: string, password: string) => {
  const response = await fetch('/api/v1/auth/register', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, email, password }),
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }
  
  const data = await response.json();
  localStorage.setItem('token', data.token);
  return data;
};

用户登出

POST /api/v1/auth/logout

登出当前用户(清除服务端会话)。

请求

http
POST /api/v1/auth/logout
Authorization: Bearer <token>

响应

成功 (200 OK):

json
{
  "message": "Logged out successfully"
}

示例

typescript
const logout = async () => {
  const token = localStorage.getItem('token');
  
  await fetch('/api/v1/auth/logout', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${token}` },
  });
  
  localStorage.removeItem('token');
};

GitHub OAuth

POST /api/v1/auth/github/authorize-url

获取 GitHub OAuth 授权 URL。

请求

http
POST /api/v1/auth/github/authorize-url
Content-Type: application/json

{
  "login": "suggested_username",
  "turnstile_token": "optional_cloudflare_token"
}

参数说明:

字段类型必填说明
loginstring建议的 GitHub 用户名
turnstile_tokenstringCloudflare Turnstile 验证令牌

响应

成功 (200 OK):

json
{
  "authorize_url": "https://github.com/login/oauth/authorize?client_id=xxx&state=yyy"
}

使用流程

  1. 调用此端点获取授权 URL
  2. 重定向用户到该 URL
  3. 用户授权后,GitHub 会重定向回 callback URL
  4. 前端从 callback URL 获取 code 和 state
  5. 调用 callback 端点完成登录

示例

typescript
const initiateGithubLogin = async () => {
  const response = await fetch('/api/v1/auth/github/authorize-url', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({}),
  });
  
  const data = await response.json();
  window.location.href = data.authorize_url;
};

GET /api/v1/auth/github/callback

处理 GitHub OAuth 回调。

请求

http
GET /api/v1/auth/github/callback?code=xxx&state=yyy

参数说明:

参数类型必填说明
codestringGitHub 授权码
statestringCSRF 防护状态码

响应

成功 (200 OK):

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2024-01-02T12:00:00Z",
  "user": {
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "johndoe",
    "email": "user@example.com",
    "role": "user",
    "status": "active"
  }
}

说明:

  • 如果 GitHub 用户首次登录,会自动创建账户
  • 邮箱从 GitHub 获取
  • 用户名从 GitHub 登录名生成

密码管理

POST /api/v1/auth/password/forgot

请求密码重置邮件。

请求

http
POST /api/v1/auth/password/forgot
Content-Type: application/json

{
  "email": "user@example.com",
  "turnstile_token": "optional_cloudflare_token"
}

参数说明:

字段类型必填说明
emailstring注册邮箱
turnstile_tokenstringCloudflare Turnstile 验证令牌

响应

成功 (200 OK):

json
{
  "accepted": true,
  "debug_reset_token": "optional_dev_only_token"
}

说明:

  • 无论邮箱是否存在都返回成功(防止邮箱枚举)
  • 开发环境可能返回 debug_reset_token 用于测试
  • 重置链接有效期 10 分钟

示例

typescript
const forgotPassword = async (email: string) => {
  const response = await fetch('/api/v1/auth/password/forgot', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  });
  
  if (!response.ok) {
    throw new Error('Failed to send reset email');
  }
  
  return response.json();
};

POST /api/v1/auth/password/reset

使用重置令牌重置密码。

请求

http
POST /api/v1/auth/password/reset
Content-Type: application/json

{
  "token": "reset_token_from_email",
  "new_password": "new_secure_password"
}

参数说明:

字段类型必填说明
tokenstring重置令牌(来自邮件)
new_passwordstring新密码(至少8位)

响应

成功 (200 OK):

json
{
  "message": "Password reset successfully"
}

错误响应:

  • 400 Bad Request: 令牌无效或已过期
  • 400 Bad Request: 密码不符合要求

示例

typescript
const resetPassword = async (token: string, newPassword: string) => {
  const response = await fetch('/api/v1/auth/password/reset', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token, new_password: newPassword }),
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }
};

用户信息管理

GET /api/v1/me

获取当前登录用户的信息。

请求

http
GET /api/v1/me
Authorization: Bearer <token>

响应

成功 (200 OK):

json
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "username": "johndoe",
  "email": "user@example.com",
  "role": "user",
  "status": "active",
  "last_login_at": "2024-01-01T12:00:00Z"
}

错误响应:

  • 401 Unauthorized: Token 无效或已过期

示例

typescript
const getCurrentUser = async () => {
  const token = localStorage.getItem('token');
  
  const response = await fetch('/api/v1/me', {
    headers: { 'Authorization': `Bearer ${token}` },
  });
  
  if (!response.ok) {
    throw new Error('Failed to fetch user info');
  }
  
  return response.json();
};

PATCH /api/v1/me

更新当前用户信息。

请求

http
PATCH /api/v1/me
Authorization: Bearer <token>
Content-Type: application/json

{
  "username": "new_username",
  "email": "new_email@example.com"
}

参数说明:

字段类型必填说明
usernamestring新用户名
emailstring新邮箱

说明:

  • 至少提供一个字段
  • 新用户名和邮箱不能与已有用户冲突

响应

成功 (200 OK):

json
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "username": "new_username",
  "email": "new_email@example.com",
  "role": "user",
  "status": "active"
}

错误响应:

  • 400 Bad Request: 参数验证失败
  • 409 Conflict: 用户名或邮箱已被使用

示例

typescript
const updateProfile = async (updates: { username?: string; email?: string }) => {
  const token = localStorage.getItem('token');
  
  const response = await fetch('/api/v1/me', {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(updates),
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }
  
  return response.json();
};

POST /api/v1/me/password

修改当前用户密码。

请求

http
POST /api/v1/me/password
Authorization: Bearer <token>
Content-Type: application/json

{
  "current_password": "old_password",
  "new_password": "new_password"
}

参数说明:

字段类型必填说明
current_passwordstring当前密码
new_passwordstring新密码(至少8位)

响应

成功 (200 OK):

json
{
  "message": "Password changed successfully"
}

错误响应:

  • 400 Bad Request: 当前密码错误
  • 400 Bad Request: 新密码不符合要求

示例

typescript
const changePassword = async (currentPassword: string, newPassword: string) => {
  const token = localStorage.getItem('token');
  
  const response = await fetch('/api/v1/me/password', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      current_password: currentPassword,
      new_password: newPassword,
    }),
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }
};

安全注意事项

密码安全

  • 后端使用 Argon2id 算法哈希密码
  • 密码强度要求:至少 8 个字符
  • 建议使用包含大小写字母、数字和特殊字符的密码

Token 安全

  • JWT Token 有效期为 24 小时
  • Token 应存储在 localStorage 或 sessionStorage
  • 不要在 URL 中传递 Token
  • HTTPS 环境下使用

CSRF 防护

  • GitHub OAuth 使用 state 参数防止 CSRF
  • 敏感操作建议二次验证

防暴力破解

  • Cloudflare Turnstile 人机验证(可选)
  • 建议实施登录频率限制

相关文档

Released under the MIT License.