fix: H5 微信 OAuth 从 wechat_mp 读取 AppId 与回调根
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m27s
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m27s
去掉写死 AppId,跳转前拉取 /wechat/mp/oauth-config,与公众号网页授权域名对齐。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
de4fee4349
commit
99088ad461
@ -9,6 +9,7 @@ import type {
|
||||
WxFollowQrcodeResult,
|
||||
WxLoginQrcodeResult,
|
||||
WxLoginResult,
|
||||
WxOAuthConfigResult,
|
||||
} from './types';
|
||||
|
||||
export * from './types';
|
||||
@ -22,7 +23,9 @@ function unwrapOrSelf<T>(body: unknown): T {
|
||||
'qrCodeUrl' in record ||
|
||||
'jumpUrl' in record ||
|
||||
'accessToken' in record ||
|
||||
'openid' in record
|
||||
'openid' in record ||
|
||||
'appId' in record ||
|
||||
'AppId' in record
|
||||
) {
|
||||
return body as T;
|
||||
}
|
||||
@ -43,6 +46,20 @@ export async function getFollowQrcode(): Promise<WxFollowQrcodeResult> {
|
||||
return unwrapOrSelf<WxFollowQrcodeResult>(res.data);
|
||||
}
|
||||
|
||||
/** H5 网页授权:从其他配置 wechat_mp 读取 AppId / OAuth 回调根 */
|
||||
export async function getWeChatOAuthConfig(): Promise<WxOAuthConfigResult> {
|
||||
const res = await axiosInstance.get(omsUrl('/wechat/mp/oauth-config'));
|
||||
const data = unwrapOrSelf<WxOAuthConfigResult & Record<string, unknown>>(res.data);
|
||||
const appId = String(data.appId ?? data.AppId ?? '').trim();
|
||||
const oauthRedirectBase = String(
|
||||
data.oauthRedirectBase ?? data.OAuthRedirectBase ?? '',
|
||||
).trim();
|
||||
return {
|
||||
appId,
|
||||
oauthRedirectBase: oauthRedirectBase || null,
|
||||
};
|
||||
}
|
||||
|
||||
/** 查询扫码 key 是否已关联账号(轮询用,无值时不抛错) */
|
||||
export async function getWxLoginStatus(loginUuid: string): Promise<WxLoginStatusResult> {
|
||||
const res = await axiosInstance.get('/rbac/auth/wx-login/status', {
|
||||
|
||||
@ -15,6 +15,13 @@ export interface WxFollowQrcodeResult {
|
||||
scene?: string;
|
||||
}
|
||||
|
||||
/** H5 网页授权公开配置(wechat_mp) */
|
||||
export interface WxOAuthConfigResult {
|
||||
appId: string;
|
||||
/** OAuth 回调根,如 https://ideship.com;空则用当前页 origin */
|
||||
oauthRedirectBase?: string | null;
|
||||
}
|
||||
|
||||
export interface WxLoginStatusResult {
|
||||
ready: boolean;
|
||||
account?: string;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/** 微信公众号 H5 网页授权(注册 / 绑定页无 code 时跳转) */
|
||||
|
||||
import { getWeChatOAuthConfig } from '@/api/wechat';
|
||||
import {
|
||||
clearStashedInviteRef,
|
||||
readStashedInviteRef,
|
||||
@ -8,11 +9,45 @@ import {
|
||||
|
||||
export type WeChatH5Page = 'register' | 'bind' | 'login';
|
||||
|
||||
const DEFAULT_MP_APP_ID = 'wxd5141ba46c856e1f';
|
||||
type WeChatOAuthRuntimeConfig = {
|
||||
appId: string;
|
||||
/** 规范化后的 origin,如 https://ideship.com;空则用当前页 */
|
||||
redirectOrigin: string;
|
||||
};
|
||||
|
||||
export function resolveWeChatMpAppId(): string {
|
||||
const fromEnv = import.meta.env.VITE_WECHAT_MP_APPID as string | undefined;
|
||||
return fromEnv?.trim() || DEFAULT_MP_APP_ID;
|
||||
let oauthConfigPromise: Promise<WeChatOAuthRuntimeConfig> | null = null;
|
||||
|
||||
function normalizeRedirectOrigin(base: string | null | undefined): string {
|
||||
const raw = String(base ?? '').trim();
|
||||
if (!raw) return '';
|
||||
try {
|
||||
const url = new URL(raw.includes('://') ? raw : `https://${raw}`);
|
||||
return url.origin;
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/** 从其他配置 wechat_mp 取 AppId / OAuth 回调根(进程内缓存) */
|
||||
export async function resolveWeChatMpOAuthConfig(): Promise<WeChatOAuthRuntimeConfig> {
|
||||
if (!oauthConfigPromise) {
|
||||
oauthConfigPromise = getWeChatOAuthConfig()
|
||||
.then((cfg) => {
|
||||
const appId = String(cfg.appId ?? '').trim();
|
||||
if (!appId) {
|
||||
throw new Error('未配置微信公众号 AppId(其他配置 wechat_mp.AppKey)');
|
||||
}
|
||||
return {
|
||||
appId,
|
||||
redirectOrigin: normalizeRedirectOrigin(cfg.oauthRedirectBase),
|
||||
};
|
||||
})
|
||||
.catch((err) => {
|
||||
oauthConfigPromise = null;
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
return oauthConfigPromise;
|
||||
}
|
||||
|
||||
/** @deprecated 使用 stashInviteRef */
|
||||
@ -59,25 +94,27 @@ export function resolveWeChatInviteRef(query: Record<string, unknown>): string {
|
||||
return readStashedInviteRef();
|
||||
}
|
||||
|
||||
function resolveOAuthRedirectPath(page: WeChatH5Page): string {
|
||||
function resolveOAuthRedirectPath(page: WeChatH5Page, redirectOrigin: string): string {
|
||||
const origin = redirectOrigin || window.location.origin;
|
||||
if (page === 'login') {
|
||||
return `${window.location.origin}/h5/login`;
|
||||
return `${origin}/h5/login`;
|
||||
}
|
||||
return `${window.location.origin}/wechat/${page}`;
|
||||
return `${origin}/wechat/${page}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转微信 OAuth。
|
||||
* - AppId / 回调根来自其他配置 wechat_mp(AppKey、BaseUrl)
|
||||
* - register/bind:邀请码写入 state + 7 天暂存
|
||||
* - login:可选把登录后回跳路径写入 state(login:/h5/orders)
|
||||
*/
|
||||
export function redirectToWeChatOAuth(
|
||||
export async function redirectToWeChatOAuth(
|
||||
page: WeChatH5Page,
|
||||
inviteCode?: string | null,
|
||||
options?: { afterLoginPath?: string | null },
|
||||
): void {
|
||||
const appId = resolveWeChatMpAppId();
|
||||
const redirectUri = encodeURIComponent(resolveOAuthRedirectPath(page));
|
||||
): Promise<void> {
|
||||
const { appId, redirectOrigin } = await resolveWeChatMpOAuthConfig();
|
||||
const redirectUri = encodeURIComponent(resolveOAuthRedirectPath(page, redirectOrigin));
|
||||
|
||||
let state = page;
|
||||
if (page === 'login') {
|
||||
|
||||
@ -31,14 +31,19 @@ const label = computed(() =>
|
||||
jumping.value ? t('h5.login.oauthRedirecting') : t('h5.login.oauthLogin'),
|
||||
);
|
||||
|
||||
function handleClick() {
|
||||
async function handleClick() {
|
||||
if (jumping.value || props.disabled) return;
|
||||
if (!inWeChat.value) {
|
||||
Message.warning(t('h5.login.openInWechat'));
|
||||
return;
|
||||
}
|
||||
jumping.value = true;
|
||||
redirectToWeChatOAuth('login', null, { afterLoginPath: props.afterLoginPath });
|
||||
try {
|
||||
await redirectToWeChatOAuth('login', null, { afterLoginPath: props.afterLoginPath });
|
||||
} catch (e: any) {
|
||||
jumping.value = false;
|
||||
Message.error(e?.message || t('h5.login.oauthFailed'));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -176,7 +176,9 @@ function goRegister() {
|
||||
Message.warning(t('h5.login.openInWechat'));
|
||||
return;
|
||||
}
|
||||
redirectToWeChatOAuth('register');
|
||||
void redirectToWeChatOAuth('register').catch((e: any) => {
|
||||
Message.error(e?.message || t('h5.login.oauthFailed'));
|
||||
});
|
||||
}
|
||||
|
||||
function openAgreement(type: 'privacy' | 'terms') {
|
||||
@ -304,7 +306,12 @@ onMounted(async () => {
|
||||
|
||||
if (isWeChatBrowser() && route.query.auto === '1') {
|
||||
oauthBusy.value = true;
|
||||
redirectToWeChatOAuth('login', null, { afterLoginPath: resolveRedirectPath() });
|
||||
try {
|
||||
await redirectToWeChatOAuth('login', null, { afterLoginPath: resolveRedirectPath() });
|
||||
} catch (e: any) {
|
||||
oauthBusy.value = false;
|
||||
Message.error(e?.message || t('h5.login.oauthFailed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -549,7 +549,9 @@ function bindWechat() {
|
||||
Message.warning(t('h5.me.bindWechatOpenInWechat'));
|
||||
return;
|
||||
}
|
||||
redirectToWeChatOAuth('bind');
|
||||
void redirectToWeChatOAuth('bind').catch((e: any) => {
|
||||
Message.error(e?.message || t('h5.me.bindWechatOpenInWechat'));
|
||||
});
|
||||
}
|
||||
|
||||
function openOnlineKf() {
|
||||
|
||||
@ -181,7 +181,9 @@ function goCustomerCenter() {
|
||||
|
||||
function goLogin() {
|
||||
if (isWeChatBrowser()) {
|
||||
redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' });
|
||||
void redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' }).catch(() => {
|
||||
router.replace('/h5/login');
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.replace('/h5/login');
|
||||
@ -189,7 +191,9 @@ function goLogin() {
|
||||
|
||||
function goRegister() {
|
||||
if (isWeChatBrowser()) {
|
||||
redirectToWeChatOAuth('register');
|
||||
void redirectToWeChatOAuth('register').catch(() => {
|
||||
router.replace('/wechat/register');
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.replace('/wechat/register');
|
||||
@ -197,7 +201,7 @@ function goRegister() {
|
||||
|
||||
function startOAuth() {
|
||||
authModalVisible.value = false;
|
||||
redirectToWeChatOAuth('bind');
|
||||
void redirectToWeChatOAuth('bind');
|
||||
}
|
||||
|
||||
function retryOAuth() {
|
||||
|
||||
@ -247,7 +247,9 @@ function goCustomerCenter() {
|
||||
|
||||
function goLogin() {
|
||||
if (isWeChatBrowser()) {
|
||||
redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' });
|
||||
void redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' }).catch(() => {
|
||||
router.replace('/h5/login');
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.replace('/h5/login');
|
||||
@ -259,7 +261,9 @@ function goOAuthLogin() {
|
||||
|
||||
function goBind() {
|
||||
if (isWeChatBrowser()) {
|
||||
redirectToWeChatOAuth('bind');
|
||||
void redirectToWeChatOAuth('bind').catch(() => {
|
||||
router.replace('/wechat/bind');
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.replace('/wechat/bind');
|
||||
@ -267,7 +271,7 @@ function goBind() {
|
||||
|
||||
function startOAuth() {
|
||||
authModalVisible.value = false;
|
||||
redirectToWeChatOAuth('register', inviteCode.value);
|
||||
void redirectToWeChatOAuth('register', inviteCode.value);
|
||||
}
|
||||
|
||||
function retryOAuth() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user