Getting Started
QR Sign-In
Start a desktop challenge, approve it on the phone, and claim the Better Auth session back on desktop.
QR Sign-In
Use @onmax/better-auth-mobile-qr when the user starts on desktop but the signing key lives on the phone inside the Nimiq Pay mini-app.
Register the QR plugin
server/auth.config.ts
import { mobileQrSignIn } from '@onmax/better-auth-mobile-qr'
import { createNimiqPayMobileQrProvider } from '@onmax/mobile-signer-nimiq-pay'
export default {
plugins: [
mobileQrSignIn({
appName: 'My Desktop Login',
endpointPrefix: '/mobile-qr',
trustedOrigins: [
'http://127.0.0.1:3000',
'http://localhost:3000',
],
providers: [
createNimiqPayMobileQrProvider(),
],
returnTo: '/qr/result',
signingPathTemplate: '/qr/phone/{challengeId}',
}),
],
}
Desktop flow
qr-desktop.ts
import { pollMobileQrSignIn, startMobileQrSignIn } from '@onmax/better-auth-mobile-qr/client'
const started = await startMobileQrSignIn(authClient.$fetch.bind(authClient), {
endpointPrefix: '/mobile-qr',
providerId: 'nimiq-pay',
returnTo: '/qr/result',
})
const status = await pollMobileQrSignIn(authClient.$fetch.bind(authClient), {
endpointPrefix: '/mobile-qr',
challengeId: started.challengeId,
})
Phone flow
qr-phone.ts
import { completeMobileQrChallenge } from '@onmax/mobile-signer-bridge'
import { createNimiqPaySignerBridge } from '@onmax/mobile-signer-nimiq-pay'
await completeMobileQrChallenge($fetch, {
challengeId,
endpointPrefix: '/api/auth/mobile-qr',
providerId: 'nimiq-pay',
bridge: createNimiqPaySignerBridge(),
})
Endpoint contract
| Endpoint | Purpose |
|---|---|
POST /mobile-qr/start | Creates a pending challenge and sets the desktop poll cookie |
GET /mobile-qr/challenge/:challengeId | Returns the server-issued message for the phone |
POST /mobile-qr/complete | Verifies the phone assertion and marks the challenge approved |
GET /mobile-qr/status?challengeId=... | Checks the desktop poll cookie and claims the Better Auth session |
Session ownership
The phone never receives a Better Auth session token in this flow. The phone only approves the challenge. The desktop creates and stores the actual Better Auth session after the status endpoint confirms the poll cookie and sees an approved signer.
trustedOrigins to the exact desktop origins you expect and keep the QR page on that origin.