@owf/token-status-list
Implementation of Token Status List (draft-ietf-oauth-status-list)
with core bitstring handling, JWT transport, and CWT/CBOR transport — all in one package.
Features
- Core StatusList: Variable bit-width (1/2/4/8-bit) status entries with deflate compression
- Random index assignment: Deterministic, non-repeating index allocation from a private seed
- JWT transport: Create and decode Status List Tokens in JWT format
- CWT transport: Create and decode Status List Tokens in CWT/CBOR format
- Referenced Token support: Extract status claims from both JWT and CWT referenced tokens
- COSE algorithm constants: Standard IANA COSE algorithm identifiers
Installation
npm install @owf/token-status-list
Usage
Core — create and query a status list
import { StatusList, StatusTypes } from '@owf/token-status-list'
const list = new StatusList(new Array(100_000).fill(0), 1)
list.setStatus(42, StatusTypes.INVALID)
list.setStatus(99, StatusTypes.INVALID)
list.getStatus(42)
list.getStatus(0)
const compressed = list.compressStatusListToBytes()
const restored = StatusList.decompressStatusListFromBytes(compressed, 1)
Randomly assign status-list indices
Use a private, persisted seed to assign each index exactly once in a deterministic random order:
import { hasher } from '@owf/crypto'
import { StatusListIndexAllocator, createStatusListIndexAllocator } from '@owf/token-status-list'
const ctx = { hasher }
const allocator = await createStatusListIndexAllocator({ length: 1_000_000, seed }, ctx)
const index = allocator.next()
const state = allocator.getState()
const resumed = await StatusListIndexAllocator.create(state, ctx)
The seed must remain private to prevent predicting future assignments. Persist the allocator state
and advance it atomically when issuing tokens; database transactions are the caller's responsibility.
Known limitation
The compressed status-list format stores statuses in whole bytes, but does not
store the original number of status entries. For example, three 1-bit entries
are encoded in one byte, so decompressing that byte produces eight byte-aligned
entries. The first three status values are preserved, but the original list
length cannot be reconstructed from bits and lst alone.
This is allowed by the specification: Section 4.1 permits the byte array to be
the size of the number of Referenced Tokens multiplied by bits divided by 8,
or greater. Section 13.4 recommends byte-aligned list sizes but does not require
them. Callers that need the original logical length must preserve it separately.
JWT transport — issue and read a Status List Token
import { StatusList, createHeaderAndPayload, getListFromStatusListJWT } from '@owf/token-status-list'
const list = new StatusList(new Array(100_000).fill(0), 1)
list.setStatus(42, 1)
const { header, payload } = createHeaderAndPayload(
list,
{ iss: 'https://issuer.example', sub: 'https://issuer.example/statuslists/1', iat: Math.floor(Date.now() / 1000) },
{ alg: 'ES256', typ: '' },
)
const decoded = getListFromStatusListJWT(jwt)
decoded.getStatus(42)
JWT transport — read a referenced token's status entry
import { getStatusListFromJWT } from '@owf/token-status-list'
const entry = getStatusListFromJWT(referencedTokenJWT)
CWT transport — issue and read a Status List Token
import {
StatusList,
COSEAlgorithms,
createStatusListCWTPayload,
createStatusListCWTHeader,
encodeCWTPayload,
decodeCWTPayload,
} from '@owf/token-status-list'
const list = new StatusList(new Array(100_000).fill(0), 1)
list.setStatus(42, 1)
const payload = createStatusListCWTPayload(list, 'https://issuer.example/statuslists/1', Math.floor(Date.now() / 1000))
const header = createStatusListCWTHeader(COSEAlgorithms.ES256, 'key-id-1')
const cborPayload = encodeCWTPayload(list, 'https://issuer.example/statuslists/1', Math.floor(Date.now() / 1000))
const { subject, issuedAt, statusList } = decodeCWTPayload(cborPayload)
statusList.getStatus(42)
CWT transport — referenced token status claim
import { createCWTStatusClaim, encodeCWTStatusClaim, decodeCWTStatusClaim } from '@owf/token-status-list'
const claim = createCWTStatusClaim(42, 'https://issuer.example/statuslists/1')
const encoded = encodeCWTStatusClaim(42, 'https://issuer.example/statuslists/1')
const decoded = decodeCWTStatusClaim(encoded)
API
Core
StatusList | Main class — construct, get/set status, compress/decompress |
createStatusListIndexAllocator | Create a seeded allocator that returns each status-list index once |
StatusListIndexAllocator | Allocator class with next(), remaining(), and getState() |
StatusListIndexAllocatorState | Restorable allocator state type |
SLException | Error class thrown by status-list operations |
StatusTypes | Constants: VALID, INVALID, SUSPENDED, etc. |
MediaTypes | MIME types for JWT and CWT status list tokens |
BitsPerStatus | Type: 1 | 2 | 4 | 8 |
StatusListEntry | Type: { idx: number; uri: string } |
JWT Transport
createHeaderAndPayload | Build JWT header + payload from a StatusList |
getListFromStatusListJWT | Decode a StatusList from a JWT string |
getStatusListFromJWT | Extract StatusListEntry from a referenced token JWT |
JWT_STATUS_LIST_TYPE | "statuslist+jwt" |
JWTClaimNames | Claim name constants |
CWT Transport
createStatusListCWTPayload | Build CWT claims from a StatusList |
createStatusListCWTHeader | Build COSE protected header |
encodeCWTPayload / decodeCWTPayload | CBOR encode/decode full payload |
encodeStatusListToCBOR / decodeStatusListFromCBOR | CBOR encode/decode the status list only |
createCWTStatusClaim | Build a referenced-token status claim object |
encodeCWTStatusClaim / decodeCWTStatusClaim | CBOR encode/decode a status claim |
getListFromStatusListCWT | Extract StatusList from CWT payload bytes |
getStatusListFromCWT | Extract StatusListEntry from CWT payload bytes |
COSEAlgorithms | IANA COSE algorithm identifiers (ES256, ES384, EdDSA, …) |
CWT_STATUS_LIST_TYPE | "application/statuslist+cwt" |
CWTClaimKeys / CWTStatusListKeys / CWTStatusListInfoKeys / COSEHeaderKeys | Numeric/string key constants |