fix(auth): fix Android deep link intent URL format for Google OAuth

The intent:// URL in renderTauriDeepLinkPage was intent:/path (single slash),
producing zeavisedu:/login?token=xxx — a non-hierarchical URL that new URL()
cannot parse. Fixed to intent://login/path which produces a proper
hierarchical URI (zeavisedu://login/login?token=xxx).

Also hardened setupDeepLinkHandler to handle both double-slash (://) and
single-slash (:/) custom-scheme URLs as fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-16 02:44:33 +07:00
co-authored by Claude
parent ee02bd4f9a
commit e2742a7a60
2 changed files with 15 additions and 8 deletions
+3 -1
View File
@@ -80,7 +80,9 @@ function renderTauriDeepLinkPage(targetUrl: string): Response {
const escapedPath = pathAndQuery.replace(/"/g, '&quot;'); const escapedPath = pathAndQuery.replace(/"/g, '&quot;');
// intent:// scheme: Chrome on Android opens the target app by package name // intent:// scheme: Chrome on Android opens the target app by package name
// browser_fallback_url: shown if the app isn't installed // browser_fallback_url: shown if the app isn't installed
const intentUrl = `intent:${escapedPath}#Intent;scheme=zeavisedu;package=com.zeavis.edu;S.browser_fallback_url=${encodeURIComponent(targetUrl)};end`; // Use intent://login/... to produce data URI zeavisedu://login/login?token=xxx
// which new URL() can parse (single-slash non-hierarchical URLs break WebView)
const intentUrl = `intent://login${escapedPath}#Intent;scheme=zeavisedu;package=com.zeavis.edu;S.browser_fallback_url=${encodeURIComponent(targetUrl)};end`;
const html = `<!DOCTYPE html> const html = `<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+10 -5
View File
@@ -35,21 +35,26 @@ export async function setupDeepLinkHandler(): Promise<void> {
const { onOpenUrl } = await import('@tauri-apps/plugin-deep-link'); const { onOpenUrl } = await import('@tauri-apps/plugin-deep-link');
onOpenUrl((urls) => { onOpenUrl((urls) => {
for (const url of urls) { for (const url of urls) {
// url looks like: zeavisedu://zeavisedu.asepharyana.my.id/login?token=xxx // url looks like: zeavisedu://login/login?token=xxx
// Extract path + query after the host // Extract path + query after the host
try { try {
const u = new URL(url); const u = new URL(url);
const target = u.pathname + u.search + u.hash; const target = u.pathname + u.search + u.hash;
if (target && target !== '/') { if (target && target !== '/') {
window.location.href = target; window.location.href = target;
return;
}
} catch { /* try fallback below */ }
// Fallback: extract everything after scheme, handling both // and /
let match = url.match(/^[^:]+:\/\/(?:[^/]+)?(\/.*)?$/);
if (!match) {
// Also handle single-slash non-hierarchical URLs (e.g. zeavisedu:/path)
match = url.match(/^[^:]+:\/(\/.*)?$/);
} }
} catch {
// If URL parsing fails, try to extract everything after the scheme
const match = url.match(/^[^:]+:\/\/(?:[^/]+)?(\/.*)?$/);
if (match?.[1]) { if (match?.[1]) {
window.location.href = match[1]; window.location.href = match[1];
} }
} }
}
}); });
} }