- TypeScript 77.8%
- JavaScript 21.7%
- CSS 0.5%
| apps/web | ||
| packages | ||
| .gitignore | ||
| .npmrc | ||
| package.json | ||
| pnpm-workspace.yaml | ||
| README.md | ||
TOTP Authenticator — Next.js + pnpm + SQLite PWA
This is an installable, multi-user TOTP authenticator web app converted to a Next.js + pnpm workspace.
It keeps the server-backed architecture and adds camera QR scanning so users can scan authenticator QR codes directly from another screen, printed document, or external device.
Stack
Browser / installed PWA
│
Next.js React app
│
Express API mounted in a custom Next.js server
│
SQLite database file through sql.js
Workspace layout:
totp-authenticator-next-pnpm/
├── apps/
│ └── web/ # Next.js PWA + Express API server
├── packages/
│ ├── core/ # TOTP, vault encryption, QR parsing, backup/import logic
│ └── shared/ # Shared TypeScript types
├── pnpm-workspace.yaml
└── package.json
What is included
- Next.js app using the App Router.
- pnpm workspace.
- React frontend.
- Node.js + Express API inside a custom Next.js server.
- SQLite storage through
sql.js, avoiding native SQLite compilation. - Multiple users.
- Email/password registration.
- Email verification before login.
- HTTP-only cookie sessions.
- One encrypted vault per verified user.
- Client-side vault encryption; the server stores only encrypted vault JSON and cannot decrypt TOTP secrets.
- Camera QR scanning with
getUserMedia. - QR image import from screenshots/files.
otpauth://URI import.- Encrypted backup export/import.
- Auto-lock and manual vault lock.
- Installable PWA support.
- Web app manifest.
- Service worker.
- PWA icons, including maskable icon.
- API/vault responses are intentionally not cached by the service worker.
- Basic rate limiting.
- Security headers with camera permission enabled for this app only.
- Audit log table.
Requirements
- Node.js 20 or newer
- pnpm
Enable pnpm with Corepack if needed:
corepack enable
corepack prepare pnpm@9.15.0 --activate
Install
From the project root:
pnpm install
Development
pnpm dev
Open:
http://localhost:3000
The custom server runs the Next.js app and the /api/* backend on the same origin.
Production
Create the environment file:
cp apps/web/.env.example apps/web/.env
Edit at least:
NODE_ENV=production
APP_URL=https://your-domain.com
SMTP_HOST=your-smtp-host
SMTP_PORT=587
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password
SMTP_FROM="TOTP Authenticator <no-reply@your-domain.com>"
Build and start:
pnpm build
pnpm start
Default production URL:
http://localhost:3000
For public PWA installability and camera access, deploy behind HTTPS. localhost is accepted for development.
Email verification in development
If SMTP is not configured, the server prints the verification link in the console and the frontend also shows a development verification link after registration.
That is intentional for local testing only. Configure SMTP in production.
Camera QR scanner
The camera scanner is inside the import flow:
Unlock vault → Import → Open camera → Scan QR code
Security behavior:
- Camera frames are processed locally in the browser.
- QR images and camera frames are not uploaded to the backend.
- Only the parsed
otpauth://account data is added to the local decrypted vault. - The vault is encrypted again before being saved to SQLite.
The app allows camera access with this server header:
Permissions-Policy: camera=(self)
PWA behavior
The app is installable on supported browsers when served from HTTPS, or from localhost during development.
The service worker caches the app shell and static assets. It does not cache:
/api/*- vault responses
- login/session responses
- write requests
Practical behavior:
- If the app is already open and the vault is unlocked, TOTP codes continue working in memory.
- On a fresh cold start, the app needs the backend/API to log in and load the encrypted vault from SQLite.
- Decrypted secrets are never stored by the service worker.
Android later
No rewrite is needed for the next Android step.
Recommended path:
- Publish this as a hosted HTTPS PWA.
- Wrap it as a Trusted Web Activity for Play Store distribution.
- Move to Capacitor only if you need native secure storage, biometric unlock, push notifications, or deeper Android APIs.
Because the core authenticator logic is in packages/core, a later Android shell can reuse the same TOTP, encryption, and QR parsing logic.