मुफ़्त APK बनाएँ

JavaScript ब्रिज API

साइन-इन और passkeys

असली Google अकाउंट चुनने वाला, वेब रीडायरेक्ट की जगह नेटिव, और Android के Credential Manager से passkeys - navigator.credentials Chrome की तरह काम करता है, या अपने सर्वर के JSON विकल्पों के साथ AppMint.passkey इस्तेमाल करें।

AppMint.passkey हेल्पर#

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

उदाहरण

Android के passkey मैनेजर के ज़रिए Passkeys (फ़िंगरप्रिंट, चेहरे या स्क्रीन लॉक से साइन इन - बिना पासवर्ड), आपके passkey सर्वर के बनाए JSON विकल्पों के साथ।

लौटाता है: create(optionsJSON) → RegistrationResponseJSON का Promise; get(optionsJSON) → AuthenticationResponseJSON का Promise (जाँच के लिए कोई भी अपने सर्वर को भेजें)। दोनों DOMException के साथ reject होते हैं (err.name, और err.reason = एक छोटा कोड)। isAvailable() → boolean, origin() → string, assetLinks() → array - सब तुरंत। ज़रूरत: Google Play services वाला Android 9+, एक passkey सर्वर, और नीचे के दो सेट-अप चरण। कोई बिल्ड स्विच नहीं।

साइन इन:

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

एक बनाएँ (यूज़र के किसी और तरीके से साइन इन करने के बाद):

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

एक बार सेट अप करें - इसे Google Play से इंस्टॉल किए ऐप में चलाएँ (Play अपनी key से साइन करता है):

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

नोट्स: err.name / err.reason: NotAllowedError (cancelled, no_credential, interrupted), InvalidStateError (पहले से मौजूद), SecurityError के साथ not_linked (assetlinks.json में ऐप की सूची नहीं) या rp_mismatch (rpId इस पेज का डोमेन नहीं), NotSupportedError / unavailable (Android < 9 या Play services नहीं), TypeError / invalid_options (ऐप में बंडल पेज का कोई डोमेन नहीं होता, इसलिए rp.id / rpId को अपने डोमेन पर सेट करें)। window.AppMint.passkey सिर्फ़ ऐप में होता है; ब्राउज़र में navigator.credentials इस्तेमाल करें (वही कोड ऐप में भी काम करता है)।

WebToApkAuth.signInWithGoogle हेल्पर#

window.WebToApkAuth.signInWithGoogle(webClientId)

उदाहरण

यूज़र को Android के अपने अकाउंट पिकर से उसके Google अकाउंट में साइन इन कराता है, और आपको Google ID token देता है। Google अपना साइन-इन पेज ऐप WebViews के अंदर ब्लॉक करता है ("disallowed_useragent"); यह नेटिव रास्ता ब्लॉक नहीं होता।

लौटाता है: {ok:true, idToken, email, displayName, profilePictureUri} का Promise। यह एक Error के साथ REJECT होता है जिसका message कोड है (cancelled, google_play_services_unavailable, missing_web_client_id, failed, …)। ज़रूरत: आपका OAuth Web client ID (.apps.googleusercontent.com पर ख़त्म होता है), और ऐप के SHA-1 और SHA-256 फ़िंगरप्रिंट आपके Google / Firebase प्रोजेक्ट में रजिस्टर हों (आपके बिल्ड की _signkey_info.txt फ़ाइल से, और Play पर प्रकाशित करें तो Play की App signing key से)। कोई बिल्ड स्विच नहीं।

Firebase Authentication के साथ (compat SDK):

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

अपने सर्वर के साथ - ID token भेजें और वहीं उसकी जाँच करें (सिर्फ़ पेज के भरोसे कभी न रहें):

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

Supabase Auth के साथ (ID token को इसे दें 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);
  }
}

नोट्स: Web क्लाइंट ID दें। कोई आर्गुमेंट न होने पर हेल्पर पहले window.firebaseConfig._googleWebClientId इस्तेमाल करता है अगर आपका पेज उसे सेट करता है, फिर इस ऐप के लिए अपलोड की गई google-services.json का Web क्लाइंट (सिर्फ़ तब जब वह फ़ाइल आपके अपने Firebase प्रोजेक्ट की हो जिसमें Google साइन-इन चालू है - AppMint Push बिल्ड की कभी नहीं); इनमें से कुछ न हो तो यह missing_web_client_id के साथ reject होता है। जो पेज ऐप का अपना नहीं है (ऐप के अंदर खुली बाहरी साइट) उसे foreign_origin से मना कर दिया जाता है। WEB क्लाइंट ID इस्तेमाल करें, Android क्लाइंट ID नहीं। "No credentials available" का मतलब आम तौर पर यह है कि SHA-1 रजिस्टर नहीं है। यह हेल्पर सिर्फ़ ऐप में है - ब्राउज़र में अपना सामान्य वेब साइन-इन इस्तेमाल करें। इसे बटन टैप से कॉल करें।

WebToApkAuth.signOutGoogle हेल्पर#

window.WebToApkAuth.signOutGoogle()

उदाहरण

Android की सेव की हुई Google साइन-इन पसंद साफ़ करता है, ताकि अगला signInWithGoogle पिछले अकाउंट को चुपचाप दोहराने के बजाय अकाउंट पिकर फिर दिखाए।

लौटाता है: {ok:true} का Promise; एक Error के साथ reject होता है जिसका message बताता है कि क्या गड़बड़ हुई। ज़रूरत: कुछ नहीं।

इसे अपने साइन-आउट के साथ कॉल करें:

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

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

पहले अपने सर्वर से साइन आउट करें, फिर Google की पसंद साफ़ करें, ताकि कोई विफलता लॉगआउट को न रोके:

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

नोट्स: इससे आपके ऐप का अपना सेशन खत्म नहीं होता - यूज़र को अपने बैकएंड से खुद साइन आउट करें। यह सिर्फ़ यह रीसेट करता है कि अगली बार फ़ोन कौन सा Google अकाउंट सुझाए।

navigator.credentials वेब स्टैंडर्ड#

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

उदाहरण

स्टैंडर्ड WebAuthn passkey कॉल। सामान्य Android WebView इन्हें नहीं कर सकता; ऐप में ये Android के passkey मैनेजर तक जाते हैं, इसलिए Chrome के लिए लिखे कोड और लाइब्रेरी (SimpleWebAuthn, Hanko, Corbado, Auth0) बिना बदलाव के काम करते हैं।

लौटाता है: बिल्कुल Chrome की तरह PublicKeyCredential (.toJSON() के साथ) का Promise; DOMException के साथ reject होता है। ज़रूरत: Google Play services वाला Android 9+, एक passkey सर्वर, आपके डोमेन की assetlinks.json जिसमें ऐप सूचीबद्ध हो, और आपका सर्वर ऐप का origin स्वीकार करे (AppMint.passkey देखें)। कोई बिल्ड स्विच नहीं।

स्टैंडर्ड API से साइन इन करें (विकल्प आपके सर्वर से, बाइनरी फ़ील्ड ArrayBuffers के रूप में):

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

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

फ़ोन जो समर्थन करता है उसके हिसाब से तय करें कि कौन से साइन-इन बटन दिखाने हैं (ऐप में passkey ऑटोफ़िल बंद है, इसलिए एक बटन रखें):

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
}

नोट्स: टेक्स्ट फ़ील्ड में passkey ऑटोफ़िल (mediation: 'conditional') ऐप में उपलब्ध नहीं है - isConditionalMediationAvailable() जवाब में false देता है और कॉल NotSupportedError के साथ reject होता है; साइन-इन किसी बटन से शुरू करें। signal (AbortController) काम करता है। ऐप में बंडल पेज का अपना कोई डोमेन नहीं होता: rp.id / rpId को अपने डोमेन पर सेट करें। publicKey के बिना कॉल (पासवर्ड, OTP) पहले की तरह WebView को जाते हैं।

OTPCredential वेब स्टैंडर्ड#

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

उदाहरण

स्टैंडर्ड WebOTP API: SMS से लॉगिन कोड अपने-आप भर जाता है। Android Google की "Allow this app to read this message?" शीट दिखाता है - SMS अनुमति की ज़रूरत नहीं।

लौटाता है: एक OTPCredential {type:'otp', code} का Promise। यूज़र Deny टैप करे या signal abort हो तो AbortError के साथ reject होता है, पाँच मिनट तक SMS न आए तो InvalidStateError, Google Play services न हों तो NotSupportedError। ज़रूरत: कुछ चालू करने की ज़रूरत नहीं। SMS का अंत इस पेज के होस्ट वाली एक लाइन से होना चाहिए: @<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 भेजता है (आख़िरी लाइन बाइंडिंग है):

Your Acme code is 482913.

@acme.example #482913

"Resend code" के बाद नया अनुरोध शुरू करें (यह अभी इंतज़ार कर रहे अनुरोध की जगह ले लेता है, जो AbortError के साथ reject होता है):

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

नोट्स: होस्ट पेज का अपना होस्ट होना चाहिए - वेबसाइट वाले ऐप के लिए यह आपका डोमेन है (वही SMS जो Chrome पहले से पढ़ता है); ऑफ़लाइन/AI से बने ऐप के लिए यह appassets.androidplatform.net है। किसी दूसरे होस्ट का मैसेज अनदेखा कर दिया जाता है और ऐप इंतज़ार करता रहता है। सिर्फ़ टॉप पेज (एम्बेडेड origin से बंधे कोड, @top #code @iframe, समर्थित नहीं हैं)। एक समय में सिर्फ़ एक अनुरोध इंतज़ार करता है; नया अनुरोध पुराने को रद्द कर देता है। इसके पीछे का इवेंट: appmint:otp.

__otpCancel ब्रिज#

window.WebToApk.__otpCancel(requestId: String)

लंबित WebOTP अनुरोध छोड़ देता है (AbortSignal, या नए अनुरोध ने उसकी जगह ले ली)।

उदाहरण

WebOTP अनुरोध रद्द करने (AbortSignal) के पीछे का आंतरिक ट्रांसपोर्ट। पेज स्टैंडर्ड API इस्तेमाल करते हैं; इसे कभी सीधे कॉल न करें - __ मेथड बिना सूचना के बदल सकते हैं।

लौटाता है: कुछ नहीं। ज़रूरत: कुछ चालू करने की ज़रूरत नहीं।

पब्लिक API इस्तेमाल करें:

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

जैसे ही यूज़र खुद कोड टाइप करना शुरू करे, इंतज़ार बंद कर दें:

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

जब यूज़र फ़ोन नंबर बदलने के लिए पीछे जाए, तब इंतज़ार रद्द करें:

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

नोट्स: ऐप शेल (WebApiPolyfills.kt) में लागू। वही पेज कोड Chrome में बिना बदलाव के काम करता है।

__otpStart ब्रिज#

window.WebToApk.__otpStart(requestId: String)

WebOTP: Google की SMS User Consent शीट के ज़रिए एक SMS का (पाँच मिनट तक) इंतज़ार करता है - SMS अनुमति नहीं। जवाब `appmint:otp {requestId, message|error}` है।

उदाहरण

navigator.credentials.get({ otp: { transport: ['sms'] } }) के पीछे का आंतरिक ट्रांसपोर्ट। पेज स्टैंडर्ड API इस्तेमाल करते हैं; इसे कभी सीधे कॉल न करें - __ मेथड बिना सूचना के बदल सकते हैं।

लौटाता है: कुछ नहीं; जवाब appmint:otp इवेंट {requestId, message} या {requestId, error} (cancelled, timeout, unavailable) के रूप में आता है। ज़रूरत: कुछ चालू करने की ज़रूरत नहीं।

पब्लिक API इस्तेमाल करें:

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

एक मिनट बाद timeout signal से इंतज़ार बंद करें:

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

त्रुटि के नाम से यूज़र को बताएँ कि आगे क्या करना है:

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

नोट्स: ऐप शेल (WebApiPolyfills.kt) में लागू। वही पेज कोड Chrome में बिना बदलाव के काम करता है।

passkeyIsAvailable ब्रिज#

window.WebToApk.passkeyIsAvailable(): Boolean

Google Play services वाले Android 9+ पर True - वे फ़ोन जो passkey रख सकते हैं।

उदाहरण

बताता है कि यह फ़ोन passkeys इस्तेमाल कर सकता है या नहीं। पेज आम तौर पर पब्लिक API AppMint.passkey.isAvailable() (या PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()) इस्तेमाल करते हैं, जो इस कॉल को लपेटता है।

लौटाता है: एक boolean, तुरंत - Google Play services वाले Android 9+ पर और passkey चैनल के लिए काफ़ी नए WebView पर true। ज़रूरत: कुछ नहीं।

पब्लिक API:

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

सीधा कॉल, अगर आपको चाहिए:

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

पासवर्ड से साइन-इन के बाद एक बार "Add a passkey" पेश करें, सिर्फ़ उन फ़ोन पर जो उसे रख सकते हैं (स्टैंडर्ड जाँच, 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;
}

नोट्स: जब यह false हो, तो AppMint.passkey.create/get NotSupportedError के साथ reject होते हैं। passkey बटन छुपा दें और अपना दूसरा साइन-इन दें।

passkeyOrigin ब्रिज#

window.WebToApk.passkeyOrigin(): String

वह origin जो passkey सर्वर इस ऐप से देखता है: `android:apk-key-hash:<base64url SHA-256>`।

उदाहरण

वह origin बताता है जो आपका passkey सर्वर इस ऐप से देखता है: android:apk-key-hash:<base64url SHA-256 of the signing certificate>। पेज आम तौर पर पब्लिक API AppMint.passkey.origin() इस्तेमाल करते हैं, जो इस कॉल को लपेटता है।

लौटाता है: एक string, तुरंत ("" अगर सर्टिफ़िकेट पढ़ा न जा सके)। ज़रूरत: कुछ नहीं।

Google Play से इंस्टॉल किए ऐप में इसे एक बार चलाएँ, और मान को अपने सर्वर के allowed origins में जोड़ें:

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

सीधा कॉल वही string देता है:

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

origin को किसी छुपी सेटिंग्स स्क्रीन पर दिखाएँ, ताकि आप डीबगर के बिना Play इंस्टॉल से उसे पढ़ सकें:

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
}

नोट्स: Google Play आपके ऐप को दोबारा साइन करता है, इसलिए टेस्ट APK का मान Play इंस्टॉल से अलग होगा। SimpleWebAuthn का उदाहरण: expectedOrigin: ['https://your-domain.com', 'android:apk-key-hash:...'].

signInWithGoogle ब्रिज#

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

===== Credential Manager से नेटिव Google Sign-In ===== यह नेटिव Android में साइन-इन चलाकर Google के Android WebView OAuth ब्लॉक (disallowed_useragent त्रुटि) को पार करता है, और सिर्फ़ नतीजे में मिला Google ID token वापस JS तक पहुँचाता है, जहाँ Firebase Web SDK उसे GoogleAuthProvider.credential(idToken) + signInWithCredential(...) से इस्तेमाल करता है। JS कॉन्ट्रैक्ट: window.WebToApk.signInWithGoogle(serverClientId, callbackId) serverClientId "" = इस पैकेज के लिए क्रिएटर की अपनी google-services.json का Web क्लाइंट (type 3); कुछ न हो तो त्रुटि "missing_web_client_id"। जो पेज ऐप का अपना नहीं है उसे त्रुटि "foreign_origin" मिलती है। -> पूरा होने पर window.__webToApkAuth[callbackId](resultJsonString) कॉल होता है, जहाँ resultJsonString = '{"ok":true,"idToken":"...","email":"...","displayName":"..."}' या '{"ok":false,"error":"<message>"}'

उदाहरण

नेटिव Google साइन-इन शुरू करता है (Android का अकाउंट पिकर) और Google ID token के साथ जवाब देता है। पेज आम तौर पर Promise हेल्पर WebToApkAuth.signInWithGoogle(webClientId) इस्तेमाल करते हैं, जो इस कॉल को लपेटता है।

लौटाता है: कुछ नहीं। जवाब बाद में आता है: शेल window.__webToApkAuth[callbackId](json) को '{"ok":true,"idToken":"...","email":"...","displayName":"...","profilePictureUri":"..."}' या '{"ok":false,"error":"<code>","detail":"..."}' के साथ कॉल करता है, फिर वह एंट्री हटा देता है। ज़रूरत: आपका OAuth Web client ID, और ऐप का SHA-1 / SHA-256 आपके Google या Firebase प्रोजेक्ट में रजिस्टर हो। कोई बिल्ड स्विच नहीं।

हेल्पर (सुझाया गया):

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

सीधा कॉल (आर्गुमेंट का क्रम: वेब क्लाइंट ID, फिर आपकी callback id):

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

खाली client ID के साथ सीधा कॉल, जो आपकी अपलोड की google-services.json का Web क्लाइंट इस्तेमाल करता है:

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

नोट्स: त्रुटि कोड: cancelled (यूज़र ने पिकर बंद कर दिया), google_play_services_unavailable (कोई Google अकाउंट या provider नहीं - SHA-1 रजिस्टर न होना भी ऐसा ही दिखता है), missing_web_client_id (कुछ दिया नहीं गया, और आपके अपलोड किए google-services.json में कोई Web क्लाइंट नहीं), foreign_origin (स्क्रीन पर मौजूद पेज ऐप का अपना नहीं है), failed, unexpected_credential_type, parse_failed: ...। खाली पहला आर्गुमेंट होने पर, आपके अपलोड किए google-services.json में Web क्लाइंट हो तो वही इस्तेमाल होता है। ID token की जाँच अपने सर्वर पर करें (या उसे Firebase को दें, इसके साथ GoogleAuthProvider.credential(idToken)).

signOutGoogle ब्रिज#

window.WebToApk.signOutGoogle(callbackId: String)

सेव की हुई Credential Manager स्थिति साफ़ करता है ताकि अगला signInWithGoogle कॉल अकाउंट पिकर फिर दिखाए (पिछले अकाउंट को चुपचाप दोबारा चुनने के बजाय)। जनरेट किए गए ऐप को यूज़र के साइन-आउट करने पर, firebase.auth().signOut() के अलावा इसे भी कॉल करना चाहिए।

उदाहरण

Android की सेव की हुई Google साइन-इन स्थिति साफ़ करता है, ताकि अगली बार साइन-इन पर अकाउंट पिकर फिर दिखे। पेज आम तौर पर Promise हेल्पर WebToApkAuth.signOutGoogle() इस्तेमाल करते हैं, जो इस कॉल को लपेटता है।

लौटाता है: कुछ नहीं। जवाब बाद में आता है: शेल window.__webToApkAuth[callbackId](json) को '{"ok":true}' या '{"ok":false,"error":"..."}' के साथ कॉल करता है। ज़रूरत: कुछ नहीं।

हेल्पर (सुझाया गया):

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

सीधा कॉल:

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

सीधा कॉल एक Promise में लपेटा हुआ, जब यूज़र अपना अकाउंट डिलीट करे तब इस्तेमाल होता है:

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

नोट्स: यह यूज़र को Firebase या आपके सर्वर से साइन आउट नहीं करता - वह आप खुद करें।

हर डॉक्स बिल्ड पर ऐप रनटाइम और उसकी उदाहरण फ़ाइलों से बनाया जाता है। Markdown में पढ़ें · सभी फ़ैमिली.

2026-10-03 को शिप किए गए ब्रिज से मिलाकर जाँचा गया।