🎉 RamAPI v1.0 is now available! Read the Getting Started Guide
Documentation
API Reference
Auth Utilities

Auth API Reference

Complete API reference for RamAPI's authentication utilities.

Table of Contents

  1. JWTService
  2. JWTConfig
  3. JWTPayload
  4. authenticate()
  5. optionalAuthenticate()
  6. PasswordService
  7. Examples

JWTService

Service class for JWT token operations.

Constructor

new JWTService(config: JWTConfig)

Parameters:

ParameterTypeRequiredDescription
configJWTConfigYesJWT configuration

Example:

import { JWTService } from 'ramapi';
 
const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
  expiresIn: 86400, // 24 hours in seconds
  algorithm: 'HS256',
});

sign()

Sign a JWT token with payload.

sign(payload: JWTPayload): string

Parameters:

ParameterTypeRequiredDescription
payloadJWTPayloadYesToken payload (must include sub)

Returns: string - Signed JWT token

Example:

const token = jwtService.sign({
  sub: 'user-123',
  email: 'user@example.com',
  role: 'admin',
});
 
console.log(token);
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTEyMyIsImVtYWlsIjoidXNl...

verify()

Verify and decode a JWT token.

verify(token: string): JWTPayload

Parameters:

ParameterTypeRequiredDescription
tokenstringYesJWT token to verify

Returns: JWTPayload - Decoded and verified payload

Throws:

  • HTTPError(401, 'Token expired') - If token has expired
  • HTTPError(401, 'Invalid token') - If token is malformed or signature is invalid

Example:

try {
  const payload = jwtService.verify(token);
  console.log('User ID:', payload.sub);
  console.log('Email:', payload.email);
} catch (error) {
  if (error.statusCode === 401) {
    console.error('Authentication failed:', error.message);
  }
}

decode()

Decode token without verification (unsafe - use with caution).

decode(token: string): JWTPayload | null

Parameters:

ParameterTypeRequiredDescription
tokenstringYesJWT token to decode

Returns: JWTPayload | null - Decoded payload (not verified) or null if invalid

Warning: This method does NOT verify the token signature. Only use for debugging or when verification is not required.

Example:

const payload = jwtService.decode(token);
 
if (payload) {
  console.log('Token contains:', payload);
  // Don't trust this data - it's not verified!
}

JWTConfig

Configuration interface for JWT service.

interface JWTConfig {
  secret: string;
  expiresIn?: number;
  algorithm?: jwt.Algorithm;
  issuer?: string;
  audience?: string;
}

Properties

PropertyTypeRequiredDefaultDescription
secretstringYes-Secret key for signing/verifying tokens
expiresInnumberNoundefinedToken expiration time in seconds
algorithmjwt.AlgorithmNo'HS256'Signing algorithm
issuerstringNoundefinedToken issuer (iss claim)
audiencestringNoundefinedToken audience (aud claim)

Algorithms

Supported algorithms (from jsonwebtoken):

Symmetric:

  • HS256 (HMAC SHA-256) - Default
  • HS384 (HMAC SHA-384)
  • HS512 (HMAC SHA-512)

Asymmetric:

  • RS256 (RSA SHA-256)
  • RS384 (RSA SHA-384)
  • RS512 (RSA SHA-512)
  • ES256 (ECDSA SHA-256)
  • ES384 (ECDSA SHA-384)
  • ES512 (ECDSA SHA-512)

Examples

Basic configuration:

const config: JWTConfig = {
  secret: process.env.JWT_SECRET!,
  expiresIn: 86400, // 24 hours
};

With issuer and audience:

const config: JWTConfig = {
  secret: process.env.JWT_SECRET!,
  expiresIn: 3600, // 1 hour
  issuer: 'my-app',
  audience: 'api.example.com',
};

With RSA (asymmetric):

import fs from 'fs';
 
const config: JWTConfig = {
  secret: fs.readFileSync('private.key', 'utf8'),
  algorithm: 'RS256',
  expiresIn: 86400,
};

JWTPayload

JWT payload structure.

interface JWTPayload {
  sub: string; // Subject (user ID) - REQUIRED
  [key: string]: unknown; // Additional custom claims
}

Standard Claims

ClaimTypeRequiredDescription
substringYesSubject (typically user ID)
iatnumberAutoIssued at (timestamp)
expnumberAutoExpiration (timestamp)
issstringOptionalIssuer
audstringOptionalAudience

Custom Claims

Add any custom data to the payload:

const payload: JWTPayload = {
  sub: 'user-123',
  email: 'user@example.com',
  role: 'admin',
  permissions: ['read', 'write', 'delete'],
  organizationId: 'org-456',
};

authenticate()

Middleware factory for required JWT authentication.

function authenticate(jwtService: JWTService): Middleware

Parameters:

ParameterTypeRequiredDescription
jwtServiceJWTServiceYesJWT service instance

Returns: Middleware - Authentication middleware

Behavior:

  1. Extracts token from Authorization: Bearer \<token\> header
  2. Verifies token using JWT service
  3. Sets ctx.user to decoded payload
  4. Sets ctx.state.userId to payload.sub
  5. Throws HTTPError(401) if:
    • Authorization header is missing
    • Header format is invalid (not "Bearer <token>")
    • Token verification fails

Example:

import { JWTService, authenticate } from 'ramapi';
 
const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
});
 
const auth = authenticate(jwtService);
 
// Protected route
app.get('/profile', auth, async (ctx) => {
  console.log('Authenticated user:', ctx.user);
  // { sub: 'user-123', email: 'user@example.com', ... }
 
  const userId = ctx.state.userId; // 'user-123'
  const user = await db.getUser(userId);
 
  ctx.json({ profile: user });
});
 
// Multiple protected routes
app.get('/orders', auth, getOrders);
app.post('/orders', auth, createOrder);
app.get('/settings', auth, getSettings);

Error Responses:

// Missing header
{
  "error": true,
  "message": "Authorization header missing"
}
 
// Invalid format
{
  "error": true,
  "message": "Invalid authorization header format. Expected: Bearer \<token\>"
}
 
// Invalid/expired token
{
  "error": true,
  "message": "Token expired"
}

optionalAuthenticate()

Middleware factory for optional JWT authentication.

function optionalAuthenticate(jwtService: JWTService): Middleware

Parameters:

ParameterTypeRequiredDescription
jwtServiceJWTServiceYesJWT service instance

Returns: Middleware - Optional authentication middleware

Behavior:

  1. Checks for Authorization: Bearer \<token\> header
  2. If present and valid:
    • Sets ctx.user to decoded payload
    • Sets ctx.state.userId to payload.sub
  3. If missing or invalid:
    • Continues without error
    • ctx.user remains undefined

Example:

import { JWTService, optionalAuthenticate } from 'ramapi';
 
const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
});
 
const optionalAuth = optionalAuthenticate(jwtService);
 
// Public endpoint with optional auth
app.get('/posts', optionalAuth, async (ctx) => {
  if (ctx.user) {
    // Authenticated - show user-specific content
    const posts = await db.getPostsForUser(ctx.user.sub);
    ctx.json({ posts, authenticated: true });
  } else {
    // Anonymous - show public content only
    const posts = await db.getPublicPosts();
    ctx.json({ posts, authenticated: false });
  }
});

PasswordService

Service class for password hashing and verification using bcrypt.

Constructor

new PasswordService(saltRounds?: number)

Parameters:

ParameterTypeRequiredDefaultDescription
saltRoundsnumberNo10Number of bcrypt salt rounds

Salt Rounds Guide:

RoundsTimeSecurity
10~100msGood (default)
12~400msBetter
14~1.6sBest

Example:

import { PasswordService } from 'ramapi';
 
const passwordService = new PasswordService(12); // Higher security

hash()

Hash a password using bcrypt.

async hash(password: string): Promise\<string\>

Parameters:

ParameterTypeRequiredDescription
passwordstringYesPlain text password

Returns: Promise\<string\> - Hashed password

Example:

const hashedPassword = await passwordService.hash('myPassword123');
console.log(hashedPassword);
// $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy

verify()

Verify a password against a hash.

async verify(password: string, hash: string): Promise\<boolean\>

Parameters:

ParameterTypeRequiredDescription
passwordstringYesPlain text password
hashstringYesHashed password to compare against

Returns: Promise\<boolean\> - true if password matches, false otherwise

Example:

const isValid = await passwordService.verify('myPassword123', hashedPassword);
 
if (isValid) {
  console.log('Password correct!');
} else {
  console.log('Password incorrect!');
}

needsRehash()

Check if a hash needs to be rehashed (e.g., salt rounds changed).

needsRehash(hash: string): boolean

Parameters:

ParameterTypeRequiredDescription
hashstringYesHashed password

Returns: boolean - true if hash should be regenerated

Example:

const hash = '$2a$10$...'; // Hash with 10 rounds
 
const service = new PasswordService(12); // Now using 12 rounds
 
if (service.needsRehash(hash)) {
  // Rehash with new salt rounds
  const newHash = await service.hash(password);
  await db.updateUserPassword(userId, newHash);
}

passwordService (default instance)

Pre-configured global instance with default settings (10 salt rounds).

import { passwordService } from 'ramapi';
 
// Hash
const hash = await passwordService.hash(password);
 
// Verify
const valid = await passwordService.verify(password, hash);

Examples

Complete Auth Flow

import { createApp, JWTService, passwordService, validate } from 'ramapi';
import { z } from 'zod';
 
const app = createApp();
 
// JWT Service
const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
  expiresIn: 86400, // 24 hours
});
 
// Register
app.post('/auth/register',
  validate({
    body: z.object({
      email: z.string().email(),
      password: z.string().min(8),
      name: z.string().min(2),
    }),
  }),
  async (ctx) => {
    const { email, password, name } = ctx.body;
 
    // Check if user exists
    const exists = await db.getUserByEmail(email);
    if (exists) {
      ctx.json({ error: 'Email already registered' }, 409);
      return;
    }
 
    // Hash password
    const hashedPassword = await passwordService.hash(password);
 
    // Create user
    const user = await db.createUser({
      email,
      password: hashedPassword,
      name,
    });
 
    // Generate token
    const token = jwtService.sign({
      sub: user.id,
      email: user.email,
    });
 
    ctx.json({ user: { id: user.id, email, name }, token }, 201);
  }
);
 
// Login
app.post('/auth/login',
  validate({
    body: z.object({
      email: z.string().email(),
      password: z.string(),
    }),
  }),
  async (ctx) => {
    const { email, password } = ctx.body;
 
    // Get user
    const user = await db.getUserByEmail(email);
    if (!user) {
      ctx.json({ error: 'Invalid credentials' }, 401);
      return;
    }
 
    // Verify password
    const valid = await passwordService.verify(password, user.password);
    if (!valid) {
      ctx.json({ error: 'Invalid credentials' }, 401);
      return;
    }
 
    // Generate token
    const token = jwtService.sign({
      sub: user.id,
      email: user.email,
    });
 
    ctx.json({ user: { id: user.id, email: user.email, name: user.name }, token });
  }
);
 
// Protected routes
const auth = authenticate(jwtService);
 
app.get('/profile', auth, async (ctx) => {
  const user = await db.getUser(ctx.user.sub);
  ctx.json({ user });
});

See Also


Need help? Check the Authentication Documentation or GitHub Issues (opens in a new tab).