A JWT authentication package providing both Access Token and Refresh Token mechanisms, featuring fingerprint recognition, Redis storage, and automatic refresh functionality.
version Golang can get here
-
- Access Token (short-term) + Refresh Token (long-term)
- Automatic token refresh without requiring re-login
- ES256 algorithm (Elliptic Curve Digital Signature)
-
- Generates unique fingerprints based on User-Agent, Device ID, OS, and Browser
- Prevents token misuse across different devices
- Automatic device type detection (Desktop, Mobile, Tablet)
-
- Adds Access Token to blacklist upon logout
- Redis TTL automatically cleans expired revocation records
- Prevents reuse of logged-out tokens
-
- Refresh Token version tracking
- Auto-generates new Refresh ID after 5 refresh attempts
- Prevents replay attacks
-
- Auto-regenerates when Refresh Token has less than half lifetime remaining
- 5-second grace period for old tokens to reduce concurrency issues
- Minimizes database queries
-
- Automatic cookie reading
- Authorization Bearer Header
- Custom Headers (X-Device-ID, X-Refresh-ID)
-
- Supports file paths or direct key content
- Customizable Cookie names
- Production/Development environment auto-switching
-
npm install @pardnchiu/jwt-auth
-
import { JWTAuth } from '@pardnchiu/jwt-auth'; // initialize the JWT instance await JWTAuth.init({ privateKeyPath: "./keys/private.pem", publicKeyPath: "./keys/public.pem", // or paste keys directly: // privateKey: "-----BEGIN EC PRIVATE KEY-----...", // publicKey: "-----BEGIN PUBLIC KEY-----...", accessTokenExpires: 900, // seconds refreshTokenExpires: 604800, // seconds // true: domain=domain, samesite=none, secure=true // false: domain=localhost, samesite=lax, secure=false isProd: false, domain: "pardn.io", // cookie key, default access_token/refresh_id AccessTokenCookieKey: "access_token", RefreshTokenCookieKey: "refresh_id", // store with redis redis: { host: "localhost", port: 6379, password: "", // optional db: 0 // optional }, checkUserExists: async (userId: string): Promise<boolean> => { // return true if user exists, false otherwise return true; } }); process.on("SIGINT", async () => { await JWTAuth.close(); process.exit(0); });
-
import { Request, Response } from 'express'; import { JWTAuth } from '@pardnchiu/jwt-auth'; async function loginHandler(req: Request, res: Response) { // after verifying user login info... const userData = { id: "user123", name: "", email: "[email protected]", thumbnail: "avatar.jpg", role: "user", level: 1, scope: ["read", "write"] }; try { const tokenResult = await JWTAuth.CreateJWT(req, res, userData); // automatically set in cookies res.json({ success: true, token: tokenResult.token, refresh_id: tokenResult.refresh_id }); } catch (error) { res.status(500).json({ error: error.message }); } }
-
import { Request, Response } from 'express'; import { JWTAuth } from '@pardnchiu/jwt-auth'; async function protectedHandler(req: Request, res: Response) { try { const result = await JWTAuth.VerifyJWT(req, res); if (!result.isAuth) { // Authentication failed return res.status(result).json({ error: result.isError ? "Bad Request" : "Unauthorized" }); } // Authentication success, user result.data to get user data res.json({ message: "Protected resource accessed", user: result.data }); } catch (error) { res.status(500).json({ error: error.message }); } }
-
import { Request, Response } from "express"; import { JWTAuth } from "@pardnchiu/jwt-auth"; async function logoutHandler(req: Request, res: Response) { try { await JWTAuth.RevokeJWT(req, res); res.json({ message: "Successfully logged out" }); } catch (error) { res.status(500).json({ error: error.message }); } }
privateKeyPath
/privateKey
: private key file path or contentpublicKeyPath
/publicKey
: public key file path or contentaccessTokenExpires
: access token expire timerefreshTokenExpires
: refresh id expire timeisProd
: is production or not (affects cookie setting)domain
: cookie domainredis
: redis connectionhost
: redis hostport
: redis portpassword
: redis password (optional)db
: redis db (optional)
checkUserExists
: user existence check functionAccessTokenCookieKey
: access token cookie name (default: 'access_token')RefreshTokenCookieKey
: refresh id cookie name (default: 'refresh_id')
- Cookie: Automatically reads token from cookie
- Authorization Header:
Authorization: Bearer <token>
- Custom Headers:
X-Device-ID
: Device IDX-Refresh-ID
: Custom Refresh ID
The system automatically generates a new Refresh ID in the following cases:
- Refresh version exceeds 5 times
- Remaining Refresh Token time is less than half
The new tokens are returned via:
- HTTP Header:
X-New-Access-Token
- HTTP Header:
X-New-Refresh-ID
- Cookie auto-update
- Fingerprint recognition: Generates a unique fingerprint based on User-Agent, Device-ID, OS, Browser, and Device type
- Token revocation: Adds token to a blacklist on logout
- Automatic expiration: Supports TTL to automatically clean up expired tokens
- Version control: Tracks Refresh Token versions to prevent replay attacks
- Fingerprint validation: Ensures tokens are used from the same device/browser
The VerifyJWT
method returns:
AuthData
object on successful authentication- HTTP status code number on failure:
401
: Unauthorized (invalid/expired tokens, user doesn't exist)400
: Bad Request (invalid fingerprint, malformed tokens)
Common error scenarios:
- Token revoked
- Fingerprint mismatch
- Refresh data not found
- JWT expired or invalid
- User not found
This source code project is licensed under the MIT license.
©️ 2025 邱敬幃 Pardn Chiu