All Projects

Live

API Service

2FA with Authenticator

A RESTful API for user authentication and management featuring Time-Based One-Time Passwords (TOTP) for Authenticator apps.

Role: Backend Developer


Node.jsMongoDBTOTP2FASecurityExpress.jsOTP
Problem Statement

Traditional password-only authentication is vulnerable to breaches. There was a need for an additional security layer that users can easily integrate with standard apps like Google Authenticator or Authy.

Target users: Developers looking to integrate 2FA flows into their applications, or platforms needing an extra layer of account security.

Project Walkthrough

When I set out to build this authentication service, the goal was simple but critical: provide a robust, drop-in backend for Two-Factor Authentication (2FA). Passwords alone aren't enough anymore, and integrating Time-Based One-Time Passwords (TOTP) that work seamlessly with apps like Google Authenticator or Authy is a must-have for modern applications. This project served as a deep dive into the mechanics of TOTP and cryptographic secrets.

I architected the API using Node.js and Express, connected to a MongoDB database via Mongoose. For the core 2FA functionality, I used the `otpauth` library to handle the algorithm alongside `qrcode` and `hi-base32` to generate secrets and scan-able URIs. When a user requests to enable 2FA, the server generates a unique Base32 secret, builds the TOTP URL, and renders a QR code on the fly using EJS. From there, users can scan it into their app, verify the setup with an initial token, and subsequently use their app to validate login attempts.

One of the interesting engineering challenges was accounting for the fact that a user's phone clock and the server clock might not be perfectly synchronized. I handled this by implementing a validation window that allows for minor time drift without immediately rejecting valid tokens. This project demonstrates my ability to implement complex, security-focused features from scratch and structure a backend API that is both clean and extensible.


Tech Stack

Languages

JavaScript (Node.js)

Frameworks

Express.jsEJS (Templating)

Databases

MongoDB (Mongoose)

Cloud & Infrastructure

Render

Dev Tools

PostmanNodemon

Authentication

Custom SHA-256 Password Hashing + TOTP (otpauth) for 2FA


System Architecture

Architecture Pattern

Layered MVC Architecture

Request Data Flow

Client Request -> Express Route -> Controller -> Mongoose Model -> MongoDB -> JSON Response / EJS View

Key Engineering Decisions

Separated routes, controllers, and models for modularity. Used Node's native `crypto` module for SHA-256 hashing without external dependencies for simplicity. Leveraged `otpauth` for TOTP algorithm handling rather than building the complex time-based cryptography from scratch.

Database Design

Single `users` collection containing standard auth fields (email, name, password) and 2FA-specific fields (otp_enabled, otp_verified, otp_base32, otp_auth_url).

Module Structure

routes/controllers/models/middlewares/database/

Key Features & Implementation

OTP Generation & QR Code Rendering

Generates a unique 2FA secret for a user and displays a QR code to be scanned by an Authenticator app.

How it was built

Generates a random Base32 string as the secret using `crypto` and `hi-base32`. Constructs a TOTP URI with `otpauth` and generates a Data URL for a QR code using the `qrcode` package. The result is dynamically rendered to the user via an EJS view.

OTP Verification & Validation

Allows users to verify their Authenticator setup and validate subsequent login attempts with OTPs.

How it was built

Takes the 6-digit token provided by the user, looks up their saved Base32 secret, and uses `otpauth.TOTP.validate()` to check its correctness against the current time. Provides an extended time window parameter (window: 1) for minor time drift.

Disable OTP

Allows users to safely disable two-factor authentication on their account.

How it was built

Requires the user to provide a valid OTP token one last time to authorize the disabling action. If successful, toggles `otp_enabled` to false in the database.


Challenges & Engineering Decisions

One of the most delicate parts was handling the time-based nature of the OTP algorithm, ensuring accurate validation when minor time discrepancies occur between the server and the user's authenticator app (solved using validation windows). A future security consideration would be swapping the simple SHA-256 password hash for bcrypt with a proper salt to protect against rainbow table attacks.


API Documentation

API Route Namespaces

/api/auth/api/healthchecker