/** * 新增 OAuth provider 客户端契约。 * * 覆盖参考主题有、而打包 SDK 未提供的五个平台:抖音、Microsoft、 * Facebook、Twitter、Apple。断言授权 URL 的关键参数、身份归一化结果, * 以及错误响应必须抛出而不是静默返回空身份。 */ 'use strict'; const assert = require('node:assert/strict'); const { spawnSync } = require('node:child_process'); const path = require('node:path'); const test = require('node:test'); const root = path.resolve(__dirname, '..', '..'); function runHarness() { const result = spawnSync( process.env.PHP_BINARY || 'php', [path.join(__dirname, 'oauth-provider-client-harness.php')], { cwd: root, encoding: 'utf8' }, ); assert.equal(result.status, 0, result.stderr || result.stdout); return JSON.parse(result.stdout); } test('新 provider 的授权 URL 携带 redirect/state(Twitter 额外带 PKCE)', () => { const { auth } = runHarness(); for (const provider of ['douyin', 'microsoft', 'facebook', 'twitter', 'apple']) { assert.equal(auth[provider].has_redirect, true, provider + ' redirect_uri'); assert.equal(auth[provider].has_state, true, provider + ' state'); } // 只有 Twitter 走 PKCE,其余平台不带 code_challenge。 assert.match(auth.twitter.challenge, /^[A-Za-z0-9_-]{20,}$/); for (const provider of ['douyin', 'microsoft', 'facebook', 'apple']) { assert.equal(auth[provider].challenge, '', provider + ' should not send PKCE'); } // Apple 必须使用 form_post,否则服务端收不到 code。 assert.match(auth.apple.url, /appleid\.apple\.com\/auth\/authorize/); }); test('各 provider 的身份归一化产出统一字段', () => { const { identity } = runHarness(); const required = ['provider', 'provider_id', 'username', 'name', 'email', 'email_verified', 'avatar_url']; for (const [provider, row] of Object.entries(identity)) { for (const key of required) { assert.ok(key in row, `${provider} missing ${key}`); } assert.ok(row.provider_id !== '', `${provider} provider_id must not be empty`); assert.equal(row.provider, provider); } assert.equal(identity.douyin.provider_id, 'dy-open-id'); assert.equal(identity.douyin.name, '抖音用户'); assert.match(identity.douyin.avatar_url, /^https:/); assert.equal(identity.microsoft.email, 'ms@example.test'); assert.equal(identity.microsoft.email_verified, true); // Facebook 头像原始地址是 http://,必须升级到 https。 assert.match(identity.facebook.avatar_url, /^https:\/\//); assert.equal(identity.twitter.username, 'twuser'); assert.equal(identity.twitter.email, '', 'Twitter 不返回邮箱'); // Apple 的邮箱是私密转发地址,仍可用于自动绑定。 assert.match(identity.apple.email, /@privaterelay\.appleid\.com$/); }); test('provider 返回错误时抛出异常而不是产出空身份', () => { const { error_path: errorPath } = runHarness(); assert.match(errorPath, /^threw: /); });