No description
  • TypeScript 77.8%
  • JavaScript 21.7%
  • CSS 0.5%
Find a file
2026-06-15 18:05:58 +01:00
apps/web refactor 2026-06-15 18:05:58 +01:00
packages refactor 2026-06-15 18:05:58 +01:00
.gitignore refactor 2026-06-15 18:05:58 +01:00
.npmrc refactor 2026-06-15 18:05:58 +01:00
package.json refactor 2026-06-15 18:05:58 +01:00
pnpm-workspace.yaml refactor 2026-06-15 18:05:58 +01:00
README.md refactor 2026-06-15 18:05:58 +01:00

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:

  1. Publish this as a hosted HTTPS PWA.
  2. Wrap it as a Trusted Web Activity for Play Store distribution.
  3. 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.