QR Default Domain — Integration Guide
The QR default domain (bridge domain) lets you ship cross-device authentication today without deploying your own /mobile-auth page. TryMellon hosts the mobile approval screen; your app only needs the SDK on desktop.
When ready, switch to your own domain by changing one config value — no user migration required.
How It Works
┌──────────────────────────────────────────────────────────────────┐
│ QR Default Domain Flow │
│ │
│ Desktop (your app) Bridge domain (TryMellon) │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ 1. SDK.init() │──────▶ │ Creates QR session │ │
│ │ shows QR │◀────── │ Returns qr_url + id │ │
│ │ │ └─────────────────────────┘ │
│ │ 4. Polling gets │ │
│ │ session_token│ Mobile (user's phone) │
│ │ │ ┌─────────────────────────┐ │
│ │ 5. Set cookie, │ │ 2. User scans QR │ │
│ │ redirect │ │ 3. Opens bridge URL │ │
│ └─────────────────┘ │ → Passkey approval │ │
│ └─────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
- Desktop calls
sdk.crossDevice.start()→ getsqr_urlpointing to the bridge domain. - User scans the QR with their phone.
- Mobile opens the bridge URL → TryMellon’s hosted page triggers WebAuthn (
FaceID/TouchID). - Desktop polling detects
completed→ receivessession_token. - Your app sets a session cookie and redirects to the dashboard.
Prerequisites
| Requirement | Details |
|---|---|
| SDK | @trymellon/js ≥ 1.7 |
| Application | Created in TryMellon dashboard with your desktop origin in Allowed origins |
| Plan | Growth, Scale, or Enterprise (QR default included) |
Desktop: Initialize and Show QR
import { TryMellon } from '@trymellon/js'
// 1. Create client
const clientResult = TryMellon.create({
appId: 'YOUR_APP_ID',
publishableKey: 'cli_xxxx',
});
if (!clientResult.ok) throw clientResult.error;
const client = clientResult.value;
// 2. Init cross-device session (uses bridge domain by default)
const initResult = await client.crossDevice.start();
if (!initResult.ok) {
console.error('Init failed:', initResult.error.message);
return;
}
const { session_id, qr_url } = initResult.value;
// qr_url → https://trymellonauth.com/mobile-auth?session_id=...
// 3. Render QR (use any library: uqr, qrcode, svelte-qrcode)
renderQrCode(qr_url);
// 4. Poll for approval
const controller = new AbortController();
const pollResult = await client.crossDevice.waitForCompletion(
session_id,
controller.signal
);
if (!pollResult.ok) {
if (pollResult.error.code === 'TIMEOUT') {
console.error('QR expired. Refresh to try again.');
}
return;
}
// 5. Got session token — send to your backend
const { sessionToken } = pollResult.value;
await fetch('/api/auth/set-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken }),
});
window.location.href = '/dashboard';
[!TIP] With the default bridge domain, you don’t need to build or deploy a
/mobile-authpage. TryMellon handles the mobile screen automatically.
Configuration in Dashboard
- Go to Dashboard → Your application → Edit.
- Verify your desktop origin is in Allowed origins (e.g.
https://myapp.com). - The
qr_urlreturned byinit()will use the TryMellon bridge domain by default.
Checking primary_qr_base_url
The base URL for QR codes is controlled by primary_qr_base_url in your application settings. When using the bridge domain, this is already configured for you.
Migrating to Your Own Domain
When you’re ready to host your own /mobile-auth page:
- Deploy a page at
https://yourdomain.com/mobile-auththat handles the approval flow (see Cross-Device Authentication for the mobile code). - Update
primary_qr_base_urlin your application settings tohttps://yourdomain.com. - Add
https://yourdomain.comto Allowed origins.
That’s it. No user migration, no credential changes. The SDK’s init() will now return qr_url pointing to your domain.
[!IMPORTANT] Switching domains only changes where the QR points. Existing users keep their passkeys — no re-registration needed.
Complete Example with demo-auth-app
The demo-auth-app includes a working QR login flow:
cd demo-auth-app
cp .env.example .env
# Edit .env:
# VITE_TRYMELLON_APP_ID=your_app_id
# VITE_TRYMELLON_PUBLISHABLE_KEY=cli_xxxx
# VITE_TRYMELLON_SANDBOX=false
npm install
npm run dev
Open http://localhost:4173 on desktop. Click “Login con QR” — a QR code appears. Scan it with your phone. The bridge domain handles mobile approval; the desktop receives the session.
For detailed setup (tunnels, production keys), see demo-auth-app/PROBAR-COMO-CLIENTE.md.
Snippets for Your Frontend
React
import { TryMellon } from '@trymellon/js';
import QRCode from 'qrcode.react';
import { useState, useEffect } from 'react';
function QrLogin() {
const [qrUrl, setQrUrl] = useState<string | null>(null);
useEffect(() => {
async function start() {
const client = TryMellon.create({
appId: import.meta.env.VITE_APP_ID,
publishableKey: import.meta.env.VITE_PK,
});
if (!client.ok) return;
const init = await client.value.crossDevice.start();
if (!init.ok) return;
setQrUrl(init.value.qr_url);
const poll = await client.value.crossDevice.waitForCompletion(
init.value.session_id,
new AbortController().signal
);
if (poll.ok) {
// Send poll.value.sessionToken to your backend
}
}
start();
}, []);
return qrUrl ? <QRCode value={qrUrl} /> : <p>Loading…</p>;
}
Vanilla JS
<div id="qr-container"></div>
<script type="module">
import { TryMellon } from '@trymellon/js';
const client = TryMellon.create({
appId: 'YOUR_APP_ID',
publishableKey: 'cli_xxxx',
});
if (!client.ok) throw client.error;
const init = await client.value.crossDevice.start();
if (!init.ok) throw init.error;
// Use any QR library to render init.value.qr_url
document.getElementById('qr-container').textContent =
'Scan this QR: ' + init.value.qr_url;
const poll = await client.value.crossDevice.waitForCompletion(
init.value.session_id,
new AbortController().signal
);
if (poll.ok) {
console.log('Session token:', poll.value.sessionToken);
}
</script>
API Contract (Conceptual)
| Endpoint | Method | Purpose |
|---|---|---|
/v1/auth/cross-device/init | POST | Create QR session; returns session_id, qr_url, expires_at |
/v1/auth/cross-device/status/:sessionId | GET | Poll session status (pending → completed) |
/v1/auth/cross-device/verify | POST | Mobile sends WebAuthn assertion to complete session |
/v1/auth/cross-device/context/:sessionId | GET | Mobile gets session context (app name, approval_context) |
All endpoints require Authorization: Bearer <publishableKey> and Origin header matching an allowed origin.
Next Steps
| Topic | Link |
|---|---|
| Full cross-device API and mobile code | Cross-Device Authentication |
| Backend session validation | Backend Validation |
| Release history of QR default | QR Default Release Notes |
| Troubleshooting and support | QR Support Playbook |