@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
The problem
JWT auth shows up in every Node.js app. The setup is the same every time:
- Wire up jsonwebtoken
- Configure algorithms and expiries
- Create access and refresh tokens
- Build Express middleware
- Sign, verify, and handle edge cases by hand
- Map token errors to responses
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
- Only dependency is jsonwebtoken
- Creates access and refresh token pairs
- Separate secrets for access and refresh tokens
- Express middleware included
- TypeScript support with strict mode
- Typed error classes
- 100% test coverage
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:
- Access token TTL: 15 minutes
- Refresh token TTL: 7 days
- Algorithm: HS256
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-secretFull documentation is in the README.