@dax-side/jwt-abstraction

JWT auth without the boilerplate.

Create access and refresh tokens, verify payloads, and protect routes in three lines. You still provide the secrets.

npm install @dax-side/jwt-abstraction
npm weekly downloads

The problem

JWT auth shows up in every Node.js app. The setup is the same every time:

Side-by-side comparison

Without the abstraction, you handle signing, verification, refresh flow, and error mapping yourself. That is a lot of code to keep consistent.

Without jwt-abstraction
import jwt from "jsonwebtoken";

const ACCESS_TTL = "15m";
const REFRESH_TTL = "7d";

const createTokens = (payload) => ({
  accessToken: jwt.sign(payload, process.env.JWT_SECRET, {
    expiresIn: ACCESS_TTL,
    algorithm: "HS256"
  }),
  refreshToken: jwt.sign(payload, process.env.JWT_REFRESH_SECRET, {
    expiresIn: REFRESH_TTL,
    algorithm: "HS256"
  })
});

const verifyToken = (token, secret) => {
  try {
    return jwt.verify(token, secret);
  } catch (error) {
    if (error.name === "TokenExpiredError") {
      throw new Error("Token expired");
    }
    throw new Error("Invalid token");
  }
};

const protect = () => (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ error: "Missing token" });

  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch (error) {
    if (error.name === "TokenExpiredError") {
      return res.status(401).json({ error: "Token expired" });
    }
    res.status(401).json({ error: "Invalid token" });
  }
};

const refreshTokens = async (refreshToken) => {
  const payload = verifyToken(refreshToken, process.env.JWT_REFRESH_SECRET);
  return createTokens({ userId: payload.userId });
};
With jwt-abstraction
import { useJwt } from "@dax-side/jwt-abstraction";

const jwt = useJwt({
  secret: process.env.JWT_SECRET,
  refreshTokenSecret: process.env.JWT_REFRESH_SECRET
});

const tokens = jwt.create({ userId: 123 });
const payload = await jwt.verify(tokens.accessToken);

app.get("/profile", jwt.protect(), (req, res) => {
  res.json({ user: req.user });
});

Features

Security first

Secrets are required. If you do not provide one, the package throws NoSecretError immediately. There are no default secrets.

const jwt = useJwt({
  secret: process.env.JWT_SECRET,
  refreshTokenSecret: process.env.JWT_REFRESH_SECRET
});

Safe defaults:

Error handling

Catch specific error types instead of parsing strings.

import {
  TokenExpiredError,
  InvalidTokenError,
  NoSecretError
} from "@dax-side/jwt-abstraction";

try {
  const payload = await jwt.verify(token);
} catch (error) {
  if (error instanceof TokenExpiredError) {
    // Token expired
  } else if (error instanceof InvalidTokenError) {
    // Token malformed or tampered with
  } else if (error instanceof NoSecretError) {
    // Secret not configured
  }
}

Get started

Install the package, set your secret, and wire it into Express.

npm install @dax-side/jwt-abstraction

JWT_SECRET=your-secret

Full documentation is in the README.