Customer Auth + Firebase OAuth: A Practical Integration Guide
Customer Auth + Firebase OAuth: A Practical Integration Guide
💡 Audience: Developers building consumer apps (web or mobile) on top of HyperBabel — who need real per-user authentication, not a sandbox API key pasted into a login screen.
Embedding HyperBabel into a mobile app traditionally meant shipping your hb_live_… key inside the binary. Anyone who decompiled the APK / IPA had full access to your org. Web apps had a similar story — you either built your own backend proxy or accepted that your network tab leaked the key.
The Customer Auth feature solves this. HyperBabel's backend mints per-end-user JWTs (1h access, 30d refresh) directly from your client's Firebase ID token — no hop through your own backend required. Your client apps hold only those JWTs. The org hb_live_… key stays server-side, in Secret Manager / Vault / Workers Secrets — and never leaves it.
How the flow works
[Client App] ──Sign in with Firebase──→ Firebase ID token
│
│ POST /customer/auth/firebase-exchange
│ Authorization: Bearer <firebase-id-token>
▼
[HyperBabel API] ── Google JWKS verify → org lookup → customer JWT
│
│ { access_token, refresh_token, ... }
▼
[Client App] ──Bearer <customer access token>──→ /chat, /video, /translateThis is the same pattern SendBird and Stream call "session tokens", and how Twilio's "Access Tokens" work. We picked it because it leaves identity and GDPR responsibility with you, but takes the painful "where do I keep the API key" question off the table. Firebase Auth users get an extra shortcut: HyperBabel verifies the Firebase ID token directly against Google's JWKS, so you don't need to build or operate a backend exchange function for the Firebase path.
Step 1 — Set up Firebase Auth
In the Firebase Console, enable Auth → Google + Apple sign-in. Add iOS and Android apps (if you're shipping mobile) or set up the Web SDK. Download the platform config files. Firebase handles passwordless OAuth, Apple's email-relay quirks, and account recovery for you.
Already on Auth0, Cognito, Supabase Auth, or your own email/password? Skip to the "Not using Firebase?" section near the end — the same Customer Auth feature works with any auth provider, just through a slightly different endpoint.
Step 2 — Register your Firebase project in the HyperBabel Console (one-time)
Open Dashboard → Customer Auth → Firebase Projects → Add Firebase project. You'll be asked for two things:
- Your Firebase project ID — looks like
my-app-prod(Firebase Console → Project settings → General → "Project ID"). - A short-lived Firebase ID token that proves you own the project. The form offers two ways to get one (~2 minutes) — pick whichever fits:
- Method A (recommended, no app needed): enable Email/Password in Firebase Auth → add a temporary test user → run the provided curl (or PowerShell) snippet with that user's credentials → paste the printed
idToken. - Method B (already-running app): in any app that already uses Firebase Auth, sign in any user and log
await auth().currentUser.getIdToken().
The console verifies the token against Google's JWKS, checks its aud claim equals the project ID you entered, then maps the project to your HyperBabel org. The token itself is discarded immediately — never stored. From this point on, any user signed in to this Firebase project can be exchanged for a HyperBabel customer JWT.
Step 3 — Call /customer/auth/firebase-exchange directly from your client
No backend, no Cloud Function. The client sends its Firebase ID token straight to HyperBabel:
React Native
import auth from '@react-native-firebase/auth';
import { GoogleSignin } from '@react-native-google-signin/google-signin';
import * as SecureStore from 'expo-secure-store';
// 1. Sign in with Google (Apple uses auth.AppleAuthProvider; same shape)
await GoogleSignin.hasPlayServices();
const { idToken: googleIdToken } = await GoogleSignin.signIn();
const credential = auth.GoogleAuthProvider.credential(googleIdToken);
await auth().signInWithCredential(credential);
// 2. Get a fresh Firebase ID token (force-refresh avoids edge-cases near expiry)
const firebaseIdToken = await auth().currentUser!.getIdToken(true);
// 3. Exchange for a HyperBabel customer JWT — direct, no backend hop
const res = await fetch(
'https://api.hyperbabel.com/api/v1/customer/auth/firebase-exchange',
{
method: 'POST',
headers: {
Authorization: `Bearer ${firebaseIdToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ preferred_lang_cd: 'en' }),
},
);
if (!res.ok) throw new Error('Token exchange failed');
const session = await res.json();
// 4. Store and use
await SecureStore.setItemAsync('hb_access', session.access_token);
await SecureStore.setItemAsync('hb_refresh', session.refresh_token);Web (any framework)
import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const auth = getAuth();
await signInWithPopup(auth, new GoogleAuthProvider());
const firebaseIdToken = await auth.currentUser!.getIdToken(true);
const res = await fetch(
'https://api.hyperbabel.com/api/v1/customer/auth/firebase-exchange',
{
method: 'POST',
headers: {
Authorization: `Bearer ${firebaseIdToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ preferred_lang_cd: 'en' }),
},
);
if (!res.ok) throw new Error('Token exchange failed');
const session = await res.json();
sessionStorage.setItem('hb_access', session.access_token);
sessionStorage.setItem('hb_refresh', session.refresh_token);iOS (Swift)
The pattern matches: get the Firebase ID token via Auth.auth().currentUser?.getIDTokenForcingRefresh(true) (or the async/await variant) → URLSession POST to /customer/auth/firebase-exchange with Authorization: Bearer <token> → decode the response and store access_token in Keychain. No SDK dependency beyond FirebaseAuth and URLSession.
Step 4 — Send the customer JWT on every HyperBabel call
Every subsequent call to /chat, /video, /translate, /rtm, etc. carries:
Authorization: Bearer <customer_access_token>The org API key never lives on the device or in browser storage.
Step 5 — A defensive runtime guard
In your client-side HTTP client, add one line that throws if any header value ever starts with hb_live_ or hb_test_:
function assertNoOrgApiKey(headers: Record<string, string>) {
for (const v of Object.values(headers)) {
if (typeof v === 'string' && (v.startsWith('hb_live_') || v.startsWith('hb_test_'))) {
throw new Error('Refusing to send a HyperBabel org API key from a client app');
}
}
}Now even an accidental future commit can't leak the key.
What customer JWTs can — and cannot — do
A customer JWT can call every SDK-facing endpoint — /chat, /unitedchat, /video, /translate, /rtm, /presence, /push, /storage, /users — exactly as the org API key could. It cannot reach /admin, /billing, or /auth/api-keys. So even a leaked customer JWT can't create new keys, read invoices, or change org-level settings.
Refresh, revoke, observe
- Refresh is direct from the client —
POST /customer/refreshwhen the access token is within ~5 minutes of expiry. The refresh token never unlocks the org key, so this path doesn't need a backend hop. Each refresh rotates the pair atomically (the previous refresh token is revoked) and the new session row records the current device's user-agent and IP for audit. - Revoke lives on your backend —
POST /customer/revokewith the org API key wipes every active session for oneexternal_user_idin under 60 seconds globally. Use this on log-out-everywhere, password-changed, or abuse signals. - Observe in the Console — Dashboard → Customer Auth shows token settings, your registered Firebase projects, and a searchable end-user table with active session counts and a "Revoke all" button per user.
Not using Firebase? The backend-exchange pattern
If your users sign in via Auth0, Cognito, Supabase Auth, or your own email/password system, the Firebase shortcut doesn't apply — but the same Customer Auth feature still works through a small server-to-server exchange:
// Your backend (Worker / Lambda / Express) — org API key stays server-side
async function mintCustomerJwt(externalUserId: string, displayName?: string) {
const r = await fetch('https://api.hyperbabel.com/api/v1/customer/issue-token', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HYPERBABEL_API_KEY!}`, // hb_live_…
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_user_id: externalUserId,
display_name: displayName ?? null,
preferred_lang_cd: 'en',
}),
});
if (!r.ok) throw new Error('Token issue failed');
return r.json(); // { access_token, refresh_token, expires_at, ... }
}Authenticate the user with your auth provider on the server first, then call /customer/issue-token. Return the response straight to the client — refresh, revoke, and the runtime guard all work the same as in the Firebase flow.
Reference
- API reference (Swagger): hyperbabel.com/docs — full endpoint signatures for
/customer/auth/firebase-exchange,/customer/issue-token,/refresh,/revoke, and/me. - Postman: the
/customer/*requests are pre-configured in the latest collection. Import. - Console: Customer Auth settings — register Firebase projects, tune token lifetime, manage end-user sessions.
Frequently asked questions
Why not just ship the organisation API key in the mobile app?
Because a key inside a shipped bundle can be extracted, and an organisation key carries your whole account's authority. Customer Auth exchanges a signed user identity for a short-lived, per-end-user token (1h access, 30d refresh) so the client never holds anything broader than one user's own session.
Do our users have to sign in again?
No. The exchange happens behind your existing sign-in, so the user experience does not change. You keep the identity provider you already use — the token exchange is server-to-server.
What if we do not use Firebase?
The same feature works with any provider. Firebase gets a shortcut because an ID token can be exchanged directly; with Auth0, Cognito, another provider or your own email/password system, your backend performs a small server-to-server exchange instead.
What can an end-user token actually do?
Only what that one user should do — send and read their own messages, join permitted channels, start sessions. It cannot reach administrative or billing endpoints, and revoking one user does not require rotating anything else.
Related articles
Building a Real-time Translated Live Community in 10 Minutes with 1 API Key
See how easy it is to build a global chat with native AI translation and video capabilities using HyperBabel's single all-in-one SDK without complex backend setup.

HyperBabel vs Firebase: The Pricing Dilemma of NoSQL Chat
Firebase is a trap for chat apps. A single "hello" in a 1,000-user room costs 1,000 database read operations. See why HyperBabel's flat concurrency is the only way to scale safely.

HyperBabel vs SendBird (2026): Escaping the MAU Trap
SendBird's MAU-based pricing creates a financial trap as you grow. See how HyperBabel's revolutionary flat-rate pricing and $7.99/1M messages overage guarantees massive cost savings.
