Documentation

Pairing a browser by QR code

Your page shows a QR code, the person scans it with the Touchque app, and the phone that scanned is the one asked to approve, once, with number matching. No username to type, and a phishing page cannot borrow the approval: the QR is bound to this browser's key and the request goes only to the phone that read it.

How it works
  1. The page sends its browser key's thumbprint (@touchque/web browserKey.thumbprint()) to your server.
  2. Your server calls qrPairings.create: the SDK makes a nonce, sends only its SHA-256 and returns uri (touchque://pair-browser?…). Show it as a QR code; the nonce never leaves your server.
  3. Poll qrPairings.get until confirmed, then send login.request with qrPairing: Touchque asks that phone only, for the user it scanned for, once.
States
stateMeaning
waitingShown, not scanned yet.
scannedA phone holding one of your users' accounts read it; the person has not confirmed yet.
confirmedThe person confirmed on the phone: externalUsername says who, proximity how near the phone was (same network, same country). Send the login request now.
requestedThe login request went to that phone (requestId); wait for it as usual.
approvedApproved on the phone.
refusedYour server said no (refuse); the phone shows your reason and message.
reportedThe person said someone told them to scan it: their 2FA freezes.
expiredNot used within ttlSeconds (30 to 300, default 120).
Your own checks first. When your rules say no for this browser (an unapproved computer, too many browsers, rate limits), call qrPairings.refuse with a reason (policy, device_not_approved, too_many_browsers, rate_limited, no_account, other) and a short message: the phone tells the person why. It works after the scan, before the login request.
// The page (@touchque/web): the browser key's thumbprint
import { browserKey } from '@touchque/web';
await browserKey.proof();                          // makes the key if there is none
const keyThumbprint = await browserKey.thumbprint();

// Your server (Node)
const p = await tq.qrPairings.create({ keyThumbprint, clientIp: req.ip, userAgent: req.get('user-agent') });
// show p.uri (touchque://pair-browser?…) as a QR code, then poll:
const now = await tq.qrPairings.get(p.pairingId);   // waiting → scanned → confirmed
if (now.state === 'confirmed') {
  await tq.login.request({ externalUsername: now.externalUsername, type: 'LOGIN', qrPairing: p.pairingId });
}
// or, when your own checks say no:
await tq.qrPairings.refuse(p.pairingId, { reason: 'device_not_approved', message: 'Ask IT to approve this computer.' });