QR code payload
How to build the QR code and the phone link from a login session, in any language.
The API doesn't return a QR code or an app link: you build both in the
browser from the three values of the session (referenceId,
qrReferenceSecret, createdAt), and render the QR code again every ~2
seconds.
qrTime = floor((now - createdAt) / 1000) // seconds, as a decimal string
key = SHA256(qrReferenceSecret) // the raw 32 bytes
qrAuthCode = hex(HMAC_SHA256(key, qrTime)) // lowercase hex
payload = "idfier.{referenceId}.{qrTime}.{qrAuthCode}"| Where | Content |
|---|---|
| QR code | idfier://auth?authData=<payload> |
| Button on a phone | https://www.idfier.com/auth?authData=<payload> (a universal link that opens the app) |
URL-encode the payload in both. IDfier accepts a qrTime within about 10
seconds of its own clock, which is why the code has to be refreshed.
TypeScript (browser)
type Session = { referenceId: string; qrReferenceSecret: string; createdAt: string };
export async function qrPayload(session: Session) {
const qrTime = String(Math.floor((Date.now() - new Date(session.createdAt).getTime()) / 1000));
const encoder = new TextEncoder();
const keyBytes = await crypto.subtle.digest('SHA-256', encoder.encode(session.qrReferenceSecret));
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
const mac = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(qrTime)));
const code = Array.from(mac, (byte) => byte.toString(16).padStart(2, '0')).join('');
return `idfier.${session.referenceId}.${qrTime}.${code}`;
}crypto.subtle needs a secure context: https, or localhost in
development.
Python
import hashlib, hmac, math, time
from datetime import datetime
def qr_payload(reference_id: str, qr_reference_secret: str, created_at: str) -> str:
created = datetime.fromisoformat(created_at.replace("Z", "+00:00")).timestamp()
qr_time = str(math.floor(time.time() - created))
key = hashlib.sha256(qr_reference_secret.encode()).digest()
code = hmac.new(key, qr_time.encode(), hashlib.sha256).hexdigest()
return f"idfier.{reference_id}.{qr_time}.{code}"Keep qrReferenceSecret out of logs
The browser needs it to draw the QR code, but don't log or store it: with it, anyone can produce a valid code for the session.