const WEBVIEW_TOKEN_STORAGE_KEY = "bmt_webview_token"; let cachedToken = ""; const MAX_NESTED_PARSE_DEPTH = 4; const NESTED_URL_PARAM_KEYS = [ "url", "targetUrl", "target_url", "redirect", "redirectUrl", "redirect_url", "redirectUri", "redirect_uri", "returnUrl", "return_url", "link", "href", ]; const NESTED_URL_PARAM_KEYS_LOWER = NESTED_URL_PARAM_KEYS.map((item) => item.toLowerCase(), ); function safeDecodeURIComponent(value) { if (!value) { return ""; } try { return decodeURIComponent(value); } catch (error) { return String(value); } } function decodeMultiEncodedValue(value) { let currentValue = String(value || ""); if (!currentValue) { return ""; } for (let round = 0; round < 4; round += 1) { const decodedValue = safeDecodeURIComponent(currentValue); if (!decodedValue || decodedValue === currentValue) { break; } currentValue = decodedValue; } return currentValue; } function sanitizeTokenValue(token) { let normalizedToken = decodeMultiEncodedValue(token || "").trim(); if (!normalizedToken) { return ""; } if (/^bearer\s+/i.test(normalizedToken)) { normalizedToken = normalizedToken.replace(/^bearer\s+/i, "").trim(); } const hashIndex = normalizedToken.indexOf("#"); if (hashIndex > -1) { normalizedToken = normalizedToken.slice(0, hashIndex).trim(); } const ampersandIndex = normalizedToken.indexOf("&"); if (ampersandIndex > -1) { normalizedToken = normalizedToken.slice(0, ampersandIndex).trim(); } return normalizedToken; } function parseQueryByRegex(queryText) { const query = String(queryText || "").trim(); if (!query) { return []; } const normalizedQuery = query.replace(/^[?#&/]+/, ""); if (!normalizedQuery) { return []; } const entries = []; const pairs = normalizedQuery.split("&"); for (let index = 0; index < pairs.length; index += 1) { const pair = String(pairs[index] || ""); if (!pair) { continue; } const splitIndex = pair.indexOf("="); if (splitIndex === -1) { continue; } const key = pair.slice(0, splitIndex); const value = pair.slice(splitIndex + 1); if (!key) { continue; } entries.push({ key: key, value: value, }); } return entries; } function findTokenInText(text, depth = 0) { if (depth > MAX_NESTED_PARSE_DEPTH) { return ""; } if (!text) { return ""; } const rawText = String(text || "").trim(); if (!rawText) { return ""; } const candidates = []; const queryIndex = rawText.indexOf("?"); const hashIndex = rawText.indexOf("#"); if (queryIndex > -1) { let queryCandidate = rawText.slice(queryIndex + 1); const hashInQuery = queryCandidate.indexOf("#"); if (hashInQuery > -1) { queryCandidate = queryCandidate.slice(0, hashInQuery); } candidates.push(queryCandidate); } if (hashIndex > -1) { candidates.push(rawText.slice(hashIndex + 1)); } candidates.push(rawText); for (let index = 0; index < candidates.length; index += 1) { let queryText = String(candidates[index] || "").trim(); if (!queryText) { continue; } const nextQueryIndex = queryText.indexOf("?"); if (nextQueryIndex > -1) { queryText = queryText.slice(nextQueryIndex + 1); } queryText = queryText.replace(/^[/#&]+/, ""); if (!queryText || queryText.indexOf("=") === -1) { continue; } try { const search = new URLSearchParams(queryText); const token = search.get("token") || search.get("access_token") || ""; if (token) { return sanitizeTokenValue(token); } for (let keyIndex = 0; keyIndex < NESTED_URL_PARAM_KEYS.length; keyIndex += 1) { const nestedKey = NESTED_URL_PARAM_KEYS[keyIndex]; const nestedValue = search.get(nestedKey) || ""; if (!nestedValue) { continue; } const nestedDecodedValue = decodeMultiEncodedValue(nestedValue); const nestedToken = findTokenInText(nestedValue, depth + 1) || findTokenInText(nestedDecodedValue, depth + 1); if (nestedToken) { return nestedToken; } } } catch (error) { // Continue to fallback regex mode. } const matcher = queryText.match( /(?:^|[?&#/])(token|access_token)=([^&#]+)/i, ); if (matcher && matcher[2]) { return sanitizeTokenValue(matcher[2]); } const fallbackEntries = parseQueryByRegex(queryText); for (let entryIndex = 0; entryIndex < fallbackEntries.length; entryIndex += 1) { const entry = fallbackEntries[entryIndex]; const entryKey = String(entry.key || ""); const entryValue = String(entry.value || ""); if (!entryKey || !entryValue) { continue; } const normalizedEntryKey = entryKey.toLowerCase(); const isTokenKey = normalizedEntryKey === "token" || normalizedEntryKey === "access_token"; if (isTokenKey) { return sanitizeTokenValue(entryValue); } const isNestedUrlKey = NESTED_URL_PARAM_KEYS_LOWER.indexOf(normalizedEntryKey) > -1; if (!isNestedUrlKey) { continue; } const nestedDecodedValue = decodeMultiEncodedValue(entryValue); const nestedToken = findTokenInText(entryValue, depth + 1) || findTokenInText(nestedDecodedValue, depth + 1); if (nestedToken) { return nestedToken; } } } return ""; } export function extractTokenFromUrl(url) { if (!url) { return ""; } const directToken = findTokenInText(url); if (directToken) { return directToken; } const hashIndex = url.indexOf("#"); if (hashIndex === -1) { return ""; } return findTokenInText(url.slice(hashIndex + 1)); } export function getCurrentWebviewUrl() { // #ifdef H5 return window.location.href || ""; // #endif // #ifdef APP-PLUS if (typeof plus === "undefined" || !plus.webview) { return ""; } const currentWebview = plus.webview.currentWebview() || plus.webview.getLaunchWebview(); if (!currentWebview || typeof currentWebview.getURL !== "function") { return ""; } return currentWebview.getURL() || ""; // #endif return ""; } function getH5ReferrerUrl() { // #ifdef H5 if (typeof document !== "undefined") { return String(document.referrer || "").trim(); } // #endif return ""; } function normalizeToken(token) { return sanitizeTokenValue(token); } function saveTokenToStorage(token) { const normalizedToken = normalizeToken(token); if (!normalizedToken) { return ""; } cachedToken = normalizedToken; try { if (typeof uni !== "undefined" && uni.setStorageSync) { uni.setStorageSync(WEBVIEW_TOKEN_STORAGE_KEY, normalizedToken); } } catch (error) { // Ignore storage failures and keep in-memory fallback. } return normalizedToken; } function readTokenFromStorage() { if (cachedToken) { return cachedToken; } try { if (typeof uni !== "undefined" && uni.getStorageSync) { const token = normalizeToken(uni.getStorageSync(WEBVIEW_TOKEN_STORAGE_KEY)); if (token) { cachedToken = token; return token; } } } catch (error) { // Ignore storage failures and fallback to runtime token extraction. } return ""; } export function setCurrentWebviewToken(token) { return saveTokenToStorage(token); } export function refreshCurrentWebviewToken(url) { const candidateUrls = []; const inputUrl = String(url || "").trim(); const currentUrl = String(getCurrentWebviewUrl() || "").trim(); const referrerUrl = getH5ReferrerUrl(); if (inputUrl) { candidateUrls.push(inputUrl); } if (currentUrl && candidateUrls.indexOf(currentUrl) === -1) { candidateUrls.push(currentUrl); } if (referrerUrl && candidateUrls.indexOf(referrerUrl) === -1) { candidateUrls.push(referrerUrl); } for (let index = 0; index < candidateUrls.length; index += 1) { const nextToken = extractTokenFromUrl(candidateUrls[index]); if (nextToken) { return saveTokenToStorage(nextToken); } } return readTokenFromStorage(); } export function getCurrentWebviewToken() { return refreshCurrentWebviewToken(); }