QR Login & Multi-Device
QR Login & Multi-Device
Cross-device login via QR code — log into a new device by scanning with a device you're already logged into.
QR Login Flow
The QR login lets a user who is already authenticated on one device (phone, tablet) log into a new device (desktop, laptop) by scanning a QR code. No password entry needed on the new device.
The Flow
Device A (new, not logged in) Device B (already logged in)
┌──────────────────────────┐ ┌──────────────────────────┐
│ │ │ │
│ Login Screen │ │ (user's phone/tablet) │
│ │ │ │
│ [📱 Log in with QR] │ │ │
│ │ │ │
└──────────────────────────┘ └──────────────────────────┘
│ │
▼ │
┌──────────────────────────┐ ┌──────────────────────────┐
│ │ │ │
│ ┌────────────────────┐ │ │ │
│ │ ▄▄▄ ▄▄▄ ▄▄▄ │ │ scanned │ Browser opens: │
│ │ ▄▄▄ ▄▄▄ ▄▄▄ │ │ ◄────────── │ core.auth/qr/confirm │
│ │ ▄▄▄ ▄▄▄ ▄▄▄ │ │ by user │ │
│ └────────────────────┘ │ │ "Log in on new device?" │
│ │ │ │
│ Scan this code with │ │ Device: Chrome/Windows │
│ a device you're │ │ IP: 192.168.1.42 │
│ already logged into │ │ Location: Lausanne, CH │
│ │ │ │
│ Waiting... │ │ [Approve] [Deny] │
│ │ │ │
└──────────────────────────┘ └──────────────────────────┘
│ │
│ ▼
│ ┌──────────────────────────┐
│ │ ✓ Login approved │
│ │ │
│ │ You can close this page │
│ └──────────────────────────┘
▼
┌──────────────────────────┐
│ │
│ ✓ Logged in! │
│ │
│ [Continue] │
│ │
└──────────────────────────┘Step-by-Step
1. Device A requests QR code
When the user clicks "Log in with QR code" on the login screen:
POST /auth/qr/generate
→ Returns:
{
"qr_token": "abc-123-uuid",
"qr_data": "https://core.auth.medkronos.com/qr/scan?token=abc-123-uuid",
"expires_in": 300
}qr_tokenis a one-time token stored in a temporary table (Redis or DB)- Expires in 5 minutes
- The QR code encodes the
qr_dataURL
2. Device A polls for status
GET /auth/qr/status?token=abc-123-uuid
→ Returns:
{ "status": "pending" } // waiting for scan
{ "status": "scanned" } // scanned, waiting for approval
{ "status": "approved" } // user approved — login complete
{ "status": "denied" } // user denied
{ "status": "expired" } // 5 min timeoutDevice A polls every 2 seconds. Stops when status is
approved, denied, or expired.3. User scans QR on Device B
The QR code URL opens
core.auth/qr/scan?token=abc-123-uuid on Device B's browser. If Device B has an active session (session_ref cookie), core.auth:- Validates the qr_token
- Reads the session from the cookie
- Updates qr_token status to
scanned - Shows the confirmation screen
4. Confirmation screen on Device B
┌─────────────────────────────────────────┐
│ │
│ Log in on new device? │
│ │
│ Device: Chrome 125 on Windows │
│ IP: 192.168.1.42 │
│ Location: Lausanne, Switzerland │
│ Time: June 25, 2026 at 19:45 │
│ │
│ [ Approve ] │
│ [ Deny ] │
│ │
└─────────────────────────────────────────┘5. User approves
POST /auth/qr/confirm
{ "token": "abc-123-uuid", "action": "approve" }core.auth:
- Validates qr_token
- Gets the user from Device B's session
- Creates a new session for Device A
- Updates qr_token status to
approved - Returns the session info (session_ref, user info)
6. Device A completes login
Device A's poll receives
status: approved with session data. It:- Stores the session_ref
- Redirects to the app or dashboard
- User is logged in on Device A
Security Considerations
| Concern | Mitigation |
| QR code interception | One-time token, 5-min expiry, HTTPS only |
| Replay attacks | Token consumed on first approval |
| Phishing (fake QR) | QR encodes core.auth domain — user sees the real domain in browser |
| Session leakage | New session is created specifically for Device A, not a copy of Device B's session |
| Unauthorized approval | Device B must be authenticated; confirmation shows device details for user verification |
QR Token Storage
CREATE TABLE qr_login_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
token VARCHAR(64) UNIQUE NOT NULL,
status VARCHAR(20) DEFAULT 'pending', -- pending, scanned, approved, denied, expired
user_id UUID, -- set when scanned (from Device B's session)
device_info JSON, -- Device A's user agent, IP
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP NOT NULL, -- NOW() + 5 min
approved_at TIMESTAMP
);
CREATE INDEX idx_qr_token ON qr_login_tokens(token);
CREATE INDEX idx_qr_expires ON qr_login_tokens(expires_at);Cleanup job removes expired tokens every 5 minutes.
Multi-Device Session Management
Users can view and manage all their active sessions from the Account Management frontend (see Frontend Architecture).
Session List
┌─────────────────────────────────────────┐
│ │
│ Active Sessions │
│ │
│ 📱 This device │
│ iPhone 15, Safari │
│ Lausanne, Switzerland │
│ Last active: now │
│ │
│ 💻 Desktop │
│ Chrome 125, Windows │
│ Lausanne, Switzerland │
│ Last active: 2 hours ago │
│ │
│ [Revoke] each session │
│ [Log out everywhere] │
│ │
└─────────────────────────────────────────┘Device Trust (Future)
Planned extension: mark devices as "trusted" to skip 2FA on known devices.
- User logs in with 2FA on a device → "Trust this device?" prompt
- If trusted → skip 2FA for 30 days on this device
- Device identified by fingerprint (browser + OS + hardware characteristics)
- Trust expires after 30 days, requiring re-verification
This is a Phase 2+ feature. For now, 2FA is required every time if enabled.
See also: Registration & Login for the main login flow, Session Architecture for session mechanics.