Buat APK Gratis

API jembatan JavaScript

Masuk dan passkey

Pemilih akun Google yang sungguhan, native dan bukan pengalihan web, dan passkey melalui Credential Manager Android - navigator.credentials berfungsi seperti di Chrome, atau gunakan AppMint.passkey dengan opsi JSON dari server Anda.

AppMint.passkey helper#

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

Contoh

Passkey (masuk dengan sidik jari, wajah, atau kunci layar - tanpa kata sandi) melalui pengelola passkey Android, memakai opsi JSON yang dibuat server passkey Anda.

Mengembalikan: create(optionsJSON) → Promise dari RegistrationResponseJSON; get(optionsJSON) → Promise dari AuthenticationResponseJSON (kirim salah satunya ke server Anda untuk diverifikasi). Keduanya ditolak dengan DOMException (err.name, dan err.reason = kode singkat). isAvailable() → boolean, origin() → string, assetLinks() → array - semuanya seketika. Membutuhkan: Android 9+ dengan Google Play services, server passkey, dan dua langkah penyiapan di bawah. Tidak ada sakelar build.

Masuk:

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();
}

Buat satu (setelah pengguna masuk dengan cara lain):

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);
  }
}

Atur sekali saja - jalankan ini di aplikasi yang diinstal DARI Google Play (Play menandatangani dengan kuncinya sendiri):

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

Catatan: err.name / err.reason: NotAllowedError (cancelled, no_credential, interrupted), InvalidStateError (sudah ada), SecurityError dengan not_linked (assetlinks.json tidak mencantumkan aplikasi) atau rp_mismatch (rpId bukan domain halaman ini), NotSupportedError / unavailable (Android < 9 atau tanpa Play services), TypeError / invalid_options (halaman yang dibundel di aplikasi tidak memiliki domain, jadi atur rp.id / rpId ke domain Anda). window.AppMint.passkey hanya ada di aplikasi; di browser gunakan navigator.credentials (kode yang sama berfungsi di aplikasi juga).

WebToApkAuth.signInWithGoogle helper#

window.WebToApkAuth.signInWithGoogle(webClientId)

Contoh

Memasukkan pengguna dengan akun Google mereka melalui pemilih akun bawaan Android, dan memberi Anda Google ID token. Google memblokir halaman masuknya di dalam WebView aplikasi ("disallowed_useragent"); jalur native ini tidak diblokir.

Mengembalikan: Promise dari {ok:true, idToken, email, displayName, profilePictureUri}. DITOLAK dengan Error yang message-nya adalah kodenya (cancelled, google_play_services_unavailable, missing_web_client_id, failed, …). Membutuhkan: Web client ID OAuth Anda (berakhir dengan .apps.googleusercontent.com), dan sidik jari SHA-1 dan SHA-256 aplikasi terdaftar di proyek Google / Firebase Anda (dari file _signkey_info.txt build Anda, dan kunci App signing Play jika Anda menerbitkan di Play). Tidak ada sakelar build.

Dengan 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);
  }
});

Dengan server Anda sendiri - kirim ID token dan verifikasi di sana (jangan pernah mempercayainya hanya di halaman):

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 })
  });
}

Dengan Supabase Auth (serahkan ID token ke 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);
  }
}

Catatan: Berikan Web client ID. Tanpa argumen helper memakai window.firebaseConfig._googleWebClientId jika halaman Anda menetapkannya, lalu klien Web di google-services.json yang Anda unggah untuk aplikasi ini (hanya jika file itu proyek Firebase Anda sendiri dengan masuk Google diaktifkan - jangan pernah build AppMint Push); jika semuanya tidak ada, ditolak dengan missing_web_client_id. Halaman yang bukan milik aplikasi (situs eksternal yang dibuka di dalam aplikasi) ditolak dengan foreign_origin. Gunakan WEB client ID, bukan Android client ID. "No credentials available" biasanya berarti SHA-1 belum terdaftar. Helper ini hanya ada di aplikasi - di browser gunakan cara masuk web biasa. Panggil dari ketukan tombol.

WebToApkAuth.signOutGoogle helper#

window.WebToApkAuth.signOutGoogle()

Contoh

Menghapus pilihan masuk Google yang tersimpan di Android, sehingga signInWithGoogle berikutnya menampilkan pemilih akun lagi alih-alih diam-diam memakai akun terakhir.

Mengembalikan: Promise dari {ok:true}; ditolak dengan Error yang message-nya menyatakan apa yang salah. Membutuhkan: tidak ada.

Panggil bersama proses keluar Anda sendiri:

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();
});

Tombol "Switch account": hapus pilihan tersimpan, lalu masuk lagi agar pengguna memilih akun lain:

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();
  }
}

Keluar dari server Anda sendiri dulu, lalu hapus pilihan Google tanpa membiarkan kegagalan menghalangi proses keluar:

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');
}

Catatan: Ini tidak mengakhiri sesi aplikasi Anda sendiri - keluarkan pengguna dari backend Anda sendiri. Ini hanya mengatur ulang akun Google mana yang ditawarkan ponsel berikutnya.

navigator.credentials standar web#

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

Contoh

Panggilan passkey WebAuthn standar. WebView Android biasa tidak dapat melakukannya; di aplikasi, panggilan diteruskan ke pengelola passkey Android, sehingga kode dan pustaka yang ditulis untuk Chrome (SimpleWebAuthn, Hanko, Corbado, Auth0) berfungsi tanpa perubahan.

Mengembalikan: Promise dari PublicKeyCredential (dengan .toJSON()), persis seperti Chrome; ditolak dengan DOMException. Membutuhkan: Android 9+ dengan Google Play services, server passkey, assetlinks.json domain Anda yang mencantumkan aplikasi, dan server Anda menerima origin aplikasi (lihat AppMint.passkey). Tidak ada sakelar build.

Masuk dengan API standar (opsi dari server Anda, field biner sebagai 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);
  }
}

Daftarkan 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);
  }
}

Tentukan tombol masuk mana yang ditampilkan dari apa yang didukung ponsel (autofill passkey nonaktif di aplikasi, jadi sediakan tombol):

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
}

Catatan: Autofill passkey di kolom teks (mediation: 'conditional') tidak tersedia di aplikasi - isConditionalMediationAvailable() menjawab false dan panggilan ditolak dengan NotSupportedError; mulai proses masuk dari tombol. signal (AbortController) berfungsi. Halaman yang dibundel di aplikasi tidak memiliki domain sendiri: atur rp.id / rpId ke domain Anda. Panggilan tanpa publicKey (kata sandi, OTP) diteruskan ke WebView seperti sebelumnya.

OTPCredential standar web#

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

Contoh

API WebOTP standar: kode masuk dari SMS terisi sendiri. Android menampilkan lembar "Allow this app to read this message?" dari Google - tanpa izin SMS.

Mengembalikan: Promise dari OTPCredential {type:'otp', code}. Ditolak AbortError saat pengguna mengetuk Deny atau sinyal dibatalkan, InvalidStateError setelah lima menit tanpa SMS, NotSupportedError tanpa Google Play services. Membutuhkan: tidak ada yang perlu diaktifkan. SMS harus DIAKHIRI dengan baris yang menyebut host halaman ini: @<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));
}

SMS yang dikirim server Anda (baris terakhir adalah pengikatnya):

Your Acme code is 482913.

@acme.example #482913

Setelah "Resend code", mulai permintaan baru (menggantikan yang masih menunggu, yang ditolak dengan 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.');
  }
}

Catatan: host harus host halaman itu sendiri - untuk aplikasi berbasis situs web itu adalah domain Anda (SMS yang sudah dibaca Chrome); untuk aplikasi offline/buatan AI itu adalah appassets.androidplatform.net. Pesan untuk host lain diabaikan dan aplikasi terus menunggu. Hanya halaman teratas (kode yang terikat ke origin tertanam, @top #code @iframe, tidak didukung). Hanya satu permintaan yang menunggu pada satu waktu; yang lebih baru membatalkan yang lama. Event di baliknya: appmint:otp.

__otpCancel jembatan#

window.WebToApk.__otpCancel(requestId: String)

Membatalkan permintaan WebOTP yang tertunda (AbortSignal, atau permintaan baru menggantikannya).

Contoh

Transport internal di balik pembatalan permintaan WebOTP (AbortSignal). Halaman memakai API standar; jangan pernah memanggil ini langsung - metode __ dapat berubah tanpa pemberitahuan.

Mengembalikan: tidak ada. Membutuhkan: tidak ada yang perlu diaktifkan.

Gunakan API publik:

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

Berhenti menunggu begitu pengguna mulai mengetik kodenya sendiri:

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); });

Batalkan penantian saat pengguna kembali untuk mengubah nomor ponselnya:

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();
});

Catatan: Diimplementasikan di shell aplikasi (WebApiPolyfills.kt). Kode halaman yang sama berfungsi tanpa perubahan di Chrome.

__otpStart jembatan#

window.WebToApk.__otpStart(requestId: String)

WebOTP: menunggu (hingga lima menit) satu SMS melalui lembar SMS User Consent Google - tanpa izin SMS. Jawabannya adalah `appmint:otp {requestId, message|error}`.

Contoh

Transport internal di balik navigator.credentials.get({ otp: { transport: ['sms'] } }). Halaman memakai API standar; jangan pernah memanggil ini langsung - metode __ dapat berubah tanpa pemberitahuan.

Mengembalikan: tidak ada; jawaban datang sebagai event appmint:otp {requestId, message} atau {requestId, error} (cancelled, timeout, unavailable). Membutuhkan: tidak ada yang perlu diaktifkan.

Gunakan API publik:

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

Berhenti menunggu setelah satu menit dengan sinyal batas waktu:

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.');
  }
}

Beri tahu pengguna apa yang harus dilakukan selanjutnya berdasarkan nama kesalahan:

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
  }
}

Catatan: Diimplementasikan di shell aplikasi (WebApiPolyfills.kt). Kode halaman yang sama berfungsi tanpa perubahan di Chrome.

passkeyIsAvailable jembatan#

window.WebToApk.passkeyIsAvailable(): Boolean

True di Android 9+ dengan Google Play services - ponsel yang dapat menyimpan passkey.

Contoh

Menyatakan apakah ponsel ini dapat memakai passkey. Halaman biasanya memakai API publik AppMint.passkey.isAvailable() (atau PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()), yang membungkus panggilan ini.

Mengembalikan: boolean, seketika - true di Android 9+ dengan Google Play services dan WebView yang cukup baru untuk saluran passkey. Membutuhkan: tidak ada.

API publik:

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

Panggilan mentahnya, jika Anda membutuhkannya:

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

Tawarkan "Add a passkey" sekali setelah masuk dengan kata sandi, hanya di ponsel yang dapat menyimpannya (pemeriksaan standar, kode yang sama di 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;
}

Catatan: Jika ini false, AppMint.passkey.create/get ditolak dengan NotSupportedError. Sembunyikan tombol passkey dan tawarkan cara masuk lainnya.

passkeyOrigin jembatan#

window.WebToApk.passkeyOrigin(): String

Origin yang dilihat server passkey dari aplikasi ini: `android:apk-key-hash:<base64url SHA-256>`.

Contoh

Memberi origin yang dilihat server passkey Anda dari aplikasi ini: android:apk-key-hash:<base64url SHA-256 of the signing certificate>. Halaman biasanya memakai API publik AppMint.passkey.origin(), yang membungkus panggilan ini.

Mengembalikan: string, seketika ("" jika sertifikat tidak dapat dibaca). Membutuhkan: tidak ada.

Jalankan ini sekali di aplikasi yang terpasang DARI Google Play, dan tambahkan nilainya ke origin yang diizinkan server Anda:

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

Panggilan mentahnya memberikan string yang sama:

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

Tampilkan origin di layar pengaturan tersembunyi, agar Anda bisa membacanya dari instalasi Play tanpa 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
}

Catatan: Google Play menandatangani ulang aplikasi Anda, jadi APK uji memberi nilai yang berbeda dari instalasi Play. Contoh SimpleWebAuthn: expectedOrigin: ['https://your-domain.com', 'android:apk-key-hash:...'].

signInWithGoogle jembatan#

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

===== Native Google Sign-In via Credential Manager ===== Melewati pemblokiran OAuth WebView Android oleh Google (kesalahan disallowed_useragent) dengan menjalankan alur masuk di Android native dan menjembatani hanya Google ID token yang dihasilkan kembali ke JS, tempat Firebase Web SDK memakainya lewat GoogleAuthProvider.credential(idToken) + signInWithCredential(...). Kontrak JS: window.WebToApk.signInWithGoogle(serverClientId, callbackId) serverClientId "" = klien Web (tipe 3) di google-services.json milik pembuat untuk paket ini; jika tidak ada, kesalahan "missing_web_client_id". Halaman yang bukan milik aplikasi mendapat kesalahan "foreign_origin". -> saat selesai, window.__webToApkAuth[callbackId](resultJsonString) dipanggil resultJsonString = '{"ok":true,"idToken":"...","email":"...","displayName":"..."}' atau '{"ok":false,"error":"<message>"}'

Contoh

Memulai masuk Google native (pemilih akun Android) dan menjawab dengan Google ID token. Halaman biasanya memakai helper Promise WebToApkAuth.signInWithGoogle(webClientId), yang membungkus panggilan ini.

Mengembalikan: tidak ada. Jawaban datang kemudian: shell memanggil window.__webToApkAuth[callbackId](json) dengan '{"ok":true,"idToken":"...","email":"...","displayName":"...","profilePictureUri":"..."}' atau '{"ok":false,"error":"<code>","detail":"..."}', lalu menghapus entri itu. Membutuhkan: OAuth Web client ID Anda, dan SHA-1 / SHA-256 aplikasi terdaftar di proyek Google atau Firebase Anda. Tidak ada sakelar build.

Helper (disarankan):

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;
  }
}

Panggilan mentah (urutan argumen: web client ID, lalu id callback Anda):

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);
}

Panggilan mentah dengan client ID kosong, yang memakai klien Web dari google-services.json yang Anda unggah:

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.');
  });

Catatan: Kode kesalahan: cancelled (pengguna menutup pemilih), google_play_services_unavailable (tidak ada akun atau penyedia Google - juga tampilan jika pendaftaran SHA-1 hilang), missing_web_client_id (tidak ada yang diberikan, dan tidak ada klien Web di google-services.json yang Anda unggah), foreign_origin (halaman yang tampil bukan milik aplikasi), failed, unexpected_credential_type, parse_failed: .... Argumen pertama yang kosong memakai klien Web dari google-services.json unggahan Anda sendiri jika ada. Verifikasi ID token di server Anda (atau serahkan ke Firebase dengan GoogleAuthProvider.credential(idToken)).

signOutGoogle jembatan#

window.WebToApk.signOutGoogle(callbackId: String)

Menghapus status Credential Manager yang tersimpan sehingga panggilan signInWithGoogle berikutnya menampilkan pemilih akun lagi (bukan diam-diam memilih ulang akun terakhir). Aplikasi yang dibuat sebaiknya memanggil ini saat pengguna keluar, selain firebase.auth().signOut().

Contoh

Menghapus status masuk Google yang tersimpan di Android, sehingga proses masuk berikutnya menampilkan pemilih akun lagi. Halaman biasanya memakai helper Promise WebToApkAuth.signOutGoogle(), yang membungkus panggilan ini.

Mengembalikan: tidak ada. Jawaban datang kemudian: shell memanggil window.__webToApkAuth[callbackId](json) dengan '{"ok":true}' atau '{"ok":false,"error":"..."}'. Membutuhkan: tidak ada.

Helper (disarankan):

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

Panggilan mentah:

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);
}

Panggilan mentah yang dibungkus dalam Promise, dipakai saat pengguna menghapus akunnya:

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();
}

Catatan: Ini tidak mengeluarkan pengguna dari Firebase atau server Anda - lakukan sendiri.

Dibuat dari runtime aplikasi dan file contohnya pada setiap build dokumentasi. Baca sebagai Markdown · Semua keluarga.

Diperiksa terhadap jembatan yang dirilis pada 2026-10-22.