Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ouim.me/llms.txt

Use this file to discover all available pages before exploring further.

Quick Start

The createExpressAuthMiddleware function creates an Express middleware that automatically verifies Logto tokens and attaches user information to the request object.
import express from 'express';
import { createExpressAuthMiddleware } from '@ouim/logto-authkit/server';

const app = express();

const authMiddleware = createExpressAuthMiddleware({
  logtoUrl: 'https://your-tenant.logto.app',
  audience: 'https://api.yourapp.com',
});

// Protected route
app.get('/api/user/profile', authMiddleware, (req, res) => {
  // Access authenticated user info
  const { userId, payload } = req.auth;
  
  res.json({
    userId,
    email: payload.email,
    name: payload.name,
  });
});

app.listen(3000);

Configuration

The middleware accepts a VerifyAuthOptions object:
logtoUrl
string
required
Your Logto server URL
logtoUrl: 'https://your-tenant.logto.app'
audience
string
required
The API resource identifier you registered in Logto
audience: 'https://api.yourapp.com'
Custom cookie name if you changed it in the frontend
cookieName: 'my_custom_token'
requiredScope
string
Require a specific scope to be present in the token
requiredScope: 'read:admin'
allowGuest
boolean
default:"false"
Allow unauthenticated users with guest context
allowGuest: true

Request Object

The middleware adds an auth property to the Express request:
interface ExpressRequest {
  auth?: AuthContext;
  // ... other Express request properties
}

interface AuthContext {
  userId: string | null;          // Logto user ID
  isAuthenticated: boolean;       // true if authenticated
  payload: AuthPayload | null;    // Full JWT claims
  isGuest?: boolean;             // true in guest mode
  guestId?: string;              // UUID for guest users
}

Usage Examples

Basic Protected Route

import { createExpressAuthMiddleware } from '@ouim/logto-authkit/server';

const auth = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL,
  audience: process.env.LOGTO_API_RESOURCE,
});

app.get('/api/protected', auth, (req, res) => {
  res.json({ 
    message: 'This is protected!',
    userId: req.auth.userId 
  });
});

Route with Required Scope

const adminAuth = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL,
  audience: process.env.LOGTO_API_RESOURCE,
  requiredScope: 'admin:write',
});

app.delete('/api/admin/users/:id', adminAuth, (req, res) => {
  // Only users with 'admin:write' scope can access
  res.json({ success: true });
});

Multiple Middleware

Use different authentication requirements for different routes:
const basicAuth = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL,
  audience: process.env.LOGTO_API_RESOURCE,
});

const adminAuth = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL,
  audience: process.env.LOGTO_API_RESOURCE,
  requiredScope: 'admin',
});

// Regular authenticated routes
app.use('/api/user', basicAuth);
app.get('/api/user/profile', (req, res) => { /* ... */ });
app.put('/api/user/settings', (req, res) => { /* ... */ });

// Admin-only routes
app.use('/api/admin', adminAuth);
app.get('/api/admin/users', (req, res) => { /* ... */ });
app.post('/api/admin/settings', (req, res) => { /* ... */ });

Guest Mode

Allow both authenticated and guest users:
const flexibleAuth = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL,
  audience: process.env.LOGTO_API_RESOURCE,
  allowGuest: true,
});

app.get('/api/cart', flexibleAuth, (req, res) => {
  if (req.auth.isAuthenticated) {
    // Load user's saved cart from database
    const cart = await getCartByUserId(req.auth.userId);
    res.json(cart);
  } else {
    // Load guest cart from session
    const cart = await getCartByGuestId(req.auth.guestId);
    res.json(cart);
  }
});

TypeScript Usage

import express, { Request, Response } from 'express';
import { createExpressAuthMiddleware, AuthContext } from '@ouim/logto-authkit/server';

// Extend Express Request type
declare global {
  namespace Express {
    interface Request {
      auth?: AuthContext;
    }
  }
}

const app = express();

const authMiddleware = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL!,
  audience: process.env.LOGTO_API_RESOURCE!,
});

app.get('/api/profile', authMiddleware, (req: Request, res: Response) => {
  // TypeScript knows about req.auth
  const userId = req.auth!.userId;
  res.json({ userId });
});

Error Responses

The middleware returns 401 Unauthorized for authentication failures:

Missing Token

{
  "error": "Authentication required",
  "message": "No token found in cookies or Authorization header"
}

Invalid Token

{
  "error": "Authentication failed",
  "message": "Token verification failed: Token has expired"
}

Missing Scope

{
  "error": "Authentication failed",
  "message": "Token verification failed: Missing required scope: admin:write"
}
When allowGuest: true, the middleware never returns 401 errors. Instead, it sets req.auth with guest context.

How It Works

The middleware performs these steps on each request:
1

Parse Cookies

Automatically parses cookies using cookie-parser if not already available
2

Extract Token

Checks for token in cookies (logto_authtoken) then Authorization header
3

Verify Token

  • Fetches JWKS from Logto server (cached for 5 minutes)
  • Verifies JWT signature using the appropriate public key
  • Validates issuer, audience, expiration, and scopes
4

Set Auth Context

Attaches AuthContext to req.auth and calls next()
5

Handle Errors

Returns 401 JSON response or guest context (if allowGuest enabled)

Best Practices

Store configuration in environment variables:
const auth = createExpressAuthMiddleware({
  logtoUrl: process.env.LOGTO_URL,
  audience: process.env.LOGTO_API_RESOURCE,
});
Use different middleware instances for different authorization levels:
const userAuth = createExpressAuthMiddleware({ ... });
const adminAuth = createExpressAuthMiddleware({ 
  ..., 
  requiredScope: 'admin' 
});
Always check isAuthenticated when using allowGuest:
app.post('/api/checkout', flexibleAuth, (req, res) => {
  if (!req.auth.isAuthenticated) {
    return res.status(401).json({ 
      error: 'Please login to checkout' 
    });
  }
  // Process checkout
});
The full JWT payload is available in req.auth.payload:
app.get('/api/user', auth, (req, res) => {
  const { email, name, picture } = req.auth.payload;
  res.json({ email, name, picture });
});
The middleware automatically handles cookie parsing:
  • If req.cookies exists (already parsed), uses it directly
  • If not, applies cookie-parser middleware internally
  • No need to add cookie-parser to your app separately
// This works even without app.use(cookieParser())
app.get('/api/protected', authMiddleware, (req, res) => {
  res.json({ userId: req.auth.userId });
});
While the middleware handles cookie parsing internally, you can still use cookie-parser globally if needed for other routes.

Next.js Integration

Server-side auth for Next.js

Generic Usage

Use in any Node.js environment