Files
WRNexusJS/packages/jwt
2026-07-30 18:59:01 +05:30
..
2026-07-27 12:42:18 +05:30
2026-07-27 12:42:18 +05:30
2026-07-30 18:59:01 +05:30
2026-07-12 15:55:18 +05:30

@wrnexus/jwt

Dependency-free JSON Web Tokens (HS256) via Web Crypto, plus a bearer-token auth middleware for WrNexus.

Part of the WrNexus framework — an SSR-first, Bun-native full-stack web framework.

Overview

@wrnexus/jwt signs and verifies stateless JSON Web Tokens using the HS256 (HMAC-SHA-256) algorithm. It has no runtime dependencies — signing and verification are implemented directly on the standard Web Crypto API (crypto.subtle), which Bun provides natively. It runs server-side and pairs with the session-based auth in @wrnexus/core, giving you a stateless option for API and mobile clients. Reach for it when you need bearer-token auth rather than cookie sessions.

Installation

bun add @wrnexus/jwt

Private package — the machine must be authenticated to the wrnexus npm org (a read token in ~/.npmrc). Requires Bun (Node is not supported).

API

Single entry point (@wrnexus/jwt). All functions are async and return Promises.

Export Kind Description
signJwt(payload, secret, options?) function Sign claims into an HS256 token string.
verifyJwt<T>(token, secret, options?) function Verify a token and return its claims, or throw.
jwtAuth(options) function Middleware that verifies a bearer JWT and sets ctx.user.
JwtError class Error thrown on any signature/payload/expiry failure.
JwtClaims interface Claims shape (sub, iat, exp, nbf, plus arbitrary keys).
SignOptions interface Options for signJwt.
JwtAuthOptions interface Options for jwtAuth.

signJwt(payload, secret, options?)

function signJwt(payload: JwtClaims, secret: string, options?: SignOptions): Promise<string>;

Signs payload with secret using HS256 and returns the encoded token (header.body.signature). An iat (issued-at) claim is always added.

SignOptions:

  • expiresIn?: number — seconds until expiry; sets the exp claim.
  • now?: number — override the issued-at time (seconds), useful for testing.

verifyJwt<T>(token, secret, options?)

function verifyJwt<T extends JwtClaims = JwtClaims>(
  token: string,
  secret: string,
  options?: { now?: number },
): Promise<T>;

Verifies the HS256 signature and returns the decoded claims typed as T. Throws JwtError when the token is malformed, the signature is invalid, the payload is not valid JSON, the token is expired (exp), or not yet valid (nbf). Pass now (seconds) to override the reference time for the exp/nbf checks.

jwtAuth(options)

function jwtAuth(options: JwtAuthOptions): Middleware;

Returns a WrNexus Middleware that reads a token, verifies it, and assigns the claims to ctx.user.

JwtAuthOptions:

  • secret: string — the HMAC secret used to verify tokens.
  • getToken?: (ctx: Context) => string | undefined — how to extract the token. Defaults to reading Authorization: Bearer <token>.
  • required?: boolean — when true (default), a missing or invalid token responds with 401 { ok: false, error: "Unauthorized" }. When false, requests pass through and ctx.user is only set if a valid token is present.

Usage

import { signJwt, verifyJwt, jwtAuth, JwtError } from "@wrnexus/jwt";

const secret = process.env.JWT_SECRET!;

// Sign a token that expires in one hour
const token = await signJwt({ sub: user.id, role: "admin" }, secret, {
  expiresIn: 3600,
});

// Verify it later
try {
  const claims = await verifyJwt<{ sub: string; role: string }>(token, secret);
  console.log(claims.sub, claims.role);
} catch (err) {
  if (err instanceof JwtError) {
    // invalid signature, expired, malformed, etc.
  }
}

Protecting routes with the middleware:

import { jwtAuth } from "@wrnexus/jwt";

// Require a valid bearer token; ctx.user holds the verified claims
app.use(jwtAuth({ secret: process.env.JWT_SECRET! }));

// Optional auth — populate ctx.user when present, but don't 401
app.use(jwtAuth({ secret: process.env.JWT_SECRET!, required: false }));

Requirements / Notes

  • Bun-only. Uses the standard Web Crypto API (crypto.subtle.importKey, sign, verify) plus btoa/atob and TextEncoder/TextDecoder — all provided by Bun. No third-party crypto dependency.
  • Algorithm: HS256 (HMAC with SHA-256) only. Asymmetric algorithms (RS/ES) are not supported.
  • Integrates with @wrnexus/core for Context, Middleware, and ctx.user; it complements the framework's cookie/session auth with a stateless bearer-token flow for API and mobile clients.