04ef1c9105
Add jscode2session exchange, CSRF bypass for servicewechat.com, and openid-based auto register/login for the mini program client. Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
1.9 KiB
JavaScript
64 lines
1.9 KiB
JavaScript
import { fetch } from 'undici';
|
|
|
|
const JSCODE2SESSION_URL = 'https://api.weixin.qq.com/sns/jscode2session';
|
|
|
|
export function loadWechatMiniappConfig(env = process.env) {
|
|
const appId =
|
|
env.H5_WECHAT_MINIAPP_APP_ID?.trim() ??
|
|
env.H5_WECHAT_APP_ID?.trim() ??
|
|
'';
|
|
const appSecret =
|
|
env.H5_WECHAT_MINIAPP_APP_SECRET?.trim() ??
|
|
env.H5_WECHAT_APP_SECRET?.trim() ??
|
|
'';
|
|
return {
|
|
enabled: Boolean(appId && appSecret),
|
|
appId,
|
|
appSecret,
|
|
};
|
|
}
|
|
|
|
export async function exchangeMiniProgramCode(
|
|
{ appId, appSecret, code, fetchImpl = fetch } = {},
|
|
) {
|
|
const jsCode = String(code ?? '').trim();
|
|
if (!appId || !appSecret) {
|
|
throw Object.assign(new Error('小程序登录未配置 AppID/AppSecret'), {
|
|
code: 'wechat_miniapp_not_configured',
|
|
});
|
|
}
|
|
if (!jsCode) {
|
|
throw Object.assign(new Error('缺少微信登录 code'), { code: 'invalid_code' });
|
|
}
|
|
|
|
const url = new URL(JSCODE2SESSION_URL);
|
|
url.searchParams.set('appid', appId);
|
|
url.searchParams.set('secret', appSecret);
|
|
url.searchParams.set('js_code', jsCode);
|
|
url.searchParams.set('grant_type', 'authorization_code');
|
|
|
|
const response = await fetchImpl(url);
|
|
const payload = await response.json().catch(() => ({}));
|
|
if (!response.ok) {
|
|
throw Object.assign(new Error('微信登录服务暂不可用'), {
|
|
code: 'wechat_miniapp_http_failed',
|
|
status: response.status,
|
|
});
|
|
}
|
|
if (payload.errcode) {
|
|
throw Object.assign(new Error(payload.errmsg || '微信 code 无效或已过期'), {
|
|
code: 'wechat_miniapp_code_failed',
|
|
errcode: payload.errcode,
|
|
});
|
|
}
|
|
const openid = String(payload.openid ?? '').trim();
|
|
if (!openid) {
|
|
throw Object.assign(new Error('微信未返回 openid'), { code: 'wechat_miniapp_openid_missing' });
|
|
}
|
|
return {
|
|
openid,
|
|
sessionKey: payload.session_key ?? null,
|
|
unionid: payload.unionid ? String(payload.unionid).trim() : null,
|
|
};
|
|
}
|