TryMellon
Navigation

QR Default Domain — Integration Guide

Use the TryMellon bridge domain to ship cross-device QR login without deploying your own mobile-auth page.

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   │       │
│                               └─────────────────────────┘       │
└──────────────────────────────────────────────────────────────────┘
  1. Desktop calls sdk.auth.crossDevice.init() → gets qr_url pointing to the bridge domain.
  2. User scans the QR with their phone.
  3. Mobile opens the bridge URL → TryMellon’s hosted page triggers WebAuthn (FaceID / TouchID).
  4. Desktop polling detects completed → receives session_token.
  5. Your app sets a session cookie and redirects to the dashboard.

Prerequisites

RequirementDetails
SDK@trymellon/js ≥ 1.7
ApplicationCreated in TryMellon dashboard with your desktop origin in Allowed origins
PlanGrowth, 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.auth.crossDevice.init();
if (!initResult.ok) {
  console.error('Init failed:', initResult.error.message);
  return;
}

const { session_id, qr_url } = initResult.value;
// qr_url → https://trymellon-landing.pages.dev/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.auth.crossDevice.waitForSession(
  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-auth page. TryMellon handles the mobile screen automatically.


Configuration in Dashboard

  1. Go to Dashboard → Your application → Edit.
  2. Verify your desktop origin is in Allowed origins (e.g. https://myapp.com).
  3. The qr_url returned by init() 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:

  1. Deploy a page at https://yourdomain.com/mobile-auth that handles the approval flow (see Cross-Device Authentication for the mobile code).
  2. Update primary_qr_base_url in your application settings to https://yourdomain.com.
  3. Add https://yourdomain.com to 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.auth.crossDevice.init();
      if (!init.ok) return;
      setQrUrl(init.value.qr_url);

      const poll = await client.value.auth.crossDevice.waitForSession(
        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.auth.crossDevice.init();
  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.auth.crossDevice.waitForSession(
    init.value.session_id,
    new AbortController().signal
  );
  if (poll.ok) {
    console.log('Session token:', poll.value.sessionToken);
  }
</script>

API Contract (Conceptual)

EndpointMethodPurpose
/v1/auth/cross-device/initPOSTCreate QR session; returns session_id, qr_url, expires_at
/v1/auth/cross-device/status/:sessionIdGETPoll session status (pendingcompleted)
/v1/auth/cross-device/verifyPOSTMobile sends WebAuthn assertion to complete session
/v1/auth/cross-device/context/:sessionIdGETMobile gets session context (app name, approval_context)

All endpoints require Authorization: Bearer <publishableKey> and Origin header matching an allowed origin.


Next Steps

TopicLink
Full cross-device API and mobile codeCross-Device Authentication
Backend session validationBackend Validation
Release history of QR defaultQR Default Release Notes
Troubleshooting and supportQR Support Playbook