There are a lot of authentication libraries out there that deals with JWT, probably the most popular one(the one that I used a lot in my project) is the passport-jwt library used together with passport. However, the library has the few problems: - Need to be used with passport.js > This may not be a problem to some people, but I find passport.js a bit difficult to use since it's a black box model (I don't understand the magic happening behind the scene). - Need to talk to DB > The official example in documentation contains a query to db in order to authenticate the user, which I believe is against the natural, being stateless, of JWT. - Doesn't handle key rotation - Doesn't handle key revocation
In order to address these problems, I decided to make this open source library.
Getting Started
$3
I have this tested from Node version 12 and above, make sure you have the right version
$3
Install with npm
``JS npm install --save @hansenw/jwt-auth `
Usage
$3
`javascript // authService.js import JWTAuth from "@hansenw/jwt-auth";
const JWT = new JWTAuth(); export default JWT;
// to use in other files import jwt from "./authService";
// to generate a jwt token const token = jwt.sign({ / some payload / });
// to verify try { const payload = jwt.verify(token); // ... } catch (e) { // cannot be verified }
// to revoke await jwt.revoke(token); jwt.verify(token); // this will throw JWTRevoked
`
$3
`javascript import * as express from "express"; import JWTAuth from "@hansenw/jwt-auth";
// if user info is ever needed const user = await db.collection("user").find({ username: payload.username });
res.json({ message: "Authorized user only" }) })
// start the express app app.listen(3000)
`
$3
> Customze what to store in the revocation list, be default revocation list contain items on type { jti: string, exp: number }`typescript import JWTAuth, { RevocationListItem } from "@hansenw/jwt-auth";
// to verify try { const payload = jwt.verify(token); // ... } catch (e) { // cannot be verified }
// to revoke await jwt.revoke(token, (payload) => ({ jti: payload.jti, exp: payload.exp, ip: req.ip, })); jwt.verify(token); // this will throw JWTRevoked
`
$3
If you want to build your own auth server or auth service within the microservices, check out this jwt-jwks-client library I made that can be used together with this one.
#### server.ts
`typescript import * as express from "express"; import JWTAuth from "@hansenw/jwt-auth";
const app = express() const authService = new JwtAuth();
app.post("/login", (req: Request, res: Response) => { // Replace with your own matching logic if (req.body.username === "admin" && req.body.password === "password") { const token = authService.sign({ userId: "admin" }); return ( res .set("authorization", token) .send("Authorized") ); } res.status(401).send("Not authorized"); });
// Expose jwks through an API app.get("/jwks", (req: Request, res: Response) => { res.json(authService.JWKS(true)); });
`
#### Client
`ts import * as express from "express"; import JwksClient from "jwt-jwks-client";
const authClient = new JwksClient({ jwksUri: "http://localhost:3000/jwks", secure: false, });
app.get("/secret", async (req: Request, res: Response) => { const token = req.headers.authorization; if (token) { // Verify the token here await authClient.verify(token); return res.send("This is a secret page"); } return res.send(
You are not authorized to see the secret page); }); ` See complete example here
API
$3
__Class: JWTAuth__ `javascript const jwt = new JWTAuth(options: JwtAuthOptions); ` JwtAuthOptions: - algorithm?: can be ['RSA' | 'EC' | 'OKP' | 'oct'], __Default__: "EC" - crvOrSize?: key size (in bits) or named curve ('crv') for "EC", __Default__: 2048 for RSA, 'P-256' for EC, 'Ed25519' for OKP and 256 for oct. - amount?: number of keys kept in rotation, __Default__: 3 - interval?: cron expression for how often to generate a new key, __Default__: "00 00 /4 ": every 4 hour, generate a new token > Make sure the token expire time is less than the interval that a new token is generated - signSkip?: number of keys skipped when generating a new token, __Default__: 1 > By default, there are 3 keys stored, and by setting this to 1, every time a new token is signed, only the last 2 keys will be used since the first key will be removed after the rotation. - tokenAge?: token expire time in zeit/ms, __Default__ '10m'
$3
#### jwt.sign(payload: object, options?: JWT.SignOptions) Generate a new jwt token `javascript const token = jwt.sign(payload, options?); ` - payload: