feat(auth): 添加 SECTL Auth OAuth 登录功能

- 新增 OAuth 登录组件和回调页面
- 集成 Tauri 深度链接插件处理 OAuth 回调
- 添加账户设置页面显示登录信息
- 更新 CI 配置添加 OAuth 环境变量
- 扩展 API 接口支持 OAuth 相关操作
- 添加国际化支持
This commit is contained in:
Yukino_fox
2026-03-29 19:30:27 +08:00
parent 3f3bfbf284
commit 7a59572f11
33 changed files with 8477 additions and 200 deletions
+922
View File
@@ -0,0 +1,922 @@
# SECTL Auth API 参考
本文档详细说明 SECTL Auth 提供的所有 API 接口。
## 基础信息
- **基础 URL**: `https://sectl.top`
- **认证方式**: OAuth 2.0 + Bearer Token
- **数据格式**: JSON
## 接口概览
| 接口 | 方法 | 说明 | 对应页面 |
| ----------------------- | ---- | ------------------- | ------------------------ |
| `/oauth/authorize` | GET | 获取授权码 | OAuthAuthorizeView.vue |
| `/api/oauth/token` | POST | 换取/刷新令牌 | AuthCallbackView.vue |
| `/api/oauth/userinfo` | GET | 获取用户信息 | PlatformUsersView.vue |
| `/api/oauth/config` | POST | 配置 Token 过期时间 | PlatformSettingsView.vue |
| `/api/oauth/revoke` | POST | 撤销令牌 | SecurityView.vue |
| `/api/oauth/introspect` | POST | 验证令牌 | - |
---
## OAuth 接口
### 1. 获取授权码
引导用户跳转到授权页面。
```http
GET /oauth/authorize
```
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ------------- | ------ | ---- | ---------------- |
| client_id | string | ✅ | 平台 ID |
| redirect_uri | string | ✅ | 回调 URL |
| response_type | string | ✅ | 固定值 `code` |
| state | string | ❌ | 防 CSRF 状态参数 |
**示例请求:**
```
https://sectl.top/oauth/authorize?client_id=pf_AHmIhAiptyLFxk8x&redirect_uri=http://localhost:5000/callback&response_type=code
```
**成功响应:**
- 重定向到 `redirect_uri?code={authorization_code}&state={state}`
**错误响应:**
- 重定向到 `redirect_uri?error={error_code}&error_description={description}`
**错误码:**
| 错误码 | 说明 |
| ------------------------- | ------------------ |
| invalid_request | 请求参数缺失或无效 |
| unauthorized_client | 客户端未授权 |
| access_denied | 用户拒绝授权 |
| unsupported_response_type | 不支持的响应类型 |
| invalid_scope | 无效的权限范围 |
| server_error | 服务器内部错误 |
**相关页面:**
- [OAuthAuthorizeView.vue](../../src/views/OAuthAuthorizeView.vue) - 授权页面
- [LoginView.vue](../../src/views/LoginView.vue) - 登录页面
---
### 2. 获取访问令牌
使用授权码换取访问令牌。
```http
POST /api/oauth/token
```
**请求头:**
```
Content-Type: application/json
```
**请求体:**
| 参数名 | 类型 | 必填 | 说明 |
| ------------- | ------ | ---- | ------------------------------------------------ |
| grant_type | string | ✅ | `authorization_code``refresh_token` |
| code | string | 条件 | 授权码(grant_type=authorization_code 时必填) |
| refresh_token | string | 条件 | 刷新令牌(grant_type=refresh_token 时必填) |
| client_id | string | ✅ | 平台 ID |
| client_secret | string | ✅ | 平台密钥 |
| redirect_uri | string | 条件 | 回调地址(grant_type=authorization_code 时必填) |
**授权码模式示例:**
```json
{
"grant_type": "authorization_code",
"code": "NjliNDIyOTYwMDE4znLUNeWM6J8ye0MC",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1",
"redirect_uri": "http://localhost:5000/callback"
}
```
**刷新令牌模式示例:**
```json
{
"grant_type": "refresh_token",
"refresh_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1"
}
```
**成功响应:**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 3600
}
```
**响应字段:**
| 字段 | 类型 | 说明 |
| ------------- | ------ | ----------------------- |
| access_token | string | 访问令牌(JWT |
| refresh_token | string | 刷新令牌 |
| token_type | string | 令牌类型,固定为 Bearer |
| expires_in | number | 过期时间(秒) |
**错误响应:**
```json
{
"error": "invalid_grant",
"error_description": "Authorization code has expired"
}
```
**错误码:**
| 错误码 | HTTP 状态 | 说明 |
| ---------------------- | --------- | ------------------------ |
| invalid_request | 400 | 请求格式错误 |
| invalid_client | 401 | 客户端认证失败 |
| invalid_grant | 400 | 授权码无效或过期 |
| unauthorized_client | 400 | 客户端无权使用此授权类型 |
| unsupported_grant_type | 400 | 不支持的授权类型 |
| invalid_scope | 400 | 无效的权限范围 |
**相关页面:**
- [AuthCallbackView.vue](../../src/views/AuthCallbackView.vue) - 回调处理
---
### 3. 获取用户信息
使用访问令牌获取用户信息。
```http
GET /api/oauth/userinfo
```
**请求头:**
```
Authorization: Bearer {access_token}
```
**成功响应:**
```json
{
"user_id": "69bd422960018cf4d0e5",
"email": "user@example.com",
"name": "张三",
"github_username": "zhangsan",
"permission": 1
}
```
**响应字段:**
| 字段 | 类型 | 说明 |
| --------------- | ------ | --------------------- |
| user_id | string | 用户唯一标识 |
| email | string | 用户邮箱 |
| name | string | 用户名称 |
| github_username | string | GitHub 用户名(如有) |
| permission | number | 用户权限等级 |
**错误响应:**
```json
{
"error": "invalid_token",
"error_description": "The access token is invalid or has expired"
}
```
**相关页面:**
- [PlatformUsersView.vue](../../src/views/dashboard/manage/PlatformUsersView.vue) - 平台用户列表
---
### 4. 配置 Token 过期时间
配置平台的 access_token 过期时间(需要管理员权限)。
```http
POST /api/oauth/config
```
**请求头:**
```
Content-Type: application/json
```
**请求体:**
| 参数名 | 类型 | 必填 | 说明 |
| ------------ | ------ | ---- | ------------------------------------------- |
| platform_id | string | ✅ | 平台 ID |
| expires_in | number | ✅ | 过期时间(秒),-1 表示永不过期,最小 60 秒 |
| admin_secret | string | ✅ | 管理员密钥 |
**请求示例:**
```json
{
"platform_id": "pf_AHmIhAiptyLFxk8x",
"expires_in": 86400,
"admin_secret": "your-admin-secret"
}
```
**成功响应:**
```json
{
"success": true,
"message": "Token expiration time updated",
"expires_in": 86400
}
```
**错误响应:**
```json
{
"error": "unauthorized",
"error_description": "Invalid admin secret"
}
```
**相关页面:**
- [PlatformSettingsView.vue](../../src/views/dashboard/other/PlatformSettingsView.vue) - 平台设置
---
### 5. 撤销令牌
撤销指定的访问令牌或刷新令牌。
```http
POST /api/oauth/revoke
```
**请求头:**
```
Content-Type: application/json
```
**请求体:**
| 参数名 | 类型 | 必填 | 说明 |
| --------------- | ------ | ---- | --------------------------------- |
| token | string | ✅ | 要撤销的令牌 |
| token_type_hint | string | ❌ | `access_token``refresh_token` |
| client_id | string | ✅ | 平台 ID |
| client_secret | string | ✅ | 平台密钥 |
**请求示例:**
```json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type_hint": "access_token",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1"
}
```
**成功响应:**
```json
{
"success": true,
"message": "Token revoked successfully"
}
```
**相关页面:**
- [SecurityView.vue](../../src/views/dashboard/other/SecurityView.vue) - 安全设置
- [LoginPlatformsView.vue](../../src/views/dashboard/list/LoginPlatformsView.vue) - 登录平台列表
---
### 6. 验证令牌
验证访问令牌的有效性和获取令牌信息。
```http
POST /api/oauth/introspect
```
**请求头:**
```
Content-Type: application/json
```
**请求体:**
| 参数名 | 类型 | 必填 | 说明 |
| ------------- | ------ | ---- | ------------ |
| token | string | ✅ | 要验证的令牌 |
| client_id | string | ✅ | 平台 ID |
| client_secret | string | ✅ | 平台密钥 |
**请求示例:**
```json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1"
}
```
**活跃令牌响应:**
```json
{
"active": true,
"user_id": "69bd422960018cf4d0e5",
"client_id": "pf_AHmIhAiptyLFxk8x",
"exp": 1704783600,
"iat": 1704780000
}
```
**非活跃令牌响应:**
```json
{
"active": false
}
```
**响应字段:**
| 字段 | 类型 | 说明 |
| --------- | ------- | ------------------------ |
| active | boolean | 令牌是否有效 |
| user_id | string | 用户 ID(仅活跃令牌) |
| client_id | string | 平台 ID(仅活跃令牌) |
| exp | number | 过期时间戳(仅活跃令牌) |
| iat | number | 签发时间戳(仅活跃令牌) |
---
## 平台管理 API
以下 API 用于管理平台信息,需要通过 Appwrite SDK 调用。
### 获取平台列表
```javascript
// 使用 Appwrite SDK
const result = await databases.listDocuments({
databaseId: "69bd89d8000304c37368",
collectionId: "platforms",
queries: [Query.orderDesc("$createdAt"), Query.limit(100)],
})
```
**相关页面:**
- [PlatformManagementView.vue](../../src/views/dashboard/manage/PlatformManagementView.vue)
### 获取平台详情
```javascript
const platform = await databases.getDocument({
databaseId: "69bd89d8000304c37368",
collectionId: "platforms",
documentId: "platform-document-id",
})
```
**相关页面:**
- [PlatformDetailView.vue](../../src/views/dashboard/detail/PlatformDetailView.vue)
### 更新平台信息
```javascript
await databases.updateDocument({
databaseId: "69bd89d8000304c37368",
collectionId: "platforms",
documentId: "platform-document-id",
data: {
callback_url: "http://new-callback.com",
token_expires_in: 7200,
},
})
```
**相关页面:**
- [PlatformSettingsView.vue](../../src/views/dashboard/other/PlatformSettingsView.vue)
### 申请创建平台
```javascript
await databases.createDocument({
databaseId: "69bd89d8000304c37368",
collectionId: "platform_applications",
documentId: ID.unique(),
data: {
name: "我的应用",
description: "应用描述",
callback_url: "http://localhost:5000/callback",
owner_id: currentUser.$id,
status: "pending",
created_at: new Date().toISOString(),
},
})
```
**相关页面:**
- [PlatformApplyView.vue](../../src/views/dashboard/form/PlatformApplyView.vue)
---
## 用户管理 API
### 获取用户授权的平台列表
```javascript
const result = await databases.listDocuments({
databaseId: "69bd89d8000304c37368",
collectionId: "oauth_tokens",
queries: [Query.equal("user_id", currentUser.$id), Query.equal("revoked", false)],
})
```
**相关页面:**
- [LoginPlatformsView.vue](../../src/views/dashboard/list/LoginPlatformsView.vue)
### 获取平台授权用户列表
```javascript
const result = await databases.listDocuments({
databaseId: "69bd89d8000304c37368",
collectionId: "oauth_tokens",
queries: [Query.equal("platform_id", "pf_xxx"), Query.equal("revoked", false)],
})
```
**相关页面:**
- [PlatformUsersView.vue](../../src/views/dashboard/manage/PlatformUsersView.vue)
---
## 云存储 API
云存储 API 提供文件上传、下载、管理等功能。需要通过 OAuth access_token 进行认证。
### 接口概览
| 接口 | 方法 | 说明 | SDK 方法 |
| ------------------------------- | ------ | ------------------------ | ------------------------------- |
| `/api/cloud/upload` | POST | 上传文件 | `uploadFile()` |
| `/api/cloud/files/{file_id}` | GET | 获取文件信息 | `getFile()` |
| `/api/cloud/download/{file_id}` | GET | 获取下载链接 | `downloadFile()` |
| `/api/cloud/view/{file_id}` | GET | 查看文件内容 | `viewFile()` |
| `/api/cloud/preview/{file_id}` | GET | 预览文件(支持图片处理) | `previewFile()` |
| `/api/cloud/info` | GET | 获取存储信息 | `getStorageInfo()` |
| `/api/cloud/files` | GET | 列出/搜索文件 | `listFiles()` / `searchFiles()` |
| `/api/cloud/files/{file_id}` | PUT | 更新文件(重命名) | `updateFile()` |
| `/api/cloud/files/{file_id}` | DELETE | 删除文件 | `deleteFile()` |
---
### 1. 上传文件
上传文件到云存储,系统会自动验证文件格式和检查存储容量。
```http
POST /api/cloud/upload
```
**请求头:**
```
Authorization: Bearer {access_token}
Content-Type: multipart/form-data
```
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ----------- | ------ | ---- | ------------ |
| file | File | ✅ | 要上传的文件 |
| platform_id | string | ✅ | 平台 ID |
**请求示例(JavaScript):**
```javascript
const formData = new FormData()
formData.append("file", fileInput.files[0])
formData.append("platform_id", "pf_AHmIhAiptyLFxk8x")
const response = await fetch("https://sectl.top/api/cloud/upload", {
method: "POST",
headers: {
Authorization: "Bearer eyJhbGciOiJIUzI1NiIs...",
},
body: formData,
})
```
**成功响应:**
```json
{
"success": true,
"file_id": "cf_xxxxxxxx",
"filename": "example.jpg",
"size": 1024567,
"url": "https://cloud.appwrite.io/v1/storage/buckets/69c869010007b752a9b1/files/xxx/view",
"storage": {
"used": "50MB",
"limit": "100MB",
"available": "50MB",
"percentage": 50
}
}
```
**错误响应 - 格式不允许:**
```json
{
"error": "invalid_format",
"error_description": "File format 'exe' is not allowed",
"allowed_formats": ["jpg", "png", "pdf"]
}
```
**错误响应 - 超出容量:**
```json
{
"error": "storage_exceeded",
"error_description": "Upload would exceed storage limit",
"storage": {
"used": "95MB",
"limit": "100MB",
"available": "5MB",
"requested": "10MB"
}
}
```
**相关页面:**
- [CloudServiceView.vue](../../src/views/dashboard/CloudServiceView.vue) - 云服务总览
---
### 2. 下载文件
获取文件的下载链接。
```http
GET /api/cloud/download/{file_id}
```
**请求头:**
```
Authorization: Bearer {access_token}
```
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ------- | ------ | ---- | ------- |
| file_id | string | ✅ | 文件 ID |
**成功响应:**
```json
{
"success": true,
"file_id": "cf_xxxxxxxx",
"filename": "example.jpg",
"download_url": "https://cloud.appwrite.io/v1/storage/buckets/69c869010007b752a9b1/files/xxx/download",
"expires_at": "2025-01-01T01:00:00Z"
}
```
---
### 3. 获取存储信息
获取用户在该平台的存储使用情况和配置信息。
```http
GET /api/cloud/info?platform_id={platform_id}
```
**请求头:**
```
Authorization: Bearer {access_token}
```
**查询参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ----------- | ------ | ---- | ------- |
| platform_id | string | ✅ | 平台 ID |
**成功响应:**
```json
{
"success": true,
"platform_id": "pf_xxx",
"tier": "free",
"storage": {
"used": 52428800,
"used_formatted": "50MB",
"limit": 104857600,
"limit_formatted": "100MB",
"available": 52428800,
"available_formatted": "50MB",
"percentage": 50
},
"allowed_formats": ["jpg", "png", "pdf"],
"max_file_size": 10485760
}
```
---
### 4. 列出文件
获取用户在该平台的文件列表。
```http
GET /api/cloud/files?platform_id={platform_id}&limit=20&offset=0
```
**请求头:**
```
Authorization: Bearer {access_token}
```
**查询参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ----------- | ------ | ---- | ----------------- |
| platform_id | string | ✅ | 平台 ID |
| limit | number | ❌ | 每页数量,默认 20 |
| offset | number | ❌ | 偏移量,默认 0 |
**成功响应:**
```json
{
"success": true,
"files": [
{
"file_id": "cf_xxx",
"filename": "example.jpg",
"size": 1024567,
"size_formatted": "1MB",
"created_at": "2025-01-01T00:00:00Z"
}
],
"total": 10,
"limit": 20,
"offset": 0
}
```
---
### 5. 删除文件
删除指定的文件。
```http
DELETE /api/cloud/files/{file_id}
```
**请求头:**
```
Authorization: Bearer {access_token}
```
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ------- | ------ | ---- | ------- |
| file_id | string | ✅ | 文件 ID |
**成功响应:**
```json
{
"success": true,
"message": "File deleted successfully",
"storage": {
"used": "45MB",
"limit": "100MB",
"available": "55MB"
}
}
```
---
## 云存储 SDK
我们提供了 JavaScript SDK 简化云存储 API 的调用。
### 安装
```html
<script src="https://sectl.top/platform-sdk/cloud-storage.js"></script>
```
或使用 ES6 模块:
```javascript
import CloudStorage from "./cloud-storage.js"
```
### 初始化
```javascript
const cloud = new CloudStorage({
baseUrl: "https://sectl.top",
platformId: "your_platform_id",
accessToken: "your_access_token",
})
```
### 使用示例
```javascript
// 上传文件
try {
const result = await cloud.uploadFile(fileInput.files[0])
console.log("文件上传成功:", result.file_id)
console.log("存储使用情况:", result.storage.percentage + "%")
} catch (error) {
if (error.code === "invalid_format") {
console.error("不支持的文件格式:", error.data.allowed_formats)
} else if (error.code === "storage_exceeded") {
console.error("存储空间不足")
}
}
// 获取存储信息
const info = await cloud.getStorageInfo()
console.log("已用空间:", info.storage.used_formatted)
console.log("允许格式:", info.allowed_formats)
// 列出文件
const files = await cloud.listFiles({ limit: 10 })
files.files.forEach((file) => {
console.log(file.filename, file.size_formatted)
})
// 下载文件
const download = await cloud.downloadFile("cf_xxx")
window.location.href = download.download_url
// 删除文件
await cloud.deleteFile("cf_xxx")
```
**相关文件:**
- [cloud-storage.js](../../platform-sdk/cloud-storage.js) - SDK 源码
---
## 远程退登 API
### 创建退登事件
当用户退出登录时,系统会自动创建退登事件。
```javascript
await databases.createDocument({
databaseId: "69bd89d8000304c37368",
collectionId: "logout_events",
documentId: ID.unique(),
data: {
user_id: currentUser.$id,
platform_id: null, // null 表示全局退出
global: true,
created_at: new Date().toISOString(),
},
})
```
### 订阅退登事件
使用 Appwrite Realtime 订阅退登事件:
```javascript
import { Client } from "appwrite"
const client = new Client()
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject("your-project-id")
const unsubscribe = client.subscribe(
"databases.69bd89d8000304c37368.collections.logout_events.documents",
(response) => {
const event = response.payload
if (event.user_id === currentUserId) {
// 执行本地退出
localLogout()
}
}
)
```
**相关页面:**
- [SecurityView.vue](../../src/views/dashboard/other/SecurityView.vue)
---
## 错误处理
### HTTP 状态码
| 状态码 | 说明 |
| ------ | ------------------ |
| 200 | 请求成功 |
| 400 | 请求参数错误 |
| 401 | 未授权,认证失败 |
| 403 | 禁止访问,权限不足 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
### 错误响应格式
```json
{
"error": "error_code",
"error_description": "详细的错误描述",
"error_uri": "https://docs.example.com/error/error_code"
}
```
---
## 安全建议
1. **使用 HTTPS** - 所有 API 通信必须使用 HTTPS
2. **保护密钥** - 不要在客户端代码中暴露 client_secret
3. **验证回调地址** - 确保 redirect_uri 与注册时一致
4. **使用状态参数** - 添加 state 参数防止 CSRF 攻击
5. **安全存储令牌** - Token 应安全存储,避免泄露
6. **及时撤销** - 用户退出时及时撤销令牌
---
## 相关页面
- [OAuthAuthorizeView.vue](../../src/views/OAuthAuthorizeView.vue) - OAuth 授权页面
- [AuthCallbackView.vue](../../src/views/AuthCallbackView.vue) - 认证回调
- [PlatformManagementView.vue](../../src/views/dashboard/manage/PlatformManagementView.vue) - 平台管理
- [PlatformDetailView.vue](../../src/views/dashboard/detail/PlatformDetailView.vue) - 平台详情
- [PlatformSettingsView.vue](../../src/views/dashboard/other/PlatformSettingsView.vue) - 平台设置
- [SecurityView.vue](../../src/views/dashboard/other/SecurityView.vue) - 安全设置
- [LoginPlatformsView.vue](../../src/views/dashboard/list/LoginPlatformsView.vue) - 登录平台列表