hwtr-js

HWT 프로토콜 github의 JavaScript 참조 구현체입니다. 이 라이브러리를 사용한 데모는 hwt-demo에 있습니다.

공식 문서 이슈

Token 형식: hwt.signature.key-id.expires-unix-seconds.format.payload


설치

// 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'

빠른 시작

// Generate keys once — store the result securely
const keyConfig = await Hwtr.generateKeys({ type: 'Ed25519' })

// Create an instance
const hwtr = await Hwtr.factory({}, keyConfig)

// Sign
const token = await hwtr.create({ sub: 'user:123', role: 'editor' })

// Verify
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>

권장 생성자. new Hwtr(options).importKeys(keyConfig)와 동일합니다.

const hwtr = await Hwtr.factory({ expiresInSeconds: 3600 }, keyConfig)

생성자 옵션 — 모두 선택 사항:

옵션 기본값 설명
expiresInSeconds 60 기본 token 유효 기간
maxTokenLifetimeSeconds 86400 상한값; 0 = 무제한
leewaySeconds 1 클럭 오차 허용 범위. 스펙은 5분(300초)을 초과하지 않도록 권장하며, 최댓값은 강제되지 않습니다.
maxTokenSizeBytes 4096 범위: 512–16384
format 'j' payload codec
signatureSize 0 HMAC 서명을 N자로 잘라냄; 0 = 전체; 비대칭 키에서는 무시됨
throwOnInvalid false ok: false 반환 대신 예외 발생
throwOnExpired false ok: false 반환 대신 예외 발생
throwOnGenerate true 서명 실패 시 예외 발생
throwOnEncoding true codec 실패 시 예외 발생

키 생성

// Complete key set — pass directly to factory or importKeys
const keyConfig = await Hwtr.generateKeys({
  count: 2,           // number of keys (default: 1)
  current: 'primary', // id of the signing key
  type: 'Ed25519',    // algorithm (default: 'HMAC')
})
// → { current, type, keys: [{ id, created, privateKey, publicKey }] }

// Single HMAC key
const key = Hwtr.generateKey({ id: 'main' })
// → { id, secret, created }

// Single asymmetric pair
const pair = await Hwtr.generateKeyPair({ id: 'k1', type: 'ECDSA-P256' })
// → { id, created, publicKey, privateKey }  (base64url SPKI/PKCS8)

지원 알고리즘: 'HMAC', 'Ed25519', 'ECDSA-P256', 'ECDSA-P384', 'ECDSA-P521'

HMAC은 대칭 방식으로 단일 서비스 전용입니다. 서비스 간 검증(cross-service verification)이나 위임 체인(delegation chain)이 필요한 경우 비대칭 방식을 사용하세요. P-521 지원은 환경마다 다를 수 있으니 사용 전에 테스트하세요.


hwtr.importKeys(keyConfig)Promise<Hwtr>

서명 및/또는 검증(verification) 키를 가져옵니다. factory()가 대신 호출해 줍니다.

// keyConfig shape
{
  current: 'primary',    // id of the key used to sign new tokens
  type: 'Ed25519',
  keys: [
    { id: 'primary', created: '...', privateKey: 'BASE64URL', publicKey: 'BASE64URL' }
    // HMAC: { id, created, secret }
  ],
  publicKeys: {          // optional: verification-only keys from other services
    'partner-key': 'BASE64URL'
  }
}

키 교체(rotation)를 위해 여러 키를 지원합니다. token의 kid와 일치하는 id를 가진 키라면 어느 것이든 검증할 수 있습니다. 새 token 서명에는 id === current인 키만 사용됩니다.


hwtr.create(payload, hiddenData?)Promise<string>

기본 만료 시간으로 token을 생성합니다.

const token = await hwtr.create({ sub: 'user:123' })

// hiddenData는 서명되지만 token에 포함되지 않음 — 검증자(verifier)는 동일한 값을 제공해야 함
const token = await hwtr.create({ sub: 'user:123' }, { ip: '203.0.113.1' })

token이 maxTokenSizeBytes를 초과하면 ''을 반환합니다(throwOnInvalid가 설정된 경우 예외 발생).


hwtr.createWith(expiresInSeconds, payload, hiddenData?)Promise<string>

특정 유효 기간으로 token을 생성합니다. maxTokenLifetimeSeconds를 초과하면 예외가 발생합니다.

const token = await hwtr.createWith(300, { sub: 'user:123', oneTime: true })

hwtr.verify(token, hiddenData?)Promise<VerifyResult>

서명과 만료 여부를 검증합니다. 기본적으로 예외를 발생시키지 않습니다.

const result = await hwtr.verify(token)
// result.ok      — true이면 유효함
// result.data    — 디코딩된 payload
// result.expired — 만료된 경우 true
// result.expires — 유닉스 시간(초)
// result.error   — 실패 시 존재

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? }>

서명 또는 만료 여부 검증 없이 payload를 디코딩합니다. 검사 목적으로만 사용하세요.


서비스 간 검증

서명 없이 검증만 수행하는 서비스는 개인 keys 없이 publicKeys만 전달합니다:

const verifyOnly = await Hwtr.factory({}, {
  type: 'Ed25519',
  keys: [],
  publicKeys: { 'auth-key': 'BASE64URL' }
})

키 배포를 위한 편의 메서드:

const pub = await hwtr.exportPublicKey('primary')       // base64url SPKI 문자열
const all = await hwtr.getPublicKeys()                  // { id: base64url, ... }
await hwtr.addPublicKey({ id, publicKeyBase64, type })  // 런타임에 외부 키 추가

Codec

'j'(JSON)는 유일한 내장 codec입니다. 커스텀 codec은 프로세스당 한 번 등록합니다:

Hwtr.registerFormat('name', {
  encode(data) { /* → Uint8Array */ },
  decode(buffer) { /* → value */ }
})
Hwtr.formats // → ['j', 'name', ...]

형식 이름 규칙: /^[a-zA-Z][a-zA-Z0-9]{1,19}$/. 이미 등록된 이름은 예외를 발생시킵니다.

Date, BigInt, Map, Set, typed array를 지원하는 확장 JSON codec(jx)은 소스 저장소의 hwtr.formats.js에서 확인할 수 있으며, 배포 패키지에는 포함되지 않습니다.


유틸리티

Hwtr.timingSafeEqual(a, b)      // constant-time comparison; strings or Uint8Array
Hwtr.bufferToBase64Url(buffer)  // ArrayBuffer | Uint8Array → base64url string
Hwtr.base64urlToUint8Array(str) // base64url → Uint8Array; empty array on bad input
Hwtr.textToBase64Url(text)      // UTF-8 string → base64url
Hwtr.base64urlToText(str)       // base64url → UTF-8 string
Hwtr.isHwt(str)                 // true if str contains a plausible HWT signature segment
Hwtr.ALGORITHMS                 // supported algorithm map
Hwtr.version                    // version

위 항목들은 hwtr.js에서 named export로도 제공됩니다.


관련 자료


라이선스

Copyright 2026 Jim Montgomery
SPDX-License-Identifier: Apache-2.0

Apache License 2.0. LICENSE를 참조하세요.