feat(auth): add WeChat mini program login endpoint
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>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user