@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 monthly downloads
669 downloads in the past 30 days. Updated Feb 9, 2026.

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.