This commit is contained in:
seb
2026-06-23 11:04:28 +02:00
commit 24f68c94f3
16 changed files with 2580 additions and 0 deletions

97
src/auth/googleSignIn.js Normal file
View File

@@ -0,0 +1,97 @@
const GIS_SCRIPT = 'https://accounts.google.com/gsi/client';
let scriptPromise;
function loadGoogleScript() {
if (window.google?.accounts?.id) return Promise.resolve();
if (scriptPromise) return scriptPromise;
scriptPromise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = GIS_SCRIPT;
script.async = true;
script.defer = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error('Failed to load Google Sign-In'));
document.head.appendChild(script);
});
return scriptPromise;
}
function clickRenderedGoogleButton(clientId, onCredential) {
const container = document.createElement('div');
container.style.cssText = 'position:fixed;left:-9999px;top:0;opacity:0;pointer-events:none;';
document.body.appendChild(container);
window.google.accounts.id.renderButton(container, {
type: 'standard',
size: 'large',
theme: 'outline',
});
const button = container.querySelector('[role="button"]');
if (!button) {
container.remove();
throw new Error('Could not open Google Sign-In');
}
window.google.accounts.id.initialize({
client_id: clientId,
callback: (response) => {
container.remove();
onCredential(response.credential);
},
auto_select: false,
cancel_on_tap_outside: true,
});
button.click();
}
/**
* Prompt for a Google ID token (One Tap, falling back to the sign-in popup).
*/
export async function requestGoogleIdToken(clientId) {
if (!clientId) {
throw new Error('Google client ID is not configured');
}
await loadGoogleScript();
return new Promise((resolve, reject) => {
let settled = false;
const finish = (fn, value) => {
if (settled) return;
settled = true;
fn(value);
};
window.google.accounts.id.initialize({
client_id: clientId,
callback: (response) => finish(resolve, response.credential),
auto_select: false,
cancel_on_tap_outside: true,
});
window.google.accounts.id.prompt((notification) => {
if (settled) return;
const needsFallback =
notification.isNotDisplayed() ||
notification.isSkippedMoment() ||
notification.isDismissedMoment();
if (!needsFallback) return;
try {
clickRenderedGoogleButton(clientId, (credential) =>
finish(resolve, credential),
);
} catch (err) {
finish(reject, err);
}
});
});
}