All projects
Completed2024

SecureAuth API

A production-ready JWT authentication API with refresh token rotation, device session management, and role-based access control. Built with Node.js, Express, and PostgreSQL.

Node.jsTypeScriptPostgreSQLJWTRESTExpress

Overview

SecureAuth is a standalone authentication microservice you can drop into any project. It handles everything from initial registration to token refresh, session management, and role-based permissions — so you don't have to rewrite auth from scratch every time.

The problem it solves

I kept writing the same authentication boilerplate across projects. Basic JWT, no refresh tokens, no session management, no way to revoke tokens. It worked until it didn't.

SecureAuth is what I wish I had from the start.

Architecture

The API follows a clean separation: routes handle HTTP concerns, services contain the business logic, and repositories handle all database interactions. Nothing touches the database directly except the repository layer.

src/
├── routes/
│   ├── auth.routes.ts     # /register, /login, /refresh, /logout
│   └── users.routes.ts    # /me, /sessions, /sessions/:id
├── services/
│   ├── auth.service.ts    # Token generation, validation, rotation
│   └── user.service.ts    # User CRUD, password hashing
├── repositories/
│   ├── user.repo.ts
│   └── session.repo.ts
└── middleware/
    ├── authenticate.ts    # Verify access token
    └── authorize.ts       # Check roles/permissions

Key features

Database schema

The two core tables beyond users are refresh_tokens and audit_logs:

CREATE TABLE refresh_tokens (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     UUID REFERENCES users(id) ON DELETE CASCADE,
  token       TEXT UNIQUE NOT NULL,
  family      UUID NOT NULL,       -- for theft detection
  device_info JSONB,
  expires_at  TIMESTAMPTZ NOT NULL,
  created_at  TIMESTAMPTZ DEFAULT NOW()
);

What I learned

The hardest part was handling concurrent token refresh requests — if two API calls fire at the same moment with an expired access token, both shouldn't try to refresh simultaneously. I solved this with a client-side queue that holds pending requests while a refresh is in flight.

The second hard thing was testing. Auth has a lot of edge cases: expired tokens, revoked tokens, concurrent requests, clock skew. I ended up with a comprehensive test suite using Jest and Supertest before I trusted it in production.

All projects