QR Code Rendering
Both Credential Factory and Verification Gateway hand you everything you need to put a scannable QR code in front of the holder. You have two options.
Option 1 — Use the pre-rendered qrCodeDataUrl (recommended)
Every successful POST /api/v1/iaas/credential-offers and POST /api/v1/verifier/sessions response includes qrCodeDataUrl: a complete, base64-encoded PNG as a data URL.
{
"qrCodeDataUrl": "data:image/png;base64,iVBORw0KGgo..."
}
Render it directly — no QR library required:
<img src="data:image/png;base64,iVBORw0KGgo..." alt="Scan with your wallet" />
In React:
<img src={session.qrCodeDataUrl} alt="Scan with your wallet to share your credential" />
This is the zero-dependency path and is correct for almost every integration.
Option 2 — Render the raw URL with your own QR library
If you need custom styling (brand colours, embedded logo, specific sizing/error-correction), render the raw offerUrl (Credential Factory) or authorizationUrl (Verification Gateway) with a QR library of your choice.
Node.js with qrcode:
import QRCode from 'qrcode';
const dataUrl = await QRCode.toDataURL(session.authorizationUrl, {
errorCorrectionLevel: 'M',
margin: 2,
width: 320,
});
Browser with qrcode.js:
new QRCode(document.getElementById('qr'), {
text: offer.offerUrl,
width: 320,
height: 320,
});
Treat the URL as opaque — encode it exactly as returned, with no trimming, re-encoding, or parameter manipulation. The authorizationUrl in particular contains URL-encoded JSON (client_metadata) that breaks if double-encoded.
Mobile: use a deep link instead
A user on a phone can't scan their own screen. When your flow runs on the same device as the wallet, present the URL as a tappable link — the openid-credential-offer:// and openid4vp:// schemes open the wallet app directly:
{isMobile ? (
<a href={session.authorizationUrl}>Open in your wallet</a>
) : (
<img src={session.qrCodeDataUrl} alt="Scan with your wallet" />
)}
Practical tips
- Display size: render at 240–320 px on desktop. The offer/authorization URLs are long, so the QR is dense — too small and cameras struggle.
- Contrast: dark modules on a light background. Verify scanability if you restyle (dark mode included).
- Refresh on expiry: the QR is only valid until
expiresAt. Hide it and offer a "Generate new code" action once the offer/session expires — a stale QR that scans but fails is worse UX than an explicit refresh. - Accessibility: always set meaningful
alttext (e.g. "Scan with your wallet to receive your credential"), and offer the deep-link alternative where possible. - Don't persist these artifacts:
qrCodeDataUrland the underlying URLs are short-lived secrets — don't log them or store them beyond their TTL (Security Best Practices).