Skip to main content

AOVIS App 登录认证接口规范

Authentication API Specification for Mobile App

版本 1.0 | 2026-04-13 | 适用对象:App 研发团队

注意:App Token 与网页端 Session 完全独立,同一用户可以同时在网页端登录和 App 登录,互不干扰。
Token 格式前缀为 aovis_app_,长度约 100 字符,请按字符串存储,不要做 JWT 解码。

1. 概述

AOVIS App 使用独立于网页端的 Token 体系与后端通信。网页端(aovis.app)采用 Auth.js Cookie Session,原生 App 不使用 Cookie,改用数据库存储的随机 Bearer Token(以下简称 App Token)。

App Token 的完整生命周期如下:

  • 用户在 App 内完成登录(Google / Apple / Email Magic Link 三选一)
  • App 调用颁发接口,后端验证身份后签发 App Token
  • App 将 Token 存入 Keychain(iOS)或 Keystore(Android)
  • 后续所有 API 请求在 Authorization Header 中携带 Token
  • 用户退出登录时调用吊销接口,Token 立即失效

2. Base URL 与通用规范

项目
Base URL(生产)https://aovis.app
Content-Typeapplication/json
字符编码UTF-8
鉴权方式Authorization: Bearer <app_token>
Token 有效期30 天(到期前需换新)
Magic Link 有效期10 分钟,一次性使用

3. 登录流程总览

App 支持三种登录方式,最终都会获得一个 App Token。三条路径的起点不同,终点相同。

登录方式适用场景App 端操作后端接口
Google OAuth用户有 Google 账号调用 Google Sign-In SDK,获取 ID TokenPOST /api/auth/app-token
Apple OAuthiOS 用户,Sign in with Apple调用 Apple Authentication SDK,获取 Identity TokenPOST /api/auth/app-token
Email Magic Link任意邮箱用户(兜底路径)用户输入邮箱,App 请求发送邮件;用户点击邮件链接,App 拦截链接获取 tokenPOST /api/auth/app-magic-link + POST /api/auth/app-magic-link/verify

4. 接口详情

4.1 POST /api/auth/app-token

用于 Google 和 Apple OAuth 路径。App 在原生 SDK 完成 OAuth 后,将平台颁发的 Identity Token 提交给后端,换取 AOVIS App Token。

请求

POST https://aovis.app/api/auth/app-token
Content-Type: application/json

{
"provider": "google" | "apple",
"identity_token": "<Google ID Token 或 Apple Identity Token>",
"platform": "ios" | "android",
"device_label": "iPhone 15 Pro" // 可选,便于后台识别设备
}

字段说明

字段类型必填说明
providerstring固定值 googleapple
identity_tokenstringGoogle:调用 GoogleSignIn.authenticate() 后从 GoogleSignInAuthentication.idToken 获取;Apple:调用 ASAuthorizationAppleIDCredential.identityToken 获取,Base64 解码为字符串
platformstring固定值 iosandroid
device_labelstring可选,传入设备型号字符串,后台展示用

成功响应 200 OK

{
"access_token": "aovis_app_xxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 2592000,
"user_id": "clxxxxxxxxxxxxxxxx",
"email": "user@example.com"
}

错误响应

HTTP 状态error 字段含义
401invalid_tokenidentity_token 验证失败(过期、签名错误、aud 不匹配)
400missing_fields必填字段缺失
400invalid_providerprovider 不是 google 或 apple

Google 路径注意事项

  • identity_token 来源:GoogleSignIn SDK 的 GoogleSignInAuthentication.idToken
  • 后端通过 Google tokeninfo API 验证,无需 App 端做额外操作
  • Google Client ID 需与后端配置一致(AUTH_GOOGLE_ID = 495713473945-5mi26rd9glrv2qu7f6hi6aqnutl36ac2.apps.googleusercontent.com

Apple 路径注意事项

  • identity_token 是 Data 类型,需转为 UTF-8 字符串再传入 JSON
  • 后端通过 Apple JWKS 公钥验证签名,无需 App 传额外参数
  • Apple Bundle ID 需与后端 APPLE_CLIENT_ID 环境变量一致(APPLE_CLIENT_ID = com.aovis.app
  • Sign in with Apple 只在 iOS 14+ 可用,Android 无此选项

4.2 POST /api/auth/app-magic-link

Email Magic Link 路径第一步:触发发送登录邮件。用户输入邮箱后,App 调用此接口,后端发送一封包含一次性链接的邮件。

请求

POST https://aovis.app/api/auth/app-magic-link
Content-Type: application/json

{
"email": "user@example.com"
}

成功响应 200 OK

{ "sent": true }

无论邮箱是否已注册,都返回 200,防止邮箱枚举攻击。App 端直接提示「邮件已发送,请查收」即可。

错误响应

HTTP 状态error 字段含义
400invalid_email邮箱格式不合法

邮件内容

用户收到的邮件主题为 Sign in to AOVIS,邮件内有一个按钮,链接格式为:

https://aovis.app/app-auth?token=aovis_ml_xxxxxxxxxxxxxxxx

链接有效期 10 分钟,只能使用一次。

4.3 POST /api/auth/app-magic-link/verify

Email Magic Link 路径第二步:App 拦截到邮件链接中的 token 后,调用此接口完成验证,换取 App Token。

App 端拦截链接的流程

  • 用户点击邮件中的链接
  • 系统检测到 App 已安装且配置了 Universal Links / App Links,直接唤起 App
  • App 在 onReceiveUniversalLink / onAppLinkReceived 回调中获取完整 URL
  • 从 URL 中解析 query 参数 token

请求

POST https://aovis.app/api/auth/app-magic-link/verify
Content-Type: application/json

{
"token": "aovis_ml_xxxxxxxxxxxxxxxx",
"platform": "ios" | "android",
"device_label": "Pixel 8" // 可选
}

成功响应 200 OK

/api/auth/app-token 成功响应格式完全相同:

{
"access_token": "aovis_app_xxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 2592000,
"user_id": "clxxxxxxxxxxxxxxxx",
"email": "user@example.com"
}

错误响应

HTTP 状态error 字段含义
401invalid_or_expired_tokentoken 不存在、已过期(超过 10 分钟)、或已被使用过

4.4 DELETE /api/auth/app-token

退出登录。调用后当前 Token 立即失效,后续使用该 Token 的请求将返回 401。

请求

DELETE https://aovis.app/api/auth/app-token
Authorization: Bearer aovis_app_xxxxxxxxxxxxxxxx

成功响应 200 OK

{ "revoked": true }

错误响应

HTTP 状态含义
401Token 无效、已过期或已被吊销

5. 携带 Token 调用业务接口

登录成功后,所有需要身份验证的业务接口都通过 Authorization Header 传递 Token:

GET https://aovis.app/api/v1/devices
Authorization: Bearer aovis_app_xxxxxxxxxxxxxxxx
Content-Type: application/json

如果 Token 无效或过期,接口统一返回:

HTTP 401 Unauthorized

{
"error": "unauthorized"
}

收到 401 后,App 应清除本地存储的 Token,跳转到登录页面,引导用户重新登录。

为使 Email Magic Link 能直接唤起 App,需要在 App 端完成以下配置。后端已部署所需的验证文件,App 端只需声明对应 domain。

在 Xcode 中启用 Associated Domains

  • 打开项目 Target → Signing & Capabilities
  • 点击 + 添加 Associated Domains
  • 添加条目:applinks:aovis.app

处理链接回调

// AppDelegate.swift
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL,
url.host == "aovis.app",
url.path.hasPrefix("/app-auth") else { return false }
let token = URLComponents(url: url, resolvingAgainstBaseURL: false)
?.queryItems?.first(where: { $0.name == "token" })?.value
// 将 token 传递给登录模块
NotificationCenter.default.post(name: .magicLinkReceived, object: token)
return true
}

AndroidManifest.xml 中声明 intent-filter

<activity android:name=".MainActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="aovis.app"
android:pathPrefix="/app-auth" />
</intent-filter>
</activity>

在 Activity 中处理 Intent

// MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIntent(intent)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
}

private fun handleIntent(intent: Intent) {
val uri = intent.data ?: return
if (uri.host == "aovis.app" && uri.path?.startsWith("/app-auth") == true) {
val token = uri.getQueryParameter("token")
// 将 token 传递给登录 ViewModel
}
}

联调前需将正式发布证书的 SHA-256 指纹告知 AOVIS 后端团队,后端将更新 https://aovis.app/.well-known/assetlinks.json 中的指纹配置。开发阶段可先用调试证书指纹联调,发布前替换为正式证书指纹。

7. 错误码汇总

HTTP 状态error 字段触发场景App 端建议处理
400missing_fields请求体缺少必填字段检查请求构造逻辑
400invalid_email邮箱格式不合法在 UI 层提前做格式校验
400invalid_providerprovider 字段值不合法检查请求构造逻辑
401invalid_tokenGoogle/Apple identity_token 验证失败引导用户重新进行 OAuth 流程
401invalid_or_expired_tokenMagic Link token 无效、过期或已使用提示用户重新请求邮件
401unauthorizedApp Token 无效或过期清除本地 Token,跳转登录页
500服务器内部错误展示通用错误提示,可重试

8. 联调步骤建议

建议按以下顺序完成接入联调,每步验证通过后再进入下一步:

步骤验证内容工具
Step 1POST /api/auth/app-token(Google):使用 Google OAuth Playground 获取测试 ID Token,通过 Postman 调用接口,确认返回 200 + access_tokenGoogle OAuth Playground + Postman
Step 2携带 access_token 调用任意业务接口(如 GET /api/v1/devices),确认鉴权通过返回 200Postman
Step 3DELETE /api/auth/app-token 吊销 Token,再次用该 Token 调业务接口确认返回 401Postman
Step 4POST /api/auth/app-magic-link 发送邮件,确认收到邮件且链接格式正确Postman + 真实邮箱
Step 5POST /api/auth/app-magic-link/verify 提交从邮件链接中提取的 token,确认返回 200 + access_tokenPostman
Step 6App 端集成 Universal Links / App Links,真机点击邮件链接确认 App 被唤起且 token 正确获取真机测试
Step 7POST /api/auth/app-token(Apple):iOS 真机完成 Sign in with Apple 流程,确认返回 200 + access_tokeniOS 真机

Fifth Key Element Co., Limited | AOVIS Internal Confidential | Version 1.0 | 2026-04-13