A simple, secure authentication backend (TypeScript + Express + Prisma) with optional 2FA support.
This project implements user registration, login, password reset, and optional two-factor authentication using TOTP. It is intended as a focused authentication microservice that you can adapt into a larger system.
- Email/password registration and login
- JWT access tokens and refresh tokens
- Optional TOTP two-factor authentication (speakeasy)
- Password reset via email (nodemailer)
- Prisma for database access and migrations
- Rate limiting and security middlewares (helmet, express-rate-limit)
These commands are written for the fish shell (your default). Adjust if you're using bash/zsh.
- Install dependencies
pnpm install- Create a
.envfile with the required environment variables (example below). You can use printf to write a template:
printf 'DATABASE_URL="postgresql://user:pass@localhost:5432/dbname"\n\
PORT=4000\n\
JWT_SECRET="your_jwt_secret"\n\
REFRESH_TOKEN_SECRET="your_refresh_token_secret"\n\
SMTP_HOST=smtp.example.com\n\
SMTP_PORT=587\n\
SMTP_USER=your_smtp_user\n\
SMTP_PASS=your_smtp_password\n' > .env- Generate Prisma client and run migrations (development)
pnpm prisma generate
pnpm prisma migrate dev --name init- Run in development (watch)
pnpm run dev- Build and run production
pnpm run build
pnpm startAt minimum you will want to set:
- DATABASE_URL — Prisma-compatible connection string
- PORT — server port (default: 4000)
- JWT_SECRET — secret used to sign access tokens
- REFRESH_TOKEN_SECRET — secret for refresh tokens
- SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS — SMTP settings for sending reset emails
- EMAIL_FROM — optional "from" address for outgoing emails
Add any other environment keys used by your deployment/CI.
- Schema:
prisma/schema.prisma - Migrations are stored in
prisma/migrations.
Common commands:
pnpm prisma migrate dev # run migrations locally and generate client
pnpm prisma migrate deploy # run migrations in production
pnpm prisma studio # open Prisma Studiodist/(compiled JS) is ignored by.gitignore. Build artifacts should not be committed. If you accidentally committeddist/in the past, the repository has a cleanup commit to remove trackeddist/files and.gitignoreprevents re-adding them.- Keep secrets out of git. This repository's
.gitignorealready contains.env*patterns.
Brief overview of the primary files and folders you'll see in this repo:
src/— TypeScript source filesindex.ts— application entry pointcontrollers/— Express controllers (auth, 2FA)services/— business logic and integrations (authServices, emailService, twoFactorService)routes/— route definitions (v1)middleware/— Express middleware (auth, rate limiting)validators/— request validation logictypes/— shared TypeScript types
prisma/— Prisma schema and migrationsgenerate/— generated Prisma client (do not commit; listed for context).env*andprod.env— environment variable files (ignored in git)README.md,package.json,tsconfig.json,Dockerfile— repo metadata and build config
This structure keeps clear separation between HTTP handling (controllers/routes) and business logic (services), making unit testing and maintenance easier.
This project follows a lightweight maintenance workflow to keep the repo clean and stable:
- Branching: use topic branches from
master(or create amainbranch if you prefer). Prefix branch names with the issue or feature, e.g.feat/2fa-setuporfix/login-guard. - Commits: follow Conventional Commits (e.g.,
feat(...),fix(...),chore(...)) so changelogs can be generated later. - Pull requests: open PRs for all changes; include a description of the change and link to any related issues. Keep PRs small and focused.
- Code style: keep TypeScript strict (tsconfig) and prefer small, well-tested functions in
services/. - Tests: add unit tests (Jest/Vitest) for
services/and integration tests (supertest) for critical endpoints.
- Use
pnpmto manage dependencies. Runpnpm upto update dependencies and test locally before committing. - Consider Dependabot or GitHub Actions to automatically open PRs for dependency updates.
- Never commit secrets. Use
.gitignore(already configured) and keep production secrets in a secret manager or via the deployment platform. - Rotate JWT/refresh secrets periodically and store them in a secure vault.
For questions or to report bugs, open an issue in the repository. If you want direct help with setup, tell me what environment you deploy to (Docker, DigitalOcean, Kubernetes) and I can provide tailored instructions.
This repository documents a single-container Docker deployment (build + run) since you deploy with Docker directly. If you use a multi-service orchestration later you can add a docker-compose.yml or Kubernetes manifests.
Build and run (single container)
# build the image from the project root
docker build -t auth-backend:latest .
# run the container using an env file (recommended)
# create a `prod.env` file with your environment variables and do:
docker run -d \
--name auth-backend \
-p 8000:8000 \
--env-file prod.env \
auth-backend:latestNotes
- Make sure the
PORTvalue inprod.envmatches the port you expose (the example uses8000). The project default in development is4000— keep them consistent. - Use Docker secrets or your orchestration's secret management for production instead of plaintext env files.
- Build the app inside the image (
pnpm run build) and use a multi-stage Dockerfile to produce a small production image. - To run Prisma migrations, either run them before starting the container or exec into the container and run
pnpm prisma migrate deploy.
If you'd like, I can add a minimal Dockerfile or a prod.env.example to this repo and commit them for you.
There are no tests included by default. I recommend adding a small test suite (Jest or Vitest) with:
- unit tests for authentication logic
- integration tests for the API endpoints (supertest)
If you plan to continue development:
- Open issues for features you want (2FA UX, refresh token rotation, session invalidation)
- Add tests for critical flows (login, registration, password reset)
- Harden token handling and add rate limits where needed