Overview

Flexible authentication system for Vercube applications

The Auth module provides a powerful, flexible authentication system for Vercube applications. Built around a provider-based architecture, it allows you to implement various authentication strategies such as JWT, sessions, OAuth, or custom solutions.

Installation

$ pnpm add @vercube/auth

Quick Start

Create an Auth Provider

Create a custom authentication provider by extending the AuthProvider class:

src/providers/JWTAuthProvider.ts
import { AuthProvider, type AuthTypes } from '@vercube/auth';

interface User {
  id: number;
  username: string;
  roles: string[];
}

export class JWTAuthProvider extends AuthProvider<User> {
  
  public validate(request: Request, params?: AuthTypes.MiddlewareOptions): string | null {
    const token = request.headers.get('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      return 'No token provided';
    }

    try {
      const user = this.verifyToken(token);
      
      // Check roles if specified
      if (params?.roles && params.roles.length > 0) {
        const hasRequiredRole = params.roles.some(role => user.roles.includes(role));
        if (!hasRequiredRole) {
          return 'Insufficient permissions';
        }
      }
      
      return null; // Authentication successful
    } catch {
      return 'Invalid token';
    }
  }

  public getCurrentUser(request: Request): User | null {
    const token = request.headers.get('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      return null;
    }

    try {
      return this.verifyToken(token);
    } catch {
      return null;
    }
  }

  private verifyToken(token: string): User {
    // Your JWT verification logic here
    // This is a simplified example
    return { id: 1, username: 'john', roles: ['user'] };
  }
}

Register Provider in Container

Register your auth provider in the DI container during application setup:

src/setup.ts
import { type App } from '@vercube/core';
import { AuthProvider } from '@vercube/auth';
import { JWTAuthProvider } from './providers/JWTAuthProvider';

export function setup(app: App): void {
  app.container.bind(AuthProvider, JWTAuthProvider);
}

Protect Your Endpoints

Use the @Auth decorator to protect controller methods:

src/controllers/ProfileController.ts
import { Controller, Get } from '@vercube/core';
import { Auth, User } from '@vercube/auth';

interface User {
  id: number;
  username: string;
  roles: string[];
}

@Controller('/profile')
export class ProfileController {
  
  @Get('/')
  @Auth()
  public getProfile(@User() user: User) {
    return { profile: user };
  }
  
  @Get('/admin')
  @Auth({ roles: ['admin'] })
  public getAdminPanel(@User() user: User) {
    return { admin: true, user };
  }
}

Core Concepts

AuthProvider

The AuthProvider is an abstract class that defines the interface for authentication implementations. All authentication providers must extend this class and implement two methods:

  • validate() - Validates incoming requests and returns null on success or an error message string on failure
  • getCurrentUser() - Returns the authenticated user object or null if not authenticated

Decorators

The Auth module provides two decorators for easy integration with controllers:

DecoratorDescription
@Auth()Protects a method, requiring authentication before execution
@User()Injects the current authenticated user as a method parameter

Role-Based Access Control

You can restrict access based on user roles by passing options to the @Auth decorator:

@Auth({ roles: ['admin', 'moderator'] })
public adminOnly(@User() user: User) {
  // Only accessible by admins and moderators
}

The validate() method in your provider receives these options and should check if the user has the required roles.

Authentication Flow

When a request hits a protected endpoint:

  1. The @Auth decorator triggers the authentication middleware
  2. Your AuthProvider.validate() method is called with the request
  3. If validate() returns null, authentication succeeds
  4. If validate() returns a string, authentication fails with that error message
  5. The @User decorator calls getCurrentUser() to inject the user object

Common Patterns

import { AuthProvider, type AuthTypes } from '@vercube/auth';
import jwt from 'jsonwebtoken';

export class JWTAuthProvider extends AuthProvider<User> {
  private secret = process.env.JWT_SECRET!;
  
  public validate(request: Request, params?: AuthTypes.MiddlewareOptions): string | null {
    const token = request.headers.get('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      return 'Authorization header required';
    }

    try {
      const decoded = jwt.verify(token, this.secret) as User;
      
      if (params?.roles?.length) {
        if (!params.roles.some(role => decoded.roles.includes(role))) {
          return 'Insufficient permissions';
        }
      }
      
      return null;
    } catch (error) {
      if (error instanceof jwt.TokenExpiredError) {
        return 'Token expired';
      }
      return 'Invalid token';
    }
  }

  public getCurrentUser(request: Request): User | null {
    const token = request.headers.get('Authorization')?.replace('Bearer ', '');
    if (!token) return null;
    
    try {
      return jwt.verify(token, this.secret) as User;
    } catch {
      return null;
    }
  }
}
Previous

Decorators

Authentication decorators for protecting endpoints and accessing user data

Next