Files
Codeman/src/web/ports/auth-port.ts
T
arkonandClaude Opus 4.6 746004c461 feat: implement QR code authentication for tunnel access
Adds ephemeral single-use QR tokens for passwordless tunnel login.
Scanning the QR auto-authenticates; bare tunnel URL requires Basic Auth.

Backend:
- TunnelManager: 60s token rotation, 90s grace, rejection-sampled 6-char
  base62 short codes, Map-based O(1) lookup, SVG caching, global rate limit
- Auth middleware: /q/ bypass, separate qrAuthFailures counter, enhanced
  AuthSessionRecord with device context (ip, ua, createdAt, method)
- Routes: GET /q/:code (consume + cookie + redirect), POST /api/tunnel/qr/
  regenerate, POST /api/auth/revoke, updated GET /api/tunnel/qr with cache
- SSE: tunnel:qrRotated, tunnel:qrRegenerated, tunnel:qrAuthUsed events
- Audit: qr_auth lifecycle log entries

Frontend:
- Auto-refresh QR via inline SVG in SSE (fallback fetch if absent)
- 60s countdown indicator on QR badge
- Regenerate QR button
- QRLjacking detection toast with [Revoke All] action button (10s duration)
- showToast enhanced with optional duration and action button support

Fixes:
- /api/logout now invalidates server-side session token (was only clearing
  browser cookie, leaving token valid for replay)

Tests: 20 new tests in test/qr-auth.test.ts covering token lifecycle,
bias check, rate limiting, SVG caching, and full server integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 06:05:26 +01:00

21 lines
626 B
TypeScript

/**
* @fileoverview Auth port — capabilities for authentication state.
* Route modules that need access to auth sessions or QR rate limiting depend on this port.
*/
import type { StaleExpirationMap } from '../../utils/index.js';
/** Enhanced session record with device context for audit logging */
export interface AuthSessionRecord {
ip: string;
ua: string;
createdAt: number;
method: 'qr' | 'basic';
}
export interface AuthPort {
readonly authSessions: StaleExpirationMap<string, AuthSessionRecord> | null;
readonly qrAuthFailures: StaleExpirationMap<string, number> | null;
readonly https: boolean;
}