| name | unjwt |
| description | Expert knowledge for working with unjwt — a low-level, zero-dep, JWT library using the Web Crypto API. Use this skill whenever the user is working with JWS, JWE, JWK, or JWT utilities, including signing, verifying, encrypting, decrypting, key management, and session handling. Trigger on any mention of unjwt or related JWT operations. |
| metadata | {"version":"0.1.0","library":"unjwt","library-version":"0.7.2","org":"sandros94","documentation":"https://github.com/sandros94/unjwt"} |
unjwt Skill
Low-level JWT library using the Web Crypto API. Zero runtime dependencies for core; optional peer deps for framework adapters.
Implements JWS (RFC 7515), JWE (RFC 7516), and JWK (RFC 7517).
Quick orientation
Skill written against unjwt@0.7.2. APIs are stable within v0.7 but check the changelog if behaviour seems off on a newer version.
The following is a list of reference files:
references/jws.md: sign(), verify(), signMulti(), verifyMulti(), verifyMultiAll(), generalToFlattenedJWS(), JWSSignOptions, JWSVerifyOptions, JWSMultiSignOptions, JWSMultiVerifyOptions, JWSMultiVerifyAllOptions, JWSMultiVerifyOutcome, JWSGeneralSerialization, JWSFlattenedSerialization, JWSMultiSigner
references/jwe.md: encrypt(), decrypt(), encryptMulti(), decryptMulti(), generalToFlattened(), JWEEncryptOptions, JWEDecryptOptions, JWEMultiEncryptOptions, JWEMultiDecryptOptions, JWEGeneralSerialization, JWEFlattenedSerialization, JWEMultiRecipient
references/jwk.md: generateKey(), generateJWK(), importKey(), exportKey(), wrapKey(), unwrapKey(), deriveSharedSecret(), PEM import/export (importPEM, exportPEM; importFromPEM, exportToPEM are deprecated aliases), PBES2 key derivation (deriveKeyFromPassword, deriveJWKFromPassword), JWK set utilities (getJWKsFromSet, getJWKFromSet — deprecated), JWK cache (configureJWKCache, clearJWKCache, WeakMapJWKCache), key lookup types (JWKLookupFunction, JWKLookupFunctionHeader), all JWK type definitions
references/utils.md: base64UrlEncode/base64UrlDecode, base64Encode/base64Decode, secureRandomBytes, concatUint8Arrays, textEncoder/textDecoder, type guards (isJWK, isJWKSet, isSymmetricJWK, isAsymmetricJWK, isPrivateJWK, isPublicJWK, isCryptoKey, isCryptoKeyPair, assertCryptoKey), validateJwtClaims, inferJWSAllowedAlgorithms, inferJWEAllowedAlgorithms, computeDurationInSeconds, Duration format (aliased by ExpiresIn / NotBeforeIn / MaxTokenAge), JWTClaimValidationOptions
references/adapters-h3.md: H3 session adapters (v1 and v2), useJWESession, useJWSSession, SessionManager interface, SessionConfigJWE, SessionConfigJWS, lifecycle hooks (onRead, onUpdate, onClear, onExpire, onError), key lookup hooks (onUnsealKeyLookup, onVerifyKeyLookup), lower-level functions, cookie chunking (v2), header-based tokens, refresh token pattern
references/adapters-elysia.md: Elysia session adapter, jwsSession/jweSession plugins, ambient ctx[contextKey], requireSession guard macro (derived per contextKey), multiple sessions (access JWS + refresh JWE), createJWSSession/createJWESession lower-level, SessionConfigJWS/SessionConfigJWE, lifecycle hooks (context not event), cookie chunking, header tokens, isolatedDeclarations plugin typing
Export Paths
| Path | Purpose |
|---|
unjwt | Flat barrel: all public functions and types from jws, jwe, jwk, utils |
unjwt/jws | sign(), verify() (Compact) — signMulti(), verifyMulti() (General JSON Serialization) |
unjwt/jwe | encrypt(), decrypt() (Compact) — encryptMulti(), decryptMulti() (General JSON Serialization) |
unjwt/jwk | Key generation, import/export, wrap/unwrap, PEM conversion, PBES2, cache utils |
unjwt/utils | Base64URL encode/decode, type guards, JWT claim validation, secureRandomBytes |
unjwt/adapters/h3 | H3 session adapter (aliases h3v1) |
unjwt/adapters/h3v1 | H3 v1 session adapter (Nuxt v4, Nitro v2) |
unjwt/adapters/h3v2 | H3 v2 session adapter (Nuxt v5, Nitro v3) |
unjwt/adapters/elysia | Elysia session adapter — jwsSession/jweSession plugins (>=1.4.0) |
Quick Start
import { sign, verify } from "unjwt/jws";
import { generateJWK } from "unjwt/jwk";
const key = await generateJWK("HS256");
const token = await sign({ sub: "user123" }, key, { expiresIn: "1h" });
const { payload } = await verify(token, key);
import { encrypt, decrypt } from "unjwt/jwe";
const jwe = await encrypt({ secret: "data" }, "password");
const { payload } = await decrypt(jwe, "password");
import { useJWESession } from "unjwt/adapters/h3v2";
const session = await useJWESession(event, { key: "secret", maxAge: "7D" });
await session.update({ userId: "123" });
Key Concepts
JOSEPayload: the payload type accepted by sign/encrypt — string | Uint8Array | Record<string, unknown>; covers both JWT and generic serializable objects. JWTClaims remains the spec-compliant sub-type for typed claim access
- JWK-first key model: functions accept JWK objects directly;
importKey() normalizes CryptoKey/JWK/Uint8Array/string
- Algorithm inference:
sign/encrypt infer alg/enc from JWK properties when not explicitly provided; password strings default to PBES2
dir (direct encryption): pass a CryptoKey | JWK_oct | Uint8Array directly as the CEK; enc must always be specified
- Multi-recipient JWE:
encryptMulti() emits General JSON Serialization (RFC 7516 §7.2.1). One shared CEK, one ciphertext, per-recipient wraps. dir and bare ECDH-ES are rejected in multi (throw ERR_JWE_ALG_FORBIDDEN_IN_MULTI). decryptMulti() accepts General + Flattened (auto-normalized); compact tokens stay with decrypt()
- Multi-signature JWS:
signMulti() emits General JSON Serialization (RFC 7515 §7.2.1). Shared payload, per-signer protected header. b64: false (RFC 7797) must be consistent across all signers. verifyMulti() returns the first valid signature; verifyMultiAll() returns per-signer outcomes for policy-driven verification (all-must-verify, M-of-N quorum, named signers). Both accept General + Flattened (auto-normalized); compact tokens stay with verify()
- ExpiresIn: time durations accept numbers (seconds) or strings:
"30s", "10m", "2h", "7D", "1W", "3M", "1Y" (also long forms: "minutes", "hours", "days", "weeks", "months", "years")
- H3 Session Adapters: store JWTs in chunked cookies; sessions are lazy (
id is undefined until update() is called)
- PBES2 security: default
p2c (iteration count) is 600_000 per OWASP cookbook; only lower it for legacy interoperability