← Back to DevBytes

SWC Authentication: JWT, Sessions, and OAuth Integration

SWC Authentication: JWT, Sessions, and OAuth Integration

Authentication is one of the most critical components of any modern web application. When building applications with SWC (Speedy Web Compiler) as your TypeScript/JavaScript transpilation layer, you gain significant performance benefits during development and build times. However, authentication logic itself runs at runtime, and implementing it correctly requires a solid understanding of JWT, session-based auth, and OAuth flows. This tutorial walks you through all three approaches in a single, cohesive application.

What Is SWC Authentication?

SWC is an extensible Rust-based platform for compilation, bundling, and minification. While SWC itself is a build-time tool, "SWC authentication" refers to the patterns and practices for implementing authentication in applications that rely on SWC for transpilation — typically Node.js backends, Next.js applications, or custom server setups. The authentication layer operates independently of the compiler, but leveraging SWC's fast transforms allows you to write modern TypeScript with decorators, async/await, and latest ECMAScript features without build-time penalties.

In this tutorial, we will build an Express.js API server — transpiled with SWC via @swc-node/register or @swc/core — that supports three authentication strategies: stateless JWT tokens, server-side sessions, and third-party OAuth integration.

Why It Matters

Project Setup

Let's start by setting up a Node.js project with SWC as the TypeScript compiler. Create a new directory and initialize the project:

mkdir swc-auth-tutorial
cd swc-auth-tutorial
npm init -y
npm install express cors cookie-parser dotenv
npm install jsonwebtoken express-session bcryptjs
npm install passport passport-google-oauth20 passport-github2
npm install -D @swc/core @swc-node/register typescript @types/express @types/node @types/jsonwebtoken @types/express-session @types/cookie-parser @types/bcryptjs @types/passport @types/passport-google-oauth20 @types/passport-github2

Create a .swcrc configuration file at the project root:

{
  "$schema": "https://swc.rs/schema.json",
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "decorators": true,
      "dynamicImport": true
    },
    "transform": {
      "legacyDecorator": true
    },
    "target": "es2020",
    "loose": false
  },
  "module": {
    "type": "commonjs"
  },
  "minify": false
}

Create a tsconfig.json for type checking (SWC handles transpilation, TypeScript handles types):

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "types": ["node"]
  },
  "include": ["src/**/*"]
}

Add scripts to your package.json:

{
  "scripts": {
    "dev": "node -r @swc-node/register src/index.ts",
    "build": "swc src -d dist",
    "start": "node dist/index.js",
    "typecheck": "tsc --noEmit"
  }
}

Application Structure

Our project will follow this directory structure:

src/
├── index.ts
├── config.ts
├── middleware/
│   ├── auth.ts
│   └── error.ts
├── strategies/
│   ├── jwt.strategy.ts
│   ├── session.strategy.ts
│   └── oauth.strategy.ts
├── routes/
│   ├── auth.routes.ts
│   └── protected.routes.ts
└── types/
    └── express.d.ts

Configuration and Environment

Create src/config.ts to centralize configuration:

import dotenv from 'dotenv';

dotenv.config();

export const config = {
  port: process.env.PORT || 3000,
  nodeEnv: process.env.NODE_ENV || 'development',

  // JWT configuration
  jwt: {
    secret: process.env.JWT_SECRET || 'change-this-in-production',
    accessExpiry: '15m',
    refreshExpiry: '7d',
    refreshSecret: process.env.JWT_REFRESH_SECRET || 'refresh-secret-change-me',
    issuer: 'swc-auth-tutorial',
    audience: 'swc-auth-users',
  },

  // Session configuration
  session: {
    secret: process.env.SESSION_SECRET || 'session-secret-change-me',
    name: 'swc.sid',
    maxAge: 1000 * 60 * 60 * 24, // 24 hours
    secure: process.env.NODE_ENV === 'production',
  },

  // OAuth configuration
  oauth: {
    google: {
      clientID: process.env.GOOGLE_CLIENT_ID || '',
      clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
      callbackURL: '/auth/google/callback',
    },
    github: {
      clientID: process.env.GITHUB_CLIENT_ID || '',
      clientSecret: process.env.GITHUB_CLIENT_SECRET || '',
      callbackURL: '/auth/github/callback',
    },
  },

  // CORS
  cors: {
    origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
    credentials: true,
  },
} as const;

Create a .env file (do not commit this to version control):

PORT=3000
NODE_ENV=development
JWT_SECRET=your-super-secret-jwt-key-here
JWT_REFRESH_SECRET=your-super-secret-refresh-key-here
SESSION_SECRET=your-session-secret-here
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
CORS_ORIGIN=http://localhost:5173

Extending Express Types

Create src/types/express.d.ts to augment Express with our custom user type:

import { Express } from 'express';
import { Session } from 'express-session';

declare module 'express' {
  interface Request {
    user?: AuthUser;
  }
}

declare module 'express-session' {
  interface Session {
    userId?: string;
    user?: AuthUser;
    oauthState?: string;
  }
}

export interface AuthUser {
  id: string;
  email: string;
  name: string;
  provider: 'local' | 'google' | 'github';
  providerId?: string;
  role: 'user' | 'admin';
}

export interface JwtPayload {
  sub: string;
  email: string;
  name: string;
  role: 'user' | 'admin';
  iat?: number;
  exp?: number;
  iss?: string;
  aud?: string;
}

JWT Authentication Strategy

JWT (JSON Web Tokens) provide a stateless authentication mechanism. The server issues a signed token after login, and the client sends it with each request. The server verifies the token without needing to look up session state in a database or memory store.

Create src/strategies/jwt.strategy.ts:

import jwt, { SignOptions, VerifyOptions } from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import { config } from '../config';
import type { AuthUser, JwtPayload } from '../types/express';

// In a real application, replace this with a database
const usersDB = new Map<string, { passwordHash: string; user: AuthUser }>();

export class JwtAuthService {
  /**
   * Register a new user with email and password
   */
  async register(email: string, password: string, name: string): Promise<AuthUser> {
    const existingUser = Array.from(usersDB.values()).find(
      (entry) => entry.user.email === email
    );
    if (existingUser) {
      throw new Error('User already exists with this email');
    }

    const passwordHash = await bcrypt.hash(password, 12);
    const user: AuthUser = {
      id: crypto.randomUUID(),
      email,
      name,
      provider: 'local',
      role: 'user',
    };

    usersDB.set(user.id, { passwordHash, user });
    return user;
  }

  /**
   * Login with email and password, returns access and refresh tokens
   */
  async login(email: string, password: string): Promise<{
    user: AuthUser;
    accessToken: string;
    refreshToken: string;
  }> {
    const entry = Array.from(usersDB.values()).find(
      (e) => e.user.email === email
    );
    if (!entry) {
      throw new Error('Invalid credentials');
    }

    const valid = await bcrypt.compare(password, entry.passwordHash);
    if (!valid) {
      throw new Error('Invalid credentials');
    }

    const accessToken = this.generateAccessToken(entry.user);
    const refreshToken = this.generateRefreshToken(entry.user);

    return { user: entry.user, accessToken, refreshToken };
  }

  /**
   * Generate a short-lived access token
   */
  generateAccessToken(user: AuthUser): string {
    const payload: Omit<JwtPayload, 'iat' | 'exp'> = {
      sub: user.id,
      email: user.email,
      name: user.name,
      role: user.role,
    };

    const signOptions: SignOptions = {
      expiresIn: config.jwt.accessExpiry,
      issuer: config.jwt.issuer,
      audience: config.jwt.audience,
    };

    return jwt.sign(payload, config.jwt.secret, signOptions);
  }

  /**
   * Generate a long-lived refresh token
   */
  generateRefreshToken(user: AuthUser): string {
    const payload = { sub: user.id, type: 'refresh' };

    const signOptions: SignOptions = {
      expiresIn: config.jwt.refreshExpiry,
      issuer: config.jwt.issuer,
      audience: config.jwt.audience,
    };

    return jwt.sign(payload, config.jwt.refreshSecret, signOptions);
  }

  /**
   * Verify an access token and return the decoded payload
   */
  verifyAccessToken(token: string): JwtPayload {
    const verifyOptions: VerifyOptions = {
      issuer: config.jwt.issuer,
      audience: config.jwt.audience,
    };

    return jwt.verify(token, config.jwt.secret, verifyOptions) as JwtPayload;
  }

  /**
   * Verify a refresh token and issue a new access token
   */
  refreshAccessToken(refreshToken: string): string {
    const verifyOptions: VerifyOptions = {
      issuer: config.jwt.issuer,
      audience: config.jwt.audience,
    };

    const decoded = jwt.verify(
      refreshToken,
      config.jwt.refreshSecret,
      verifyOptions
    ) as { sub: string; type: string };

    if (decoded.type !== 'refresh') {
      throw new Error('Invalid token type');
    }

    const entry = usersDB.get(decoded.sub);
    if (!entry) {
      throw new Error('User not found');
    }

    return this.generateAccessToken(entry.user);
  }

  /**
   * Find or create a user from OAuth provider data
   */
  findOrCreateUser(oauthUser: Partial<AuthUser>): AuthUser {
    const existing = Array.from(usersDB.values()).find(
      (e) =>
        e.user.provider === oauthUser.provider &&
        e.user.providerId === oauthUser.providerId
    );

    if (existing) {
      return existing.user;
    }

    const user: AuthUser = {
      id: crypto.randomUUID(),
      email: oauthUser.email || '',
      name: oauthUser.name || '',
      provider: oauthUser.provider || 'local',
      providerId: oauthUser.providerId,
      role: 'user',
    };

    usersDB.set(user.id, { passwordHash: '', user });
    return user;
  }
}

export const jwtAuthService = new JwtAuthService();

Session-Based Authentication Strategy

Session-based authentication stores user state on the server (in memory, Redis, or a database) and sends a session ID to the client via a cookie. This is the traditional approach and works well for server-rendered applications.

Create src/strategies/session.strategy.ts:

import bcrypt from 'bcryptjs';
import { config } from '../config';
import type { AuthUser } from '../types/express';
import { jwtAuthService } from './jwt.strategy';

// Shared in-memory user store (in production, use a real database)
// We reuse the same store from JwtAuthService for simplicity

export class SessionAuthService {
  /**
   * Authenticate a user and store their info in the session
   */
  async loginWithSession(
    email: string,
    password: string,
    session: Express.Session
  ): Promise<AuthUser> {
    // Reuse the JWT service's user lookup
    const result = await jwtAuthService.login(email, password);
    
    // Store minimal user info in the session
    session.userId = result.user.id;
    session.user = result.user;

    return result.user;
  }

  /**
   * Create a session for an OAuth-authenticated user
   */
  createSessionForOAuthUser(user: AuthUser, session: Express.Session): void {
    session.userId = user.id;
    session.user = user;
  }

  /**
   * Get the current user from the session
   */
  getCurrentUser(session: Express.Session): AuthUser | null {
    if (!session.userId || !session.user) {
      return null;
    }
    return session.user;
  }

  /**
   * Destroy the session (logout)
   */
  destroySession(session: Express.Session): Promise<void> {
    return new Promise((resolve, reject) => {
      session.destroy((err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  }

  /**
   * Register a new user (delegates to JWT service for user creation)
   */
  async register(email: string, password: string, name: string): Promise<AuthUser> {
    return jwtAuthService.register(email, password, name);
  }
}

export const sessionAuthService = new SessionAuthService();

OAuth Integration with Passport

OAuth allows users to authenticate using third-party providers like Google and GitHub. We use Passport.js strategies to handle the OAuth flow. After successful authentication, we can either create a JWT token or establish a server-side session.

Create src/strategies/oauth.strategy.ts:

import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
import { Strategy as GitHubStrategy } from 'passport-github2';
import { config } from '../config';
import { jwtAuthService } from './jwt.strategy';
import type { AuthUser } from '../types/express';

/**
 * Configure Passport with Google and GitHub OAuth strategies
 */
export function configureOAuthStrategies(): void {
  // Serialize user for session storage
  passport.serializeUser((user: AuthUser, done) => {
    done(null, user.id);
  });

  // Deserialize user from session
  passport.deserializeUser((id: string, done) => {
    // In production, look up user from database by ID
    // For this tutorial, we store the full user in the session
    done(null, null);
  });

  // Google OAuth Strategy
  if (config.oauth.google.clientID && config.oauth.google.clientSecret) {
    passport.use(
      new GoogleStrategy(
        {
          clientID: config.oauth.google.clientID,
          clientSecret: config.oauth.google.clientSecret,
          callbackURL: config.oauth.google.callbackURL,
        },
        async (accessToken, refreshToken, profile, done) => {
          try {
            const user = jwtAuthService.findOrCreateUser({
              email: profile.emails?.[0]?.value || '',
              name: profile.displayName || profile.username || '',
              provider: 'google',
              providerId: profile.id,
            });
            return done(null, user);
          } catch (error) {
            return done(error as Error, undefined);
          }
        }
      )
    );
  }

  // GitHub OAuth Strategy
  if (config.oauth.github.clientID && config.oauth.oauth.github.clientSecret) {
    passport.use(
      new GitHubStrategy(
        {
          clientID: config.oauth.github.clientID,
          clientSecret: config.oauth.github.clientSecret,
          callbackURL: config.oauth.github.callbackURL,
          scope: ['user:email'],
        },
        async (accessToken: string, refreshToken: string, profile: any, done: any) => {
          try {
            const user = jwtAuthService.findOrCreateUser({
              email: profile.emails?.[0]?.value || '',
              name: profile.displayName || profile.username || '',
              provider: 'github',
              providerId: profile.id,
            });
            return done(null, user);
          } catch (error) {
            return done(error as Error, undefined);
          }
        }
      )
    );
  }
}

export { passport };

Authentication Middleware

Create src/middleware/auth.ts to protect routes with either JWT or session authentication:

import { Request, Response, NextFunction } from 'express';
import { jwtAuthService } from '../strategies/jwt.strategy';
import { sessionAuthService } from '../strategies/session.strategy';
import type { AuthUser } from '../types/express';

/**
 * Extract bearer token from Authorization header
 */
function extractBearerToken(req: Request): string | null {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return null;
  }
  return authHeader.substring(7);
}

/**
 * Middleware that requires JWT authentication
 */
export function requireJwt(req: Request, res: Response, next: NextFunction): void {
  const token = extractBearerToken(req);
  if (!token) {
    res.status(401).json({ error: 'No authentication token provided' });
    return;
  }

  try {
    const payload = jwtAuthService.verifyAccessToken(token);
    req.user = {
      id: payload.sub,
      email: payload.email,
      name: payload.name,
      provider: 'local',
      role: payload.role,
    };
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid or expired token' });
  }
}

/**
 * Middleware that requires an active session
 */
export function requireSession(req: Request, res: Response, next: NextFunction): void {
  const user = sessionAuthService.getCurrentUser(req.session);
  if (!user) {
    res.status(401).json({ error: 'No active session' });
    return;
  }
  req.user = user;
  next();
}

/**
 * Middleware that accepts either JWT or session authentication
 */
export function requireAuth(req: Request, res: Response, next: NextFunction): void {
  // Try JWT first
  const token = extractBearerToken(req);
  if (token) {
    try {
      const payload = jwtAuthService.verifyAccessToken(token);
      req.user = {
        id: payload.sub,
        email: payload.email,
        name: payload.name,
        provider: 'local',
        role: payload.role,
      };
      next();
      return;
    } catch {
      // Token invalid, fall through to session check
    }
  }

  // Try session
  const user = sessionAuthService.getCurrentUser(req.session);
  if (user) {
    req.user = user;
    next();
    return;
  }

  res.status(401).json({ error: 'Authentication required' });
}

/**
 * Middleware that requires admin role
 */
export function requireAdmin(req: Request, res: Response, next: NextFunction): void {
  if (!req.user) {
    res.status(401).json({ error: 'Authentication required' });
    return;
  }
  if (req.user.role !== 'admin') {
    res.status(403).json({ error: 'Admin access required' });
    return;
  }
  next();
}

Create src/middleware/error.ts for centralized error handling:

import { Request, Response, NextFunction } from 'express';

export function errorHandler(
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
): void {
  console.error(`[ERROR] ${err.message}`, err.stack);

  if (err.message.includes('Invalid credentials') || err.message.includes('already exists')) {
    res.status(400).json({ error: err.message });
    return;
  }

  res.status(500).json({ error: 'Internal server error' });
}

Auth Routes

Create src/routes/auth.routes.ts with endpoints for all three authentication strategies:

import { Router, Request, Response, NextFunction } from 'express';
import { jwtAuthService } from '../strategies/jwt.strategy';
import { sessionAuthService } from '../strategies/session.strategy';
import { passport } from '../strategies/oauth.strategy';
import { requireAuth, requireAdmin } from '../middleware/auth';

const router = Router();

// ============================================
// JWT Authentication Routes
// ============================================

router.post('/jwt/register', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { email, password, name } = req.body;
    if (!email || !password || !name) {
      res.status(400).json({ error: 'email, password, and name are required' });
      return;
    }
    const user = await jwtAuthService.register(email, password, name);
    res.status(201).json({ message: 'User registered', user: { id: user.id, email: user.email, name: user.name } });
  } catch (err) {
    next(err);
  }
});

router.post('/jwt/login', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { email, password } = req.body;
    if (!email || !password) {
      res.status(400).json({ error: 'email and password are required' });
      return;
    }
    const { user, accessToken, refreshToken } = await jwtAuthService.login(email, password);
    res.json({
      message: 'Login successful',
      user: { id: user.id, email: user.email, name: user.name, role: user.role },
      accessToken,
      refreshToken,
    });
  } catch (err) {
    next(err);
  }
});

router.post('/jwt/refresh', (req: Request, res: Response, next: NextFunction) => {
  try {
    const { refreshToken } = req.body;
    if (!refreshToken) {
      res.status(400).json({ error: 'refreshToken is required' });
      return;
    }
    const accessToken = jwtAuthService.refreshAccessToken(refreshToken);
    res.json({ accessToken });
  } catch (err) {
    res.status(401).json({ error: 'Invalid refresh token' });
  }
});

// ============================================
// Session Authentication Routes
// ============================================

router.post('/session/register', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { email, password, name } = req.body;
    if (!email || !password || !name) {
      res.status(400).json({ error: 'email, password, and name are required' });
      return;
    }
    const user = await sessionAuthService.register(email, password, name);
    res.status(201).json({ message: 'User registered', user: { id: user.id, email: user.email, name: user.name } });
  } catch (err) {
    next(err);
  }
});

router.post('/session/login', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { email, password } = req.body;
    if (!email || !password) {
      res.status(400).json({ error: 'email and password are required' });
      return;
    }
    const user = await sessionAuthService.loginWithSession(email, password, req.session);
    res.json({ message: 'Login successful', user: { id: user.id, email: user.email, name: user.name, role: user.role } });
  } catch (err) {
    next(err);
  }
});

router.post('/session/logout', (req: Request, res: Response, next: NextFunction) => {
  sessionAuthService
    .destroySession(req.session)
    .then(() => {
      res.clearCookie('swc.sid');
      res.json({ message: 'Logged out successfully' });
    })
    .catch(next);
});

router.get('/session/me', (req: Request, res: Response) => {
  const user = sessionAuthService.getCurrentUser(req.session);
  if (!user) {
    res.status(401).json({ error: 'Not authenticated' });
    return;
  }
  res.json({ user });
});

// ============================================
// OAuth Routes (Google)
// ============================================

router.get('/google', passport.authenticate('google', { scope: ['profile', 'email'] }));

router.get(
  '/google/callback',
  passport.authenticate('google', { session: false, failureRedirect: '/login?error=oauth_failed' }),
  (req: Request, res: Response) => {
    const user = req.user as any;
    if (!user) {
      res.redirect('/login?error=no_user');
      return;
    }

    // Option A: Create a session for the OAuth user
    sessionAuthService.createSessionForOAuthUser(user, req.session);

    // Option B: Issue JWT tokens for API access
    const accessToken = jwtAuthService.generateAccessToken(user);
    const refreshToken = jwtAuthService.generateRefreshToken(user);

    // Redirect with tokens (in production, use secure httpOnly cookies)
    res.redirect(`/auth/success?token=${accessToken}&refresh=${refreshToken}`);
  }
);

// ============================================
// OAuth Routes (GitHub)
// ============================================

router.get('/github', passport.authenticate('github', { scope: ['user:email'] }));

router.get(
  '/github/callback',
  passport.authenticate('github', { session: false, failureRedirect: '/login?error=oauth_failed' }),
  (req: Request, res: Response) => {
    const user = req.user as any;
    if (!user) {
      res.redirect('/login?error=no_user');
      return;
    }

    sessionAuthService.createSessionForOAuthUser(user, req.session);
    const accessToken = jwtAuthService.generateAccessToken(user);
    const refreshToken = jwtAuthService.generateRefreshToken(user);

    res.redirect(`/auth/success?token=${accessToken}&refresh=${refreshToken}`);
  }
);

// ============================================
// Shared Protected Route
// ============================================

router.get('/me', requireAuth, (req: Request, res: Response) => {
  res.json({ user: req.user });
});

export default router;

Protected Routes

Create src/routes/protected.routes.ts to demonstrate protected endpoints:

import { Router, Request, Response } from 'express';
import { requireJwt, requireSession, requireAuth, requireAdmin } from '../middleware/auth';

const router = Router();

// JWT-only protected route (for API consumers, mobile apps, etc.)
router.get('/api/data', requireJwt, (req: Request, res: Response) => {
  res.json({
    message: 'This data is protected by JWT authentication',
    user: req.user,
    timestamp: new Date().toISOString(),
  });
});

// Session-only protected route (for browser-based web apps)
router.get('/dashboard', requireSession, (req: Request, res: Response) => {
  res.json({
    message: 'Welcome to your dashboard (session-protected)',
    user: req.user,
    timestamp: new Date().toISOString(),
  });
});

// Route accessible via either JWT or session
router.get('/profile', requireAuth, (req: Request, res: Response) => {
  res.json({
    message: 'Your profile (accessible via JWT or session)',
    user: req.user,
  });
});

// Admin-only route
router.get('/admin', requireAuth, requireAdmin, (req: Request, res: Response) => {
  res.json({
    message: 'Admin panel access granted',
    user: req.user,
  });
});

export default router;

Wiring It All Together

Create src/index.ts to bootstrap the application:

import express from 'express';
import cors from 'cors';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import passport from 'passport';
import { config } from './config';
import { configureOAuthStrategies } from './strategies/oauth.strategy';
import authRoutes from './routes/auth.routes';
import protectedRoutes from './routes/protected.routes';
import { errorHandler } from './middleware/error';

const app = express();

// CORS
app.use(
  cors({
    origin: config.cors.origin,
    credentials: true,
  })
);

// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Cookies
app.use(cookieParser());

// Session middleware
app.use(
  session({
    name: config.session.name,
    secret: config.session.secret,
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: config.session.secure,
      sameSite: 'lax',
      maxAge: config.session.maxAge,
    },
  })
);

// Passport initialization
configureOAuthStrategies();
app.use(passport.initialize());
app.use(passport.session());

// Routes
app.use('/auth', authRoutes);
app.use('/', protectedRoutes);

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// Error handler (must be last)
app.use(errorHandler);

// Start server
app.listen(config.port, () => {
  console.log(`🚀 Server running on http://localhost:${config.port}`);
  console.log(`📦 Environment: ${config.nodeEnv}`);
  console.log(`🔐 Auth endpoints available at /auth/*`);
});

Running and Testing the Application

Start the development server using SWC's fast transpilation:

npm run dev

You should see output like:

🚀 Server running on http://localhost:3000
📦 Environment: development
🔐 Auth endpoints available at /auth/*

Test the JWT flow with curl:

# Register a new user
curl -X POST http://localhost:3000/auth/jwt/register \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"SecurePass123!","name":"Test User"}'

# Login to get tokens
curl -X POST http://localhost:3000/auth/jwt/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"SecurePass123!"}'

# Access protected route with JWT
curl http://localhost:3000/api/data \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE"

# Refresh token
curl -X POST http://localhost:3000/auth/jwt/refresh \
  -H "Content-Type: application/json" \
  -d '{"refreshToken":"YOUR_REFRESH_TOKEN_HERE"}'

Test the session flow:

# Register
curl -X POST http://localhost:3000/auth/session/register \
  -H "Content-Type: application/json" \
  -d '{"email":"session@example.com","password":"SecurePass123!","name":"Session User"}'

# Login (save cookies)
curl -X POST http://localhost:3000/auth/session/login \
  -H "Content-Type: application/json" \
  -d '{"email":"session@example.com","password":"SecurePass123!"}' \
  -c cookies.txt

# Access session-protected route
curl http://localhost:3000/dashboard \
  -b cookies.txt

# Check current session user
curl http://localhost:3000/auth/session/me \
  -b cookies.txt

# Logout
curl -X POST http://localhost:3000/auth/session/logout \
  -b cookies.txt

Test the OAuth flow by navigating to these URLs in your browser:

# Google OAuth
http://localhost:3000/auth/google

# GitHub OAuth
http://localhost:3000/auth/github

Best Practices

Building for Production with SWC

To compile your TypeScript to JavaScript for production using SWC, install the SWC CLI:

npm install -D @swc/cli

Then build and run:

# Compile TypeScript to JavaScript
npm run build

# Run the compiled output
npm start

— Ad —

Google AdSense will appear here after approval

← Back to all articles