Koa JWT Middleware

4.0.4 · active · verified Wed Apr 22

koa-jwt is a middleware for Koa.js applications designed to authenticate HTTP requests using JSON Web Tokens (JWTs). It parses and validates JWTs typically provided in the `Authorization` header, or optionally from a cookie or a custom `getToken` function. Upon successful validation, the decoded JWT payload is exposed on `ctx.state.user` (by default) for subsequent middleware to use for authorization and access control. The current stable version is 4.0.4. Releases are driven by dependency updates (especially `jsonwebtoken`) and bug fixes, with major versions tied to Node.js support or significant internal changes. It differentiates itself by providing a streamlined, Koa-idiomatic approach to JWT authentication, leveraging Koa's async/await middleware pattern, and integrates well with `koa-unless` for path-based exclusion. It supports single or multiple secrets, including rolling secrets or mixed authentication methods (e.g., Auth0 PEM files and shared secrets).

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to set up `koa-jwt` to protect routes in a Koa application, including token generation, login, public and protected endpoints, and basic error handling.

import Koa from 'koa';
import jwt from 'koa-jwt';
import Router from '@koa/router';
import bodyParser from 'koa-bodyparser';
import { sign } from 'jsonwebtoken';

const app = new Koa();
const router = new Router();

// A secret key for signing and verifying tokens. In a real app, use environment variables.
const SUPER_SECRET_KEY = process.env.JWT_SECRET ?? 'your-super-secret-jwt-key';

app.use(bodyParser());

// Unprotected route for login
router.post('/login', async (ctx) => {
  const { username, password } = ctx.request.body as { username?: string, password?: string };

  if (username === 'test' && password === 'password') {
    // In a real app, you'd fetch user from DB and sign a token with user-specific data.
    const token = sign({ id: 1, username: 'test' }, SUPER_SECRET_KEY, { expiresIn: '1h' });
    ctx.body = { token };
  } else {
    ctx.status = 401;
    ctx.body = { message: 'Invalid credentials' };
  }
});

// JWT middleware protects all routes after this point, except those explicitly excluded.
// For this example, we'll manually exclude /login and /public using .unless() or a custom conditional middleware.
// A more robust solution often involves the 'koa-unless' package.
app.use(async (ctx, next) => {
  if (ctx.path.startsWith('/public') || ctx.path.startsWith('/login')) {
    await next();
  } else {
    return jwt({ secret: SUPER_SECRET_KEY, debug: true })(ctx, next);
  }
});

// Error handling for JWT authentication failures
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err: any) {
    if (401 === err.status) {
      ctx.status = 401;
      ctx.body = {
        error: 'Protected resource, use Authorization header to get access',
        details: err.originalError ? err.originalError.message : err.message
      };
    } else {
      throw err;
    }
  }
});

// Protected route
router.get('/protected', async (ctx) => {
  // ctx.state.user will contain the decoded JWT payload if authentication succeeded
  ctx.body = `Hello, ${(ctx.state as any).user.username}! This is a protected route.`;
});

// Public route
router.get('/public', async (ctx) => {
  ctx.body = 'This is a public route, no token needed.';
});

app.use(router.routes());
app.use(router.allowedMethods());

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
  console.log('Try POST /login with {username: "test", password: "password"} in body');
  console.log('Then GET /protected with "Authorization: Bearer <token>" header');
  console.log('Or GET /public without any token');
});

view raw JSON →