@nitrotool/jwt

Lightweight JWT utilities for Nuxt/Nitro/UnJS environments **with built-in h3 helpers**.
1,854
Install
npm install @nitrotool/jwt
Latest Version:1.0.5
Last Updated:Feb 2, 2026
Links: npm
Maintainer: hareland

@nitrotool/jwt

Lightweight JWT utilities for Nuxt/Nitro/UnJS environments with built-in h3 helpers.

  • Built on @tsndr/cloudflare-worker-jwt
  • Helpers that read jwtSecret from your Nitro runtime config
  • Batteries included with h3 utilities to extract tokens from requests and enforce authentication

Learn more about JWT and how it works:

Installation

pnpm install @nitrotool/jwt
npm install @nitrotool/jwt

Required peer dependency:

  • h3 (this package expects h3 to be present)

Nuxt setup

export default defineNuxtConfig({
    modules: ['@nitrotool/jwt'],
    //You can also 
    jwt: {
        //default secret (only use this locally, and override it in prod)
        secret: 'some-random-secret-yes-it-is-not-very-secure',
        // Alternatively also available as NUXT_JWT_SECRET env var
    },
});

Usage with Nuxt 3/4

This is available all places where auto-imported can be used on the server-side.

type MyClaims = { tenantId: string };

const token = await encodeJwt<MyClaims>({tenantId: '123', sub: '123'});

// Later…
const isValid = await verifyJwt(token);
const payload = await decodeJwt<MyClaims>(token);


// or
export default defineEventHandler(async (event) => {
    const jwt = await requireJwt(event);
    // you now have access to a required JWT token via Authorization bearer header.
})

Importing

Import from the main entry or subpaths:

// Node-only helpers
import {encodeJwtRaw, verifyJwtRaw, decodeJwtRaw} from '@nitrotool/jwt/core';

// h3-only helpers
import {extractApiToken, requireApiToken} from '@nitrotool/jwt/h3';

Quick Start Nitro

1. Create plugin to load secret.

// plugins/0.jwt.ts
import { defineNitroPlugin, useRuntimeConfig } from 'nitropack/runtime';
import { configureJwtRuntime } from '@nitrotool/jwt';

export default defineNitroPlugin(() => {
    configureJwtRuntime(() => useRuntimeConfig() as any);
});

2. Basic usage

import {encodeJwt, verifyJwt, decodeJwt} from '@nitrotool/jwt';

type MyClaims = { tenantId: string };

const token = await encodeJwt<MyClaims>({tenantId: '123', sub: '123'});

// Later…
const isValid = await verifyJwt(token);
const payload = await decodeJwt<MyClaims>(token); // { sub: '123', tenantId: '123', exp: ... }

By default, non-Raw helpers read the secret from your runtime config:

  • useRuntimeConfig().jwtSecret

If you need to pass a secret explicitly, use the *Raw variants.

3. In a route handler

Extract API tokens and enforce authentication in request handlers:

Require a JWT token
import {defineEventHandler} from 'h3';
import {requireJwt} from '@nitrotool/jwt';

export default defineEventHandler(async (event) => {
    const decodedJwt = await requireJwt(event)
})
Extract a JWT token manually
import { defineEventHandler } from 'h3';
import { extractApiToken, requireApiToken, decodeJwt } from '@nitrotool/jwt';

export default defineEventHandler(async (event) => {
    // Try to read token (Authorization: Bearer <token> or ?<queryKey>=<token>)
    const token = extractApiToken(event, {queryKey: 'token'}); // queryKey defaults to 'token'

    // Or strictly require it (throws UnauthenticatedError if missing)
    const requiredToken = requireApiToken(event);

    // Optionally decode/verify
    const claims = await decodeJwt<{ userId: string }>(requiredToken);

    return { ok: true, userId: claims.userId };
});

Supported token locations:

  • Authorization header: Authorization: Bearer <token>
  • Query string: ?token=<token>

Configuration

When using non-Raw helpers, ensure a secret is available at runtime:

// nuxt.config.ts (Nuxt/Nitro runtime config)
export default defineNuxtConfig({
    runtimeConfig: {
        jwtSecret: 'some-random-secret-yes-it-is-not-very-secure',
    },
});

Examples

Sign with custom TTL:

import {encodeJwtRaw} from '@nitrotool/jwt';

const token = await encodeJwtRaw(
    {userId: '123', role: 'admin'},
    process.env.JWT_SECRET!,
    60 * 10 // 10 minutes
);

Decode without verifying signature (use only for non-sensitive scenarios):

import {decodeJwt} from '@nitrotool/jwt';

const payload = await decodeJwt<{ userId: string }>(token, {verify: false});

Verify with an explicit secret:

import {verifyJwtRaw} from '@nitrotool/jwt';

const ok = await verifyJwtRaw(token, process.env.JWT_SECRET!);

Errors

  • UnauthorizedError('Invalid JWT token.') is thrown when verification fails in decodeJwt / decodeJwtRaw.
  • UnauthenticatedError() is thrown by requireApiToken when no token is present.

Security Notes

  • Keep jwtSecret strong and private.
  • Prefer short TTLs and refresh flows.
  • Only set verify: false for non-sensitive, debug-like operations.
  • Rotate secrets periodically and invalidate old tokens if needed.

API Reference

All helpers are asynchronous.

JWT helpers

  • encodeJwtRaw<T>(payload, secret, ttl = 60): Promise<string>

    • Signs a token with the provided secret.
    • ttl is in seconds. Default: 60.
    • exp is set automagically from ttl.
  • encodeJwt<T>(payload, ttl = 60): Promise<string>

    • Same as encodeJwtRaw, but uses useRuntimeConfig().jwtSecret.
  • verifyJwtRaw(token, secret): Promise<boolean>

    • Verifies signature and expiry using the provided secret.
  • verifyJwt(token): Promise<boolean>

    • Same as verifyJwtRaw, but uses useRuntimeConfig().jwtSecret.
  • decodeJwtRaw<T>(token, secret, { verify = true } = {}): Promise<T & Partial<JwtPayload>>

    • Decodes the token. When verify is true, verifies signature and expiry.
    • Throws UnauthorizedError('Invalid JWT token.') if verification fails.
    • Throws if verify is true but secret is empty.
  • decodeJwt<T>(token, { verify = true } = {}): Promise<T & Partial<JwtPayload>>

    • Same as decodeJwtRaw, but uses useRuntimeConfig().jwtSecret.
    • Throws UnauthorizedError('Invalid JWT token.') if verification fails.

Types:

  • ExtendableJwtPayload<T> lets you define custom claims merged with standard JWT claims.

h3 helpers (required)

  • extractBearerToken(event): string | undefined

    • Reads Authorization header and returns the token without Bearer .
  • extractQueryToken(event, key = 'token'): string | undefined

    • Reads token from the query string.
  • extractApiToken(event): string | undefined

    • Returns the first non-empty token found by extractBearerToken or extractQueryToken.
  • requireApiToken(event): string

    • Same as extractApiToken, but throws UnauthenticatedError if missing.

License

MIT

Version History

Version Released License
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1