refactor: 融合认证 schemeCode 按平台拆分为独立环境变量
Some checks failed
CI/CD Pipeline / Code Quality (push) Failing after 18s
CI/CD Pipeline / Unit Tests (push) Has been skipped
CI/CD Pipeline / Build & Deploy Test (push) Has been skipped
CI/CD Pipeline / Build & Deploy Production (push) Has been skipped

不同端(Android/iOS/Harmony)在阿里云控制台使用不同的方案 Code,
单一 ALIYUN_FUSION_SCHEME_CODE 无法满足多端需求。改为按平台映射
三个独立环境变量,providers 端点也按平台精确判断可用性。

同步更新 API 文档:补充 fusion/token 请求参数表和 Harmony 平台支持。
This commit is contained in:
Wang Zhuoxuan 2026-05-28 23:43:21 +08:00
parent b6fc6c5a9b
commit 7682bb2ae7
6 changed files with 58 additions and 15 deletions

View File

@ -27,7 +27,9 @@ OSS_REGION=
# RAM 子用户需授予 AliyunDypnsFullAccess 权限
ALIYUN_ACCESS_KEY_ID=
ALIYUN_ACCESS_KEY_SECRET=
ALIYUN_FUSION_SCHEME_CODE=
ALIYUN_FUSION_SCHEME_CODE_ANDROID=
ALIYUN_FUSION_SCHEME_CODE_IOS=
ALIYUN_FUSION_SCHEME_CODE_HARMONY=
# Huawei IAP (Phase 1c)
HUAWEI_IAP_URL=

View File

@ -25,6 +25,15 @@ OSS_ACCESS_KEY_SECRET=
OSS_BUCKET=duoqi-assets
OSS_REGION=oss-cn-hangzhou
# Alibaba Cloud Fusion Auth
# 阿里云号码认证服务,用于手机号一键登录和短信验证码登录
# RAM 子用户需授予 AliyunDypnsFullAccess 权限
ALIYUN_ACCESS_KEY_ID=
ALIYUN_ACCESS_KEY_SECRET=
ALIYUN_FUSION_SCHEME_CODE_ANDROID=
ALIYUN_FUSION_SCHEME_CODE_IOS=
ALIYUN_FUSION_SCHEME_CODE_HARMONY=
# Huawei IAP
HUAWEI_IAP_URL=https://subscr-drcn.iap.hicloud.com
HUAWEI_MERCHANT_ID=

View File

@ -274,9 +274,24 @@
认证:无
限流10 次/分钟
用途:获取阿里云融合认证 SDK 鉴权 Token。客户端在初始化 SDK 前调用此接口。
用途:获取阿里云融合认证 SDK 鉴权 Token。客户端在初始化 SDK 前调用此接口。`schemeCode` 和 `durationSeconds` 由服务端配置,不暴露给客户端。
请求无请求体schemeCode 从服务端配置读取)。
请求:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `platform` | `"Android"` \| `"iOS"` \| `"Harmony"` | 是 | 客户端平台 |
| `packageName` | string | Android 必填 | App 包名 |
| `packageSign` | string | Android 必填 | App 包签名MD5去冒号小写 |
| `bundleId` | string | iOS 必填 | App Bundle ID |
```json
{
"platform": "Android",
"packageName": "com.example.app",
"packageSign": "a1b2c3d4e5f6..."
}
```
响应:
@ -297,7 +312,7 @@
"success": false,
"data": null,
"error": {
"code": "UNAUTHORIZED",
"code": "SERVICE_UNAVAILABLE",
"message": "Fusion auth is not configured on the server"
}
}

View File

@ -37,7 +37,7 @@ const fusionVerifySchema = z.object({
});
const fusionTokenSchema = z.object({
platform: z.enum(['Android', 'iOS']),
platform: z.enum(['Android', 'iOS', 'Harmony']),
packageName: z.string().optional(),
packageSign: z.string().optional(),
bundleId: z.string().optional(),
@ -78,11 +78,20 @@ const PROVIDER_METADATA: Record<string, { name: string; type: ProviderEntry['typ
const CREDENTIAL_PROVIDERS = new Set(['fusion', 'phone_sms', 'huawei', 'apple']);
const DB_TOGGLE_PROVIDERS = new Set(['wechat', 'qq']);
function isCredentialConfigured(providerId: string): boolean {
function isCredentialConfigured(providerId: string, platform?: Platform): boolean {
switch (providerId) {
case 'fusion':
case 'phone_sms':
return !!(config.ALIYUN_ACCESS_KEY_ID && config.ALIYUN_ACCESS_KEY_SECRET && config.ALIYUN_FUSION_SCHEME_CODE);
case 'phone_sms': {
const hasCredentials = !!(config.ALIYUN_ACCESS_KEY_ID && config.ALIYUN_ACCESS_KEY_SECRET);
if (!hasCredentials) return false;
if (!platform) return true;
const schemeCodeMap: Record<Platform, string | undefined> = {
android: config.ALIYUN_FUSION_SCHEME_CODE_ANDROID,
ios: config.ALIYUN_FUSION_SCHEME_CODE_IOS,
harmony: config.ALIYUN_FUSION_SCHEME_CODE_HARMONY,
};
return !!schemeCodeMap[platform];
}
case 'huawei':
return !!(config.HUAWEI_CLIENT_ID && config.HUAWEI_CLIENT_SECRET);
case 'apple':
@ -109,7 +118,7 @@ function buildProviderList(platform: Platform, dbToggles: Map<string, boolean>):
if (!PLATFORM_AVAILABILITY[id]?.includes(platform)) continue;
const enabled = CREDENTIAL_PROVIDERS.has(id)
? isCredentialConfigured(id)
? isCredentialConfigured(id, platform)
: DB_TOGGLE_PROVIDERS.has(id)
? (dbToggles.get(id) ?? false)
: false;

View File

@ -29,11 +29,17 @@ function getClient() {
return clientInstance;
}
function requireSchemeCode(): string {
if (!config.ALIYUN_FUSION_SCHEME_CODE) {
throw new UnauthorizedError('Fusion auth scheme code is not configured');
function requireSchemeCode(platform: string): string {
const schemeCodeMap: Record<string, string | undefined> = {
Android: config.ALIYUN_FUSION_SCHEME_CODE_ANDROID,
iOS: config.ALIYUN_FUSION_SCHEME_CODE_IOS,
Harmony: config.ALIYUN_FUSION_SCHEME_CODE_HARMONY,
};
const schemeCode = schemeCodeMap[platform];
if (!schemeCode) {
throw new UnauthorizedError(`Fusion auth scheme code not configured for platform: ${platform}`);
}
return config.ALIYUN_FUSION_SCHEME_CODE;
return schemeCode;
}
export async function getFusionAuthToken(options: {
@ -43,7 +49,7 @@ export async function getFusionAuthToken(options: {
bundleId?: string;
}): Promise<string> {
const client = getClient();
const schemeCode = requireSchemeCode();
const schemeCode = requireSchemeCode(options.platform);
const request = new $Dypnsapi20170525.GetFusionAuthTokenRequest({
schemeCode,

View File

@ -18,7 +18,9 @@ const envSchema = z.object({
APPLE_BUNDLE_ID: z.string().optional(),
ALIYUN_ACCESS_KEY_ID: z.string().optional(),
ALIYUN_ACCESS_KEY_SECRET: z.string().optional(),
ALIYUN_FUSION_SCHEME_CODE: z.string().optional(),
ALIYUN_FUSION_SCHEME_CODE_ANDROID: z.string().optional(),
ALIYUN_FUSION_SCHEME_CODE_IOS: z.string().optional(),
ALIYUN_FUSION_SCHEME_CODE_HARMONY: z.string().optional(),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),