Skip to content

FrostDigital/fruster-auth-service

Repository files navigation

Fruster Authentication Service

A production-ready authentication microservice built with the Fruster framework. Provides JWT-based authentication with refresh tokens, password management, and user registration.

Features

  • User registration with email and username
  • Login with JWT access tokens
  • Refresh token rotation for secure token renewal
  • Password change with validation
  • Token validation endpoint
  • Password strength requirements
  • Email format validation
  • MongoDB storage with proper indexing
  • Comprehensive test suite

Architecture

This service follows Fruster best practices:

  • Handlers: Message handlers for NATS subjects (both service-to-service and HTTP)
  • Managers: Business logic layer (AuthManager, TokenManager)
  • Repositories: Data access layer (UserRepository)
  • Models: TypeScript interfaces with JSDoc for schema generation

Prerequisites

  • Node.js 18+
  • NATS server
  • MongoDB

Installation

npm install

Configuration

Create a .env file or set environment variables:

# NATS Configuration
NATS_URL=nats://localhost:4222

# MongoDB Configuration
MONGO_URL=mongodb://localhost:27017
MONGO_DB_NAME=auth-service

# JWT Configuration
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d

# Bcrypt Configuration
BCRYPT_SALT_ROUNDS=10

Running the Service

# Development mode
npm start

# Development with build
npm run start:build

# Build only
npm run build

Testing

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

API Endpoints

Register User

Subject: auth-service.register or http.post.auth.register

Request:

{
  email: string;        // Valid email format
  username: string;     // 3-30 chars, alphanumeric + _ -
  password: string;     // Min 8 chars, requires uppercase, lowercase, number, special char
  name?: string;        // Optional full name
  roles?: string[];     // Optional roles (defaults to ["user"])
}

Response: 201 Created

{
  user: User;
  tokens: {
    accessToken: string;
    refreshToken: string;
    expiresIn: number;
  }
}

Login

Subject: auth-service.login or http.post.auth.login

Request:

{
  email: string;
  password: string;
}

Response: 200 OK

{
  user: User;
  tokens: {
    accessToken: string;
    refreshToken: string;
    expiresIn: number;
  }
}

Validate Token

Subject: auth-service.validate-token or http.post.auth.validate

Request:

{
  token: string;  // JWT access token
}

Response: 200 OK

{
  valid: boolean;
  payload?: {
    userId: string;
    email: string;
    username: string;
    roles?: string[];
  }
}

Refresh Token

Subject: auth-service.refresh-token or http.post.auth.refresh

Request:

{
  refreshToken: string;
}

Response: 200 OK

{
  tokens: {
    accessToken: string;
    refreshToken: string;
    expiresIn: number;
  }
}

Change Password

Subject: auth-service.change-password or http.post.auth.change-password

Request:

{
  userId: string;
  oldPassword: string;
  newPassword: string;  // Min 8 chars, requires uppercase, lowercase, number, special char
}

Response: 200 OK

{
  success: boolean;
  message: string;
}

Get User by ID

Subject: auth-service.get-user or http.get.auth.user.:userId

Request:

{
  userId: string;
}

Response: 200 OK

{
  id: string;
  email: string;
  username: string;
  name?: string;
  roles?: string[];
  isActive: boolean;
  createdAt: string;
  updatedAt: string;
}

Error Codes

  • USER_ALREADY_EXISTS (409): Email or username already registered
  • INVALID_CREDENTIALS (401): Wrong email or password
  • USER_NOT_FOUND (404): User does not exist
  • INVALID_TOKEN (401): Token is invalid or expired
  • TOKEN_EXPIRED (401): Token has expired
  • REFRESH_TOKEN_NOT_FOUND (404): Refresh token not found or revoked
  • WEAK_PASSWORD (400): Password doesn't meet strength requirements
  • INVALID_EMAIL_FORMAT (400): Email format is invalid

Security Features

  • Passwords hashed with bcrypt (configurable salt rounds)
  • JWT tokens with configurable expiration
  • Refresh token rotation (old tokens revoked after refresh)
  • All refresh tokens revoked on password change
  • Automatic cleanup of expired tokens via MongoDB TTL index
  • Email and username normalized to lowercase
  • Strong password validation

Database Schema

Users Collection

{
  _id: string;              // UUID
  email: string;            // Unique, lowercase
  username: string;         // Unique, lowercase
  passwordHash: string;     // Bcrypt hash
  name?: string;
  roles?: string[];
  isActive: boolean;
  createdAt: string;        // ISO date-time
  updatedAt: string;        // ISO date-time
  metadata?: object;
}

Indexes:

  • email: unique
  • username: unique

Refresh Tokens Collection

{
  _id: string;              // UUID
  token: string;            // UUID
  userId: string;           // Reference to user
  expiresAt: Date;
  createdAt: Date;
  isRevoked: boolean;
}

Indexes:

  • token: unique
  • userId: non-unique
  • expiresAt: TTL index (auto-delete expired tokens)

Development

Project Structure

fruster-auth-service/
├── app.ts                  # Entry point
├── config.ts              # Configuration
├── auth-service.ts        # Service initialization
├── lib/
│   ├── errors.ts          # Custom error definitions
│   ├── models/            # TypeScript interfaces
│   │   ├── User.ts
│   │   └── Token.ts
│   ├── repositories/      # Data access layer
│   │   └── UserRepository.ts
│   ├── managers/          # Business logic
│   │   ├── AuthManager.ts
│   │   └── TokenManager.ts
│   └── handlers/          # Message handlers
│       ├── RegisterUserHandler.ts
│       ├── LoginHandler.ts
│       ├── ValidateTokenHandler.ts
│       ├── RefreshTokenHandler.ts
│       ├── ChangePasswordHandler.ts
│       └── GetUserByIdHandler.ts
└── spec/                  # Tests
    ├── helpers/
    │   └── jasmine.ts
    ├── RegisterUserHandler.spec.ts
    ├── LoginHandler.spec.ts
    ├── ValidateTokenHandler.spec.ts
    ├── RefreshTokenHandler.spec.ts
    └── ChangePasswordHandler.spec.ts

Adding New Handlers

  1. Create handler file in lib/handlers/
  2. Define request/response interfaces with JSDoc
  3. Use @injectable() and @subscribe() decorators
  4. Inject dependencies with @inject()
  5. Register handler in auth-service.ts
  6. Write tests in spec/

Code Style

# Lint code
npm run lint

# Auto-fix linting issues
npm run lint:fix

License

ISC

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •