mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 06:04:22 +08:00
7a59572f11
- 新增 OAuth 登录组件和回调页面 - 集成 Tauri 深度链接插件处理 OAuth 回调 - 添加账户设置页面显示登录信息 - 更新 CI 配置添加 OAuth 环境变量 - 扩展 API 接口支持 OAuth 相关操作 - 添加国际化支持
22 KiB
22 KiB
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. 获取授权码
引导用户跳转到授权页面。
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 - 授权页面
- LoginView.vue - 登录页面
2. 获取访问令牌
使用授权码换取访问令牌。
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 时必填) |
授权码模式示例:
{
"grant_type": "authorization_code",
"code": "NjliNDIyOTYwMDE4znLUNeWM6J8ye0MC",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1",
"redirect_uri": "http://localhost:5000/callback"
}
刷新令牌模式示例:
{
"grant_type": "refresh_token",
"refresh_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1"
}
成功响应:
{
"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 | 过期时间(秒) |
错误响应:
{
"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 - 回调处理
3. 获取用户信息
使用访问令牌获取用户信息。
GET /api/oauth/userinfo
请求头:
Authorization: Bearer {access_token}
成功响应:
{
"user_id": "69bd422960018cf4d0e5",
"email": "user@example.com",
"name": "张三",
"github_username": "zhangsan",
"permission": 1
}
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| user_id | string | 用户唯一标识 |
| string | 用户邮箱 | |
| name | string | 用户名称 |
| github_username | string | GitHub 用户名(如有) |
| permission | number | 用户权限等级 |
错误响应:
{
"error": "invalid_token",
"error_description": "The access token is invalid or has expired"
}
相关页面:
- PlatformUsersView.vue - 平台用户列表
4. 配置 Token 过期时间
配置平台的 access_token 过期时间(需要管理员权限)。
POST /api/oauth/config
请求头:
Content-Type: application/json
请求体:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| platform_id | string | ✅ | 平台 ID |
| expires_in | number | ✅ | 过期时间(秒),-1 表示永不过期,最小 60 秒 |
| admin_secret | string | ✅ | 管理员密钥 |
请求示例:
{
"platform_id": "pf_AHmIhAiptyLFxk8x",
"expires_in": 86400,
"admin_secret": "your-admin-secret"
}
成功响应:
{
"success": true,
"message": "Token expiration time updated",
"expires_in": 86400
}
错误响应:
{
"error": "unauthorized",
"error_description": "Invalid admin secret"
}
相关页面:
- PlatformSettingsView.vue - 平台设置
5. 撤销令牌
撤销指定的访问令牌或刷新令牌。
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 | ✅ | 平台密钥 |
请求示例:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type_hint": "access_token",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1"
}
成功响应:
{
"success": true,
"message": "Token revoked successfully"
}
相关页面:
- SecurityView.vue - 安全设置
- LoginPlatformsView.vue - 登录平台列表
6. 验证令牌
验证访问令牌的有效性和获取令牌信息。
POST /api/oauth/introspect
请求头:
Content-Type: application/json
请求体:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| token | string | ✅ | 要验证的令牌 |
| client_id | string | ✅ | 平台 ID |
| client_secret | string | ✅ | 平台密钥 |
请求示例:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"client_id": "pf_AHmIhAiptyLFxk8x",
"client_secret": "sk_UyMHuFf6oKXh8ZcHSb7lfOSgzumoOeg1"
}
活跃令牌响应:
{
"active": true,
"user_id": "69bd422960018cf4d0e5",
"client_id": "pf_AHmIhAiptyLFxk8x",
"exp": 1704783600,
"iat": 1704780000
}
非活跃令牌响应:
{
"active": false
}
响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| active | boolean | 令牌是否有效 |
| user_id | string | 用户 ID(仅活跃令牌) |
| client_id | string | 平台 ID(仅活跃令牌) |
| exp | number | 过期时间戳(仅活跃令牌) |
| iat | number | 签发时间戳(仅活跃令牌) |
平台管理 API
以下 API 用于管理平台信息,需要通过 Appwrite SDK 调用。
获取平台列表
// 使用 Appwrite SDK
const result = await databases.listDocuments({
databaseId: "69bd89d8000304c37368",
collectionId: "platforms",
queries: [Query.orderDesc("$createdAt"), Query.limit(100)],
})
相关页面:
获取平台详情
const platform = await databases.getDocument({
databaseId: "69bd89d8000304c37368",
collectionId: "platforms",
documentId: "platform-document-id",
})
相关页面:
更新平台信息
await databases.updateDocument({
databaseId: "69bd89d8000304c37368",
collectionId: "platforms",
documentId: "platform-document-id",
data: {
callback_url: "http://new-callback.com",
token_expires_in: 7200,
},
})
相关页面:
申请创建平台
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(),
},
})
相关页面:
用户管理 API
获取用户授权的平台列表
const result = await databases.listDocuments({
databaseId: "69bd89d8000304c37368",
collectionId: "oauth_tokens",
queries: [Query.equal("user_id", currentUser.$id), Query.equal("revoked", false)],
})
相关页面:
获取平台授权用户列表
const result = await databases.listDocuments({
databaseId: "69bd89d8000304c37368",
collectionId: "oauth_tokens",
queries: [Query.equal("platform_id", "pf_xxx"), Query.equal("revoked", false)],
})
相关页面:
云存储 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. 上传文件
上传文件到云存储,系统会自动验证文件格式和检查存储容量。
POST /api/cloud/upload
请求头:
Authorization: Bearer {access_token}
Content-Type: multipart/form-data
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file | File | ✅ | 要上传的文件 |
| platform_id | string | ✅ | 平台 ID |
请求示例(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,
})
成功响应:
{
"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
}
}
错误响应 - 格式不允许:
{
"error": "invalid_format",
"error_description": "File format 'exe' is not allowed",
"allowed_formats": ["jpg", "png", "pdf"]
}
错误响应 - 超出容量:
{
"error": "storage_exceeded",
"error_description": "Upload would exceed storage limit",
"storage": {
"used": "95MB",
"limit": "100MB",
"available": "5MB",
"requested": "10MB"
}
}
相关页面:
- CloudServiceView.vue - 云服务总览
2. 下载文件
获取文件的下载链接。
GET /api/cloud/download/{file_id}
请求头:
Authorization: Bearer {access_token}
路径参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_id | string | ✅ | 文件 ID |
成功响应:
{
"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. 获取存储信息
获取用户在该平台的存储使用情况和配置信息。
GET /api/cloud/info?platform_id={platform_id}
请求头:
Authorization: Bearer {access_token}
查询参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| platform_id | string | ✅ | 平台 ID |
成功响应:
{
"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. 列出文件
获取用户在该平台的文件列表。
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 |
成功响应:
{
"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. 删除文件
删除指定的文件。
DELETE /api/cloud/files/{file_id}
请求头:
Authorization: Bearer {access_token}
路径参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_id | string | ✅ | 文件 ID |
成功响应:
{
"success": true,
"message": "File deleted successfully",
"storage": {
"used": "45MB",
"limit": "100MB",
"available": "55MB"
}
}
云存储 SDK
我们提供了 JavaScript SDK 简化云存储 API 的调用。
安装
<script src="https://sectl.top/platform-sdk/cloud-storage.js"></script>
或使用 ES6 模块:
import CloudStorage from "./cloud-storage.js"
初始化
const cloud = new CloudStorage({
baseUrl: "https://sectl.top",
platformId: "your_platform_id",
accessToken: "your_access_token",
})
使用示例
// 上传文件
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 - SDK 源码
远程退登 API
创建退登事件
当用户退出登录时,系统会自动创建退登事件。
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 订阅退登事件:
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()
}
}
)
相关页面:
错误处理
HTTP 状态码
| 状态码 | 说明 |
|---|---|
| 200 | 请求成功 |
| 400 | 请求参数错误 |
| 401 | 未授权,认证失败 |
| 403 | 禁止访问,权限不足 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
错误响应格式
{
"error": "error_code",
"error_description": "详细的错误描述",
"error_uri": "https://docs.example.com/error/error_code"
}
安全建议
- 使用 HTTPS - 所有 API 通信必须使用 HTTPS
- 保护密钥 - 不要在客户端代码中暴露 client_secret
- 验证回调地址 - 确保 redirect_uri 与注册时一致
- 使用状态参数 - 添加 state 参数防止 CSRF 攻击
- 安全存储令牌 - Token 应安全存储,避免泄露
- 及时撤销 - 用户退出时及时撤销令牌
相关页面
- OAuthAuthorizeView.vue - OAuth 授权页面
- AuthCallbackView.vue - 认证回调
- PlatformManagementView.vue - 平台管理
- PlatformDetailView.vue - 平台详情
- PlatformSettingsView.vue - 平台设置
- SecurityView.vue - 安全设置
- LoginPlatformsView.vue - 登录平台列表