hwtr-js
Implémentation de référence JavaScript du protocole HWT github. Démos utilisant cette implémentation sur hwt-demo.
documentation officielle issues
Format du token : hwt.signature.key-id.expires-unix-seconds.format.payload
Installation
// Deno / JSR
import Hwtr from 'jsr:@hwt/hwtr-js'
// Node / Bun via JSR
// npx jsr add @hwt/hwtr-js
// Local
import Hwtr from './hwtr.js'
Démarrage rapide
// Générer les clés une fois — stocker le résultat de façon sécurisée
const keyConfig = await Hwtr.generateKeys({ type: 'Ed25519' })
// Créer une instance
const hwtr = await Hwtr.factory({}, keyConfig)
// Signer
const token = await hwtr.create({ sub: 'user:123', role: 'editor' })
// Vérifier
const result = await hwtr.verify(token)
if (result.ok) {
console.log(result.data) // { sub: 'user:123', role: 'editor' }
}
API
Hwtr.factory(options, keyConfig) → Promise<Hwtr>
Constructeur privilégié. Équivalent à new Hwtr(options).importKeys(keyConfig).
const hwtr = await Hwtr.factory({ expiresInSeconds: 3600 }, keyConfig)
Options du constructeur — toutes optionnelles :
| Option | Défaut | Description |
|---|---|---|
expiresInSeconds |
60 |
Durée de vie du token par défaut |
maxTokenLifetimeSeconds |
86400 |
Plafond absolu ; 0 = illimité |
leewaySeconds |
1 |
Tolérance au décalage d'horloge. La spécification recommande de ne pas dépasser 5 minutes (300s) ; aucun maximum imposé. |
maxTokenSizeBytes |
4096 |
Plage : 512–16384 |
format |
'j' |
Codec du payload |
signatureSize |
0 |
Tronquer la signature HMAC à N caractères ; 0 = complète ; ignoré pour les clés asymétriques |
throwOnInvalid |
false |
Lever une exception plutôt que retourner ok: false |
throwOnExpired |
false |
Lever une exception plutôt que retourner ok: false |
throwOnGenerate |
true |
Lever une exception en cas d'échec de signature |
throwOnEncoding |
true |
Lever une exception en cas d'échec du codec |
Génération de clés
// Générer les clés une fois — stocker le résultat de façon sécurisée
const keyConfig = await Hwtr.generateKeys({
count: 2, // nombre de clés (défaut : 1)
current: 'primary', // id de la clé de signature
type: 'Ed25519', // algorithme (défaut : 'HMAC')
})
// → { current, type, keys: [{ id, created, privateKey, publicKey }] }
// Clé HMAC unique
const key = Hwtr.generateKey({ id: 'main' })
// → { id, secret, created }
// Paire asymétrique unique
const pair = await Hwtr.generateKeyPair({ id: 'k1', type: 'ECDSA-P256' })
// → { id, created, publicKey, privateKey } (base64url SPKI/PKCS8)
Algorithmes supportés : 'HMAC', 'Ed25519', 'ECDSA-P256', 'ECDSA-P384', 'ECDSA-P521'
HMAC est symétrique — mono-service uniquement. Utiliser des types asymétriques pour la vérification inter-services. Le support de P-521 varie selon l'environnement ; tester avant utilisation.
hwtr.importKeys(keyConfig) → Promise<Hwtr>
Importe les clés de signature et/ou de vérification. factory() l'appelle automatiquement.
// structure de keyConfig
{
current: 'primary', // id de la clé utilisée pour signer les nouveaux tokens
type: 'Ed25519',
keys: [
{ id: 'primary', created: '...', privateKey: 'BASE64URL', publicKey: 'BASE64URL' }
// HMAC: { id, created, secret }
],
publicKeys: { // optionnel : clés de vérification uniquement, provenant d'autres services
'partner-key': 'BASE64URL'
}
}
Plusieurs clés sont supportées pour la rotation. Toute clé dont l'id correspond au kid d'un token peut le vérifier. Seule la clé avec id === current signe les nouveaux tokens.
hwtr.create(payload, hiddenData?) → Promise<string>
Crée un token avec l'expiration par défaut.
const token = await hwtr.create({ sub: 'user:123' })
// hiddenData est signé mais non intégré — le vérificateur (verifier) doit fournir la même valeur
const token = await hwtr.create({ sub: 'user:123' }, { ip: '203.0.113.1' })
Retourne '' (ou lève une exception si throwOnInvalid) lorsque le token dépasserait maxTokenSizeBytes.
hwtr.createWith(expiresInSeconds, payload, hiddenData?) → Promise<string>
Crée un token avec une durée de vie spécifique. Lève une exception si elle dépasse maxTokenLifetimeSeconds.
const token = await hwtr.createWith(300, { sub: 'user:123', oneTime: true })
hwtr.verify(token, hiddenData?) → Promise<VerifyResult>
Vérifie la signature et l'expiration. Ne lève pas d'exception par défaut.
const result = await hwtr.verify(token)
// result.ok — true si valide
// result.data — payload décodé
// result.expired — true si expiré
// result.expires — unix seconds
// result.error — présent en cas d'échec
Valeurs de result.error : 'hwt invalid', 'hwt invalid format', 'hwt expired', 'hwt unknown encoding "x"', 'hwt unknown key', 'hwt invalid signature', 'hwt data decoding failed'
hwtr.decode(token) → Promise<{ data, expires, error? }>
Décode le payload sans vérifier la signature ni l'expiration. À des fins d'inspection uniquement.
Vérification inter-services
Un service qui ne fait que vérifier (sans signer) passe publicKeys sans keys privées :
const verifyOnly = await Hwtr.factory({}, {
type: 'Ed25519',
keys: [],
publicKeys: { 'auth-key': 'BASE64URL' }
})
Méthodes utilitaires pour la distribution de clés :
const pub = await hwtr.exportPublicKey('primary') // base64url SPKI string
const all = await hwtr.getPublicKeys() // { id: base64url, ... }
await hwtr.addPublicKey({ id, publicKeyBase64, type }) // ajouter une clé externe à l'exécution
Codecs
'j' (JSON) est le seul codec intégré. Enregistrer les codecs personnalisés une fois par processus :
Hwtr.registerFormat('name', {
encode(data) { /* → Uint8Array */ },
decode(buffer) { /* → value */ }
})
Hwtr.formats // → ['j', 'name', ...]
Noms de format : /^[a-zA-Z][a-zA-Z0-9]{1,19}$/. Les noms déjà enregistrés lèvent une exception.
Un codec JSON étendu (jx) supportant Date, BigInt, Map, Set et les tableaux typés est disponible dans le dépôt source sous hwtr.formats.js — il n'est pas inclus dans le package publié.
Utilitaires
Hwtr.timingSafeEqual(a, b) // comparaison en temps constant ; chaînes ou Uint8Array
Hwtr.bufferToBase64Url(buffer) // ArrayBuffer | Uint8Array → base64url string
Hwtr.base64urlToUint8Array(str) // base64url → Uint8Array ; tableau vide en cas d'entrée invalide
Hwtr.textToBase64Url(text) // UTF-8 string → base64url
Hwtr.base64urlToText(str) // base64url → UTF-8 string
Hwtr.isHwt(str) // true si str contient un segment de signature HWT plausible
Hwtr.ALGORITHMS // carte des algorithmes supportés
Hwtr.version // version
Tous également exportés en tant qu'exports nommés depuis hwtr.js.
Liens connexes
Licence
Copyright 2026 Jim Montgomery
SPDX-License-Identifier: Apache-2.0
Licence Apache 2.0. Voir LICENSE.