Crea APK gratis

API del bridge JavaScript

Accesso e passkey

Un vero selettore di account Google, nativo anziché con reindirizzamento web, e passkey tramite il Credential Manager di Android: navigator.credentials funziona come in Chrome, oppure usa AppMint.passkey con le opzioni JSON del tuo server.

AppMint.passkey helper#

window.AppMint.passkey — create(options), get(options), isAvailable(), origin(), assetLinks()

Esempio

Passkey (accedi con impronta, volto o blocco schermo, senza password) tramite il gestore passkey di Android, usando le opzioni JSON create dal tuo server di passkey.

Restituisce: create(optionsJSON) → Promise di RegistrationResponseJSON; get(optionsJSON) → Promise di AuthenticationResponseJSON (invia l'uno o l'altro al tuo server per verificarlo). Entrambi vengono rifiutati con un DOMException (err.name, e err.reason = un breve codice). isAvailable() → booleano, origin() → stringa, assetLinks() → array - tutti subito. Richiede: Android 9+ con Google Play services, un server di passkey e i due passaggi di configurazione qui sotto. Nessun interruttore di build.

Accedi:

async function signInWithPasskey() {
  if (!window.AppMint || !window.AppMint.passkey || !window.AppMint.passkey.isAvailable()) {
    alert('Passkeys are not available on this phone.');
    return;
  }
  var options = await (await fetch('https://your-domain.com/passkey/login-options')).json();
  var answer;
  try {
    answer = await window.AppMint.passkey.get(options);   // rpId must be your domain
  } catch (err) {
    if (err.name === 'NotAllowedError') return;          // cancelled, or no passkey on this phone
    alert('Passkey problem: ' + err.message);
    return;
  }
  var res = await fetch('https://your-domain.com/passkey/login-verify', {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(answer)
  });
  if (res.ok) showAccount();
}

Creane uno (dopo che l'utente ha effettuato l'accesso in un altro modo):

async function addPasskey() {
  var options = await (await fetch('https://your-domain.com/passkey/register-options')).json();
  try {
    var answer = await window.AppMint.passkey.create(options);
    await fetch('https://your-domain.com/passkey/register-verify', {
      method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(answer)
    });
  } catch (err) {
    if (err.name === 'InvalidStateError') alert('You already have a passkey here.');
    else if (err.name !== 'NotAllowedError') alert('Passkey problem: ' + err.message);
  }
}

Da fare una sola volta: esegui questo nell'app installata DA Google Play (Play firma con la propria chiave):

console.log(JSON.stringify(window.AppMint.passkey.assetLinks(), null, 2));
// save as https://your-domain.com/.well-known/assetlinks.json
console.log(window.AppMint.passkey.origin());
// android:apk-key-hash:...  — add to your server's expected origins

Note: err.name / err.reason: NotAllowedError (cancelled, no_credential, interrupted), InvalidStateError (esiste già), SecurityError con not_linked (assetlinks.json non elenca l'app) oppure rp_mismatch (il rpId non è il dominio di questa pagina), NotSupportedError / unavailable (Android < 9 o senza Play services), TypeError / invalid_options (una pagina inclusa nell'app non ha un dominio, quindi imposta rp.id / rpId sul tuo dominio). window.AppMint.passkey esiste solo nell'app; in un browser usa navigator.credentials (lo stesso codice funziona anche nell'app).

WebToApkAuth.signInWithGoogle helper#

window.WebToApkAuth.signInWithGoogle(webClientId)

Esempio

Fa accedere l'utente con il suo account Google tramite il selettore di account di Android e ti restituisce un ID token Google. Google blocca la sua pagina di accesso nelle WebView delle app («disallowed_useragent»); questo percorso nativo non è bloccato.

Restituisce: una Promise di {ok:true, idToken, email, displayName, profilePictureUri}. Viene RIFIUTATA con un Error il cui message è il codice (cancelled, google_play_services_unavailable, missing_web_client_id, failed, …). Richiede: il tuo Web client ID OAuth (termina con .apps.googleusercontent.com) e le impronte SHA-1 e SHA-256 dell'app registrate nel tuo progetto Google / Firebase (dal file _signkey_info.txt della tua build e, se pubblichi su Play, la chiave di firma dell'app di Play). Nessun interruttore di build.

Con Firebase Authentication (SDK compat):

var WEB_CLIENT_ID = '1234567890-abc.apps.googleusercontent.com';

document.getElementById('google-button').addEventListener('click', async function () {
  if (!window.WebToApkAuth) {
    alert('Google sign-in works in the app.');     // or run your normal web sign-in here
    return;
  }
  try {
    var r = await window.WebToApkAuth.signInWithGoogle(WEB_CLIENT_ID);
    var cred = firebase.auth.GoogleAuthProvider.credential(r.idToken);
    await firebase.auth().signInWithCredential(cred);
    showWelcome(r.displayName || r.email);
  } catch (err) {
    if (err.message === 'cancelled') return;                   // user closed the picker
    if (err.message === 'google_play_services_unavailable') {
      alert('No Google account or Google Play services on this phone.');
      return;
    }
    alert('Google sign-in failed: ' + err.message);
  }
});

Con un tuo server: invia l'ID token e verificalo lì (non fidarti mai di esso solo nella pagina):

async function signInWithMyServer() {
  var r = await window.WebToApkAuth.signInWithGoogle(WEB_CLIENT_ID);
  await fetch('https://your-domain.com/auth/google', {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ idToken: r.idToken })
  });
}

Con Supabase Auth (passa l'ID token a signInWithIdToken):

async function signInWithSupabase() {
  if (!window.WebToApkAuth) {
    return supabase.auth.signInWithOAuth({ provider: 'google' });   // normal web sign-in in a browser
  }
  try {
    var r = await window.WebToApkAuth.signInWithGoogle(WEB_CLIENT_ID);
    var res = await supabase.auth.signInWithIdToken({ provider: 'google', token: r.idToken });
    if (res.error) throw res.error;
    showWelcome(res.data.user.email);
  } catch (err) {
    if (err.message !== 'cancelled') alert('Google sign-in failed: ' + err.message);
  }
}

Note: Passa il Web client ID. Senza argomento l'helper usa window.firebaseConfig._googleWebClientId se la tua pagina lo imposta, poi il client Web nel google-services.json che hai caricato per questa app (solo quando quel file è il tuo progetto Firebase con l'accesso Google attivo, mai una build AppMint Push); senza nessuno di questi viene rifiutato con missing_web_client_id. Una pagina che non appartiene all'app (un sito esterno aperto nell'app) viene rifiutata con foreign_origin. Usa il Web client ID, non un client ID Android. «No credentials available» di solito significa che lo SHA-1 non è registrato. Questo helper esiste solo nell'app: in un browser usa il tuo normale accesso web. Chiamalo da un tocco su un pulsante.

WebToApkAuth.signOutGoogle helper#

window.WebToApkAuth.signOutGoogle()

Esempio

Cancella la scelta di accesso Google salvata da Android, così il prossimo signInWithGoogle mostra di nuovo il selettore di account invece di riutilizzare in silenzio l'ultimo account.

Restituisce: una Promise di {ok:true}; viene rifiutata con un Error il cui message dice che cosa è andato storto. Richiede: nulla.

Chiamalo insieme alla tua disconnessione:

document.getElementById('sign-out').addEventListener('click', async function () {
  await firebase.auth().signOut();            // your normal sign-out (Firebase here)
  if (window.WebToApkAuth) {
    try {
      await window.WebToApkAuth.signOutGoogle();
    } catch (err) {
      console.log('Could not clear the Google choice: ' + err.message);
    }
  }
  showSignInScreen();
});

Un pulsante «Cambia account»: cancella la scelta salvata, poi accedi di nuovo così l'utente sceglie un altro account:

async function switchGoogleAccount() {
  if (!window.WebToApkAuth) return;
  await firebase.auth().signOut();
  try { await window.WebToApkAuth.signOutGoogle(); } catch (err) { /* nothing saved to clear */ }
  try {
    var r = await window.WebToApkAuth.signInWithGoogle('1234567890-abc.apps.googleusercontent.com');
    await firebase.auth().signInWithCredential(firebase.auth.GoogleAuthProvider.credential(r.idToken));
  } catch (err) {
    if (err.message !== 'cancelled') alert('Google sign-in failed: ' + err.message);
    showSignInScreen();
  }
}

Prima disconnettiti dal tuo server, poi cancella la scelta Google senza lasciare che un errore blocchi la disconnessione:

async function signOutEverywhere() {
  await fetch('https://your-domain.com/auth/logout', { method: 'POST', credentials: 'include' });
  localStorage.removeItem('profile');
  if (window.WebToApkAuth) {
    var r = await window.WebToApkAuth.signOutGoogle()
      .catch(function (err) { return { ok: false, error: err.message }; });
    if (!r.ok) console.log('Google choice not cleared: ' + r.error);
  }
  location.replace('/login');
}

Note: Questo non termina la sessione della tua app: disconnetti tu l'utente dal tuo backend. Reimposta solo quale account Google il telefono propone la volta successiva.

navigator.credentials standard web#

navigator.credentials.create / get({ publicKey }) — passkeys

Esempio

Chiamate passkey WebAuthn standard. Una normale WebView Android non può eseguirle; nell'app vanno al gestore passkey di Android, quindi il codice e le librerie scritti per Chrome (SimpleWebAuthn, Hanko, Corbado, Auth0) funzionano senza modifiche.

Restituisce: una Promise di un PublicKeyCredential (con .toJSON()), esattamente come Chrome; viene rifiutata con un DOMException. Richiede: Android 9+ con Google Play services, un server di passkey, il assetlinks.json del tuo dominio che elenca l'app e il tuo server che accetta l'origine dell'app (vedi AppMint.passkey). Nessun interruttore di build.

Accedi con l'API standard (opzioni dal tuo server, campi binari come ArrayBuffer):

async function signIn() {
  if (!window.PublicKeyCredential) { alert('Passkeys are not supported here.'); return; }
  var ok = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
  if (!ok) { alert('This phone cannot use passkeys.'); return; }

  try {
    var json = await (await fetch('https://your-domain.com/passkey/login-options')).json();
    var publicKey = PublicKeyCredential.parseRequestOptionsFromJSON(json);  // provided in the app too
    var cred = await navigator.credentials.get({ publicKey: publicKey });
    await fetch('https://your-domain.com/passkey/login-verify', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(cred.toJSON())
    });
  } catch (err) {
    if (err.name !== 'NotAllowedError') alert('Passkey problem: ' + err.message);
  }
}

Registra una passkey:

async function register() {
  if (!window.PublicKeyCredential) return;
  try {
    var json = await (await fetch('https://your-domain.com/passkey/register-options')).json();
    var cred = await navigator.credentials.create({
      publicKey: PublicKeyCredential.parseCreationOptionsFromJSON(json)
    });
    await fetch('https://your-domain.com/passkey/register-verify', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(cred.toJSON())
    });
  } catch (err) {
    if (err.name === 'InvalidStateError') alert('You already have a passkey here.');
    else if (err.name !== 'NotAllowedError') alert('Passkey problem: ' + err.message);
  }
}

Decidi quali pulsanti di accesso mostrare in base a ciò che il telefono supporta (il completamento automatico delle passkey è disattivato nell'app, quindi tieni un pulsante):

async function setupSignInButtons() {
  if (!window.PublicKeyCredential) return;
  var caps = typeof PublicKeyCredential.getClientCapabilities === 'function'
    ? await PublicKeyCredential.getClientCapabilities() : {};
  var platform = caps.passkeyPlatformAuthenticator ||
    await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
  document.getElementById('passkey-button').hidden = !platform;
  if (caps.conditionalGet) startPasskeyAutofill();   // false in the app; true in some browsers
}

Note: Il completamento automatico delle passkey in un campo di testo (mediation: 'conditional') non è disponibile nell'app: isConditionalMediationAvailable() risponde false e la chiamata viene rifiutata con NotSupportedError; avvia l'accesso da un pulsante. signal (AbortController) funziona. Una pagina inclusa nell'app non ha un dominio proprio: imposta rp.id / rpId sul tuo dominio. Le chiamate senza publicKey (password, OTP) vanno alla WebView come prima.

OTPCredential standard web#

navigator.credentials.get({ otp: { transport: ['sms'] } }) — WebOTP

Esempio

L'API standard WebOTP: il codice di accesso ricevuto via SMS si compila da solo. Android mostra la schermata di Google «Consentire a questa app di leggere questo messaggio?»: nessuna autorizzazione SMS.

Restituisce: una Promise di un OTPCredential {type:'otp', code}. Viene rifiutata con AbortError quando l'utente tocca Nega o il segnale annulla, con InvalidStateError dopo cinque minuti senza SMS, con NotSupportedError senza Google Play services. Richiede: nulla da attivare. L'SMS deve TERMINARE con una riga che indica l'host di questa pagina: @<host> #<code>.

const input = document.querySelector('input[autocomplete="one-time-code"]');

if ('OTPCredential' in window) {
  const ac = new AbortController();
  document.querySelector('form').addEventListener('submit', () => ac.abort());
  navigator.credentials.get({ otp: { transport: ['sms'] }, signal: ac.signal })
    .then((otp) => { input.value = otp.code; input.form.requestSubmit(); })
    .catch((e) => console.log('Type the code instead:', e.name));
}

L'SMS che invia il tuo server (l'ultima riga è il vincolo):

Your Acme code is 482913.

@acme.example #482913

Dopo «Invia di nuovo il codice», avvia una nuova richiesta (sostituisce quella ancora in attesa, che viene rifiutata con AbortError):

async function resendAndWait(phone) {
  await fetch('/api/send-code', {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ phone })
  });
  if (!('OTPCredential' in window)) return;
  try {
    const otp = await navigator.credentials.get({ otp: { transport: ['sms'] } });
    await verifyCode(otp.code);
  } catch (e) {
    if (e.name === 'InvalidStateError') showMessage('No SMS yet. Tap Resend to try again.');
  }
}

Note: l'host deve essere l'host della pagina stessa: per un'app basata su sito web è il tuo dominio (l'SMS che Chrome già legge); per un'app offline/creata con l'AI è appassets.androidplatform.net. Un messaggio per un altro host viene ignorato e l'app continua ad attendere. Solo pagina principale (i codici legati a un'origine incorporata, @top #code @iframe, non sono supportati). Solo una richiesta alla volta è in attesa; una più recente annulla la precedente. Evento che ne deriva: appmint:otp.

__otpCancel bridge#

window.WebToApk.__otpCancel(requestId: String)

Abbandona una richiesta WebOTP in sospeso (AbortSignal, o una richiesta più recente l'ha sostituita).

Esempio

Trasporto interno dietro l'annullamento di una richiesta WebOTP (AbortSignal). Le pagine usano l'API standard; non chiamare mai questo direttamente: i metodi __ possono cambiare senza preavviso.

Restituisce: niente. Richiede: nulla da attivare.

Usa l'API pubblica:

const ac = new AbortController();
const cred = await navigator.credentials.get({ otp: { transport: ['sms'] }, signal: ac.signal });
codeInput.value = cred.code;
ac.abort();   // the pending get() rejects with AbortError

Smetti di attendere non appena l'utente inizia a digitare il codice da solo:

const input = document.querySelector('input[autocomplete="one-time-code"]');
const ac = new AbortController();
input.addEventListener('input', () => ac.abort(), { once: true });

navigator.credentials.get({ otp: { transport: ['sms'] }, signal: ac.signal })
  .then((cred) => { input.value = cred.code; })
  .catch((e) => { if (e.name !== 'AbortError') console.log('No code:', e.name); });

Annulla l'attesa quando l'utente torna indietro per cambiare il numero di telefono:

let otpAbort = null;

function waitForSms() {
  otpAbort = new AbortController();
  return navigator.credentials.get({ otp: { transport: ['sms'] }, signal: otpAbort.signal });
}

document.getElementById('change-number').addEventListener('click', () => {
  if (otpAbort) { otpAbort.abort(); otpAbort = null; }   // the pending get() rejects with AbortError
  showPhoneStep();
});

Note: Implementato nella shell dell'app (WebApiPolyfills.kt). Lo stesso codice della pagina funziona senza modifiche in Chrome.

__otpStart bridge#

window.WebToApk.__otpStart(requestId: String)

WebOTP: attende (fino a cinque minuti) un SMS tramite la schermata SMS User Consent di Google, senza autorizzazione SMS. La risposta è `appmint:otp {requestId, message|error}`.

Esempio

Trasporto interno dietro navigator.credentials.get({ otp: { transport: ['sms'] } }). Le pagine usano l'API standard; non chiamare mai questo direttamente: i metodi __ possono cambiare senza preavviso.

Restituisce: niente; la risposta arriva come evento appmint:otp {requestId, message} oppure {requestId, error} (cancelled, timeout, unavailable). Richiede: nulla da attivare.

Usa l'API pubblica:

const ac = new AbortController();
const cred = await navigator.credentials.get({ otp: { transport: ['sms'] }, signal: ac.signal });
codeInput.value = cred.code;

Smetti di attendere dopo un minuto con un segnale di timeout:

async function waitForCode(input) {
  if (!('OTPCredential' in window)) return;
  try {
    const cred = await navigator.credentials.get({
      otp: { transport: ['sms'] },
      signal: AbortSignal.timeout(60000)
    });
    input.value = cred.code;
  } catch (e) {
    // TimeoutError after 60 s, AbortError when the user taps Deny
    showMessage('Type the code from the SMS.');
  }
}

Dì all'utente cosa fare dopo, in base al nome dell'errore:

async function readOtp() {
  try {
    const cred = await navigator.credentials.get({ otp: { transport: ['sms'] } });
    return cred.code;
  } catch (e) {
    if (e.name === 'InvalidStateError') showResendButton();      // no SMS within five minutes
    else if (e.name === 'NotSupportedError') showManualEntry();  // no Google Play services
    return null;                                                  // AbortError: the user tapped Deny
  }
}

Note: Implementato nella shell dell'app (WebApiPolyfills.kt). Lo stesso codice della pagina funziona senza modifiche in Chrome.

passkeyIsAvailable bridge#

window.WebToApk.passkeyIsAvailable(): Boolean

True su Android 9+ con Google Play services: i telefoni che possono contenere una passkey.

Esempio

Ti dice se questo telefono può usare le passkey. Le pagine di solito usano l'API pubblica AppMint.passkey.isAvailable() (oppure PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()), che incapsula questa chiamata.

Restituisce: un booleano, subito - true su Android 9+ con Google Play services e una WebView abbastanza recente per il canale passkey. Richiede: nulla.

L'API pubblica:

var canPasskey = !!(window.AppMint && window.AppMint.passkey && window.AppMint.passkey.isAvailable());
document.getElementById('passkey-button').hidden = !canPasskey;

La chiamata diretta, se ti serve:

var canPasskey = !!window.WebToApk &&
  typeof window.WebToApk.passkeyIsAvailable === 'function' &&
  window.WebToApk.passkeyIsAvailable() === true;

Proponi «Aggiungi una passkey» una sola volta dopo un accesso con password, solo sui telefoni che possono contenerne una (il controllo standard, stesso codice in Chrome):

async function offerPasskeyAfterLogin() {
  var ok = !!window.PublicKeyCredential &&
    await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
  if (!ok) return;                                   // Android below 9 or no Google Play services
  if (localStorage.getItem('passkey-offered')) return;
  localStorage.setItem('passkey-offered', '1');
  document.getElementById('add-passkey-banner').hidden = false;
}

Note: Quando è false, AppMint.passkey.create/get vengono rifiutate con NotSupportedError. Nascondi il pulsante della passkey e offri il tuo altro metodo di accesso.

passkeyOrigin bridge#

window.WebToApk.passkeyOrigin(): String

L'origine che un server di passkey vede da questa app: `android:apk-key-hash:<base64url SHA-256>`.

Esempio

Fornisce l'origine che il tuo server di passkey vede da questa app: android:apk-key-hash:<base64url SHA-256 of the signing certificate>. Le pagine di solito usano l'API pubblica AppMint.passkey.origin(), che incapsula questa chiamata.

Restituisce: una stringa, subito ("" se il certificato non può essere letto). Richiede: nulla.

Eseguilo una volta nell'app installata DA Google Play e aggiungi il valore alle origini consentite del tuo server:

if (window.AppMint && window.AppMint.passkey) {
  console.log(window.AppMint.passkey.origin());   // android:apk-key-hash:Lir5oIjf552K...
}

La chiamata diretta restituisce la stessa stringa:

if (window.WebToApk && typeof window.WebToApk.passkeyOrigin === 'function') {
  console.log(window.WebToApk.passkeyOrigin());
}

Mostra l'origine in una schermata delle impostazioni nascosta, così puoi leggerla dall'installazione da Play senza un debugger:

var out = document.getElementById('passkey-origin');
var origin = (window.AppMint && window.AppMint.passkey) ? window.AppMint.passkey.origin() : '';

if (!origin) {
  out.textContent = 'Open this screen in the installed app.';
} else {
  out.textContent = origin;               // android:apk-key-hash:...
  out.style.userSelect = 'all';           // one long-press selects the whole value
}

Note: Google Play ri-firma la tua app, quindi un APK di prova dà un valore diverso dall'installazione da Play. Esempio con SimpleWebAuthn: expectedOrigin: ['https://your-domain.com', 'android:apk-key-hash:...'].

signInWithGoogle bridge#

window.WebToApk.signInWithGoogle(serverClientId: String, callbackId: String)

===== Accesso Google nativo tramite Credential Manager ===== Aggira il blocco OAuth di Google nelle WebView Android (errore disallowed_useragent) eseguendo il flusso di accesso in Android nativo e riportando a JS solo il token ID Google risultante, dove l'SDK Web di Firebase lo usa tramite GoogleAuthProvider.credential(idToken) + signInWithCredential(...). Contratto JS: window.WebToApk.signInWithGoogle(serverClientId, callbackId) serverClientId "" = il client Web (type 3) nel google-services.json del creatore per questo pacchetto; se non ce n'è, errore "missing_web_client_id". Una pagina che non appartiene all'app riceve l'errore "foreign_origin". -> al termine, viene chiamato window.__webToApkAuth[callbackId](resultJsonString) resultJsonString = '{"ok":true,"idToken":"...","email":"...","displayName":"..."}' oppure '{"ok":false,"error":"<message>"}'

Esempio

Avvia l'accesso Google nativo (il selettore di account di Android) e risponde con un ID token Google. Le pagine di solito usano l'helper Promise WebToApkAuth.signInWithGoogle(webClientId), che incapsula questa chiamata.

Restituisce: niente. La risposta arriva dopo: la shell chiama window.__webToApkAuth[callbackId](json) con '{"ok":true,"idToken":"...","email":"...","displayName":"...","profilePictureUri":"..."}' oppure '{"ok":false,"error":"<code>","detail":"..."}', poi rimuove quella voce. Richiede: il tuo OAuth Web client ID e gli SHA-1 / SHA-256 dell'app registrati nel tuo progetto Google o Firebase. Nessun interruttore di build.

L'helper (consigliato):

async function googleSignIn() {
  if (!window.WebToApkAuth) return null;
  try {
    var r = await window.WebToApkAuth.signInWithGoogle('1234567890-abc.apps.googleusercontent.com');
    return r.idToken;
  } catch (err) {
    if (err.message !== 'cancelled') alert('Google sign-in failed: ' + err.message);
    return null;
  }
}

La chiamata grezza (ordine degli argomenti: web client ID, poi l'id della tua callback):

function googleSignInRaw(done) {
  if (!window.WebToApk || typeof window.WebToApk.signInWithGoogle !== 'function') return;
  var id = 'g_' + Date.now();
  window.__webToApkAuth = window.__webToApkAuth || {};
  window.__webToApkAuth[id] = function (json) {
    var r = JSON.parse(json);
    if (r.ok) done(r.idToken, r.email);
    else if (r.error !== 'cancelled') alert('Google sign-in failed: ' + r.error);
  };
  window.WebToApk.signInWithGoogle('1234567890-abc.apps.googleusercontent.com', id);
}

La chiamata grezza con un client ID vuoto, che usa il client Web del google-services.json che hai caricato:

function googleSignInFromUploadedFile() {
  return new Promise(function (resolve, reject) {
    if (!window.WebToApk || typeof window.WebToApk.signInWithGoogle !== 'function') {
      return reject(new Error('not_in_app'));
    }
    var id = 'g_' + Date.now();
    window.__webToApkAuth = window.__webToApkAuth || {};
    window.__webToApkAuth[id] = function (json) {
      var r = JSON.parse(json);
      if (r.ok) resolve(r); else reject(new Error(r.error));
    };
    window.WebToApk.signInWithGoogle('', id);
  });
}

googleSignInFromUploadedFile()
  .then(function (r) { showProfile(r.displayName, r.profilePictureUri); })
  .catch(function (e) {
    if (e.message === 'missing_web_client_id') console.log('Pass your Web client ID: the uploaded file has none.');
  });

Note: Codici di errore: cancelled (l'utente ha chiuso il selettore), google_play_services_unavailable (nessun account Google o provider - è anche l'aspetto di una registrazione SHA-1 mancante), missing_web_client_id (non ne è stato passato nessuno e non c'è un client Web nel google-services.json che hai caricato), foreign_origin (la pagina visualizzata non appartiene all'app), failed, unexpected_credential_type, parse_failed: .... Un primo argomento vuoto usa il client Web del tuo google-services.json caricato, se ne ha uno. Verifica l'ID token sul tuo server (oppure passalo a Firebase con GoogleAuthProvider.credential(idToken)).

signOutGoogle bridge#

window.WebToApk.signOutGoogle(callbackId: String)

Cancella lo stato salvato di Credential Manager, così la prossima chiamata a signInWithGoogle mostra di nuovo il selettore di account (invece di riselezionare in silenzio l'ultimo account). Le app generate dovrebbero chiamarlo alla disconnessione avviata dall'utente, oltre a firebase.auth().signOut().

Esempio

Cancella lo stato di accesso Google salvato da Android, così al prossimo accesso viene mostrato di nuovo il selettore di account. Le pagine di solito usano l'helper Promise WebToApkAuth.signOutGoogle(), che incapsula questa chiamata.

Restituisce: niente. La risposta arriva dopo: la shell chiama window.__webToApkAuth[callbackId](json) con '{"ok":true}' oppure '{"ok":false,"error":"..."}'. Richiede: nulla.

L'helper (consigliato):

async function signOut() {
  await myBackendSignOut();                          // end your own session first
  if (window.WebToApkAuth) {
    try { await window.WebToApkAuth.signOutGoogle(); } catch (err) { /* nothing to undo */ }
  }
}

La chiamata grezza:

if (window.WebToApk && typeof window.WebToApk.signOutGoogle === 'function') {
  var id = 'gout_' + Date.now();
  window.__webToApkAuth = window.__webToApkAuth || {};
  window.__webToApkAuth[id] = function (json) {
    var r = JSON.parse(json);
    if (!r.ok) console.log('Google sign-out failed: ' + r.error);
  };
  window.WebToApk.signOutGoogle(id);
}

La chiamata grezza incapsulata in una Promise, usata quando l'utente elimina il proprio account:

function clearGoogleChoice() {
  return new Promise(function (resolve) {
    if (!window.WebToApk || typeof window.WebToApk.signOutGoogle !== 'function') return resolve(false);
    var id = 'gout_' + Date.now();
    window.__webToApkAuth = window.__webToApkAuth || {};
    window.__webToApkAuth[id] = function (json) { resolve(JSON.parse(json).ok === true); };
    window.WebToApk.signOutGoogle(id);
  });
}

async function deleteAccount() {
  await fetch('https://your-domain.com/account', { method: 'DELETE', credentials: 'include' });
  await clearGoogleChoice();          // the next sign-in starts from the account picker
  showSignInScreen();
}

Note: Non disconnette l'utente da Firebase né dal tuo server: fallo tu.

Generato dal runtime dell'app e dai suoi file di esempio a ogni build della documentazione. Leggilo in Markdown · Tutte le famiglie.

Verificato rispetto al bridge distribuito il 22/10/2026.