Skip to content

ShivaRap/lendesk-coding-challenge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Authentication API

Note: This is a coding challenge submission built to demonstrate clean architecture, security best practices, and production-ready patterns—not a generic boilerplate.

What This Does

This API handles user registration and authentication. It validates password complexity, enforces unique usernames, and generally keeps the bad guys out while letting the good guys in.

Tech Stack

  • Runtime: Node.js (Primary Backend)
  • Framework: Express.js (HTTP Server)
  • Data Store: Redis (User Storage)
  • Security: bcrypt (for hashing passwords into gibberish), helmet (for HTTP headers)
  • Validation: express-validator (the bouncer)
  • Logging: Pino (because console.log is so 2015)

Architecture & Approach

Design Philosophy

I chose a Layered Architecture (Routes → Controller → Service → Data) to ensure separation of concerns. This makes the codebase modular, testable, and easy to maintain.

  1. Controller Layer: Handles HTTP requests, validation results, and sending responses. It knows nothing about business logic.
  2. Service Layer: Contains the core business logic (hashing passwords, checking uniqueness). It knows nothing about HTTP.
  3. Data Utility: A thin wrapper around the Redis client to manage connections.

Why Redis?

For this specific assignment, Redis was chosen as the primary data store for speed and simplicity.

  • Performance: In memory lookups are lightning fast.
  • Structure: We use HMSET to store user objects and a simple key-value strategy for uniqueness checks.

Security Decisions

  • Statelessness: The API is currently stateless. A successful login confirms credentials but does not issue a session (JWT) yet. This keeps the initial implementation lightweight while being ready for JWT integration.
  • Storage: We use bcrypt because it is computationally expensive, making brute-force attacks on the database unfeasible.

Setup & Installation

Prerequisites

  • Node.js (v18+)
  • Redis Server (must be running locally or on a server)

1. Install & Start Redis

macOS (Homebrew):

brew install redis
brew services start redis

Tip: brew services start redis runs it in the background so you don't need a separate terminal window cluttering your life.

Windows:

  • Download from redis.io (or use WSL).
  • Run redis-server in a separate terminal window.

2. Clone & Install

git clone https://github.com/ShivaRap/lendesk-coding-challenge.git
cd auth-api
npm install

3. Configure Environment

Copy the example environment file:

cp .env.example .env

Edit .env if your Redis is hiding somewhere other than localhost:6379.

4. Run Application

Ensure Redis is running (seriously, check it), then:

Development Mode (with hot reload): Open a new terminal window!

npm run dev

Production Mode:

npm start

API Endpoints

1. Register User

Create a new user account.

  • URL: POST /users
  • Body:
    {
      "username": "jdoe123",
      "password": "Password123!"
    }
  • Password Rules: At least 8 characters with uppercase, lowercase, number, and special character. No compliance, no entry.
  • Success Response (200 OK):
    {
      "success": true,
      "message": "User registered successfully",
      "data": { ... }
    }

2. Authenticate User

Verify user credentials.

  • URL: POST /auth
  • Body:
    {
      "username": "jdoe123",
      "password": "Password123!"
    }
  • Success Response (200 OK):
    {
      "success": true,
      "message": "Authentication successful",
      "data": { "authenticated": true, ... }
    }
  • Error Response (401 Unauthorized):
    {
      "success": false,
      "error": { "message": "Invalid credentials", ... }
    }

Troubleshooting

"Error: connect ECONNREFUSED 127.0.0.1:6379"

Translation: The app tried to call Redis, but nobody answered. Fix: You forgot to start Redis. Run brew services start redis or redis-server.

"Validation Error: Username must be alphanumeric"

Translation: You used a special character like _ or -. Fix: Stick to letters and numbers. cooluser1 is fine, cool_user is illegal.


Security Implementation

Implemented Measures

  1. Password Hashing: We use bcrypt (salt rounds 10), so even if the DB leaks, the passwords are just noise.

  2. Strict Input Sanitation: express-validator rejects "dirty" inputs before they even reach business logic.

  3. Graceful Error Handling: Centralized error middleware catches crashes and returns clean 500 JSON errors instead of killing the server.

  4. Password Complexity: Weak passwords like "password123" are rejected immediately.

  5. Security Headers: helmet keeps the HTTP headers tight.

  6. No Plaintext: We never save passwords in plain text. Ever.

  7. Graceful Shutdown: The app handles termination signals (SIGTERM/SIGINT) to close connections cleanly.

Environment Config

We follow the 12-Factor App pattern—configuration lives in .env, not hardcoded. The cp .env.example .env command just gives you a starting template.

Security Enhancements (TODO)

Things we should totally add before going to actual production:

  • Rate Limiting: Stop brute-force attacks on /auth.
  • HTTPS: Encrypt the transport layer (SSL/TLS).
  • Account Lockout: Lock account after 5 failed attempts (needs a Redis counter).
  • JWT/Session: Implementing tokens for stateful management if needed.

Testing

Run the automated tests (if you want to see green checkmarks):

npm test

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors