first commit
This commit is contained in:
53
docs/foundation/flow/cbor/src/cwt/claims.md
Normal file
53
docs/foundation/flow/cbor/src/cwt/claims.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# `claims`
|
||||
|
||||
## Purpose
|
||||
|
||||
Encode and decode RFC 8392 CBOR Web Token claim sets. The seven IANA-
|
||||
registered claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `cti`) are
|
||||
typed; private claims keyed by IANA-numeric label survive the round-trip
|
||||
unchanged.
|
||||
|
||||
## Features
|
||||
|
||||
- `encodeCwt(claims)` — deterministic claim-set encoding so token bytes are
|
||||
byte-stable across runs.
|
||||
- `decodeCwt(bytes)` — typed `CwtClaims` output with full validation at the
|
||||
field boundaries.
|
||||
- Time fields (`exp`, `nbf`, `iat`) accept `Date` on the input side; the
|
||||
decoder produces `Date` from raw integers, bigints, or values wrapped in
|
||||
CBOR tag 1 (epoch).
|
||||
- `aud` accepts both `string` and `string[]` per RFC 8392 §3.1.
|
||||
- Private claims threaded through `claims.private` keyed by integer label
|
||||
number — opaque to the typed claims but preserved on round-trip.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { decodeCwt, encodeCwt } from "@suaveplan/cbor/cwt";
|
||||
|
||||
const wire = encodeCwt({
|
||||
iss: "https://issuer.example",
|
||||
exp: new Date("2030-01-01"),
|
||||
cti: new Uint8Array([0x01, 0x02]),
|
||||
private: { 100: "tenant-a" },
|
||||
});
|
||||
|
||||
const claims = decodeCwt(wire);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
- `encodeCwt(claims: CwtClaims): Uint8Array`
|
||||
- `decodeCwt(bytes: Uint8Array): CwtClaims`
|
||||
|
||||
Throws `CborError(UNEXPECTED_TYPE)` for malformed claim labels, wrong inner
|
||||
types, non-string `iss`/`sub`, malformed `aud`, etc.
|
||||
|
||||
## Notes
|
||||
|
||||
`exp`/`nbf`/`iat` always serialise as integer epoch seconds rather than
|
||||
`Tagged(1, …)` — both forms are valid on the wire per RFC 8392, but the
|
||||
integer form is shorter and the deterministic profile prefers shortest-form.
|
||||
Private claims must use integer labels per the CWT registry; non-integer
|
||||
keys are silently dropped on encode (the `Object.entries` walk filters
|
||||
them) — the typed `private` shape is keyed by `number`.
|
||||
51
docs/foundation/flow/cbor/src/cwt/verify.md
Normal file
51
docs/foundation/flow/cbor/src/cwt/verify.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# `verify`
|
||||
|
||||
## Purpose
|
||||
|
||||
End-to-end CWT verification: parse the COSE wrapper, verify the signature
|
||||
or MAC, decode the claim set, and check the standard time-window plus
|
||||
audience plus issuer claims. Two entry points cover the two RFC 8392
|
||||
profiles — `verifyCwtSign1` for COSE_Sign1-wrapped tokens and
|
||||
`verifyCwtMac0` for COSE_Mac0-wrapped tokens.
|
||||
|
||||
## Features
|
||||
|
||||
- Time-window enforcement honours `exp` (with optional clock-skew tolerance)
|
||||
and `nbf` per RFC 8392 §7.
|
||||
- Audience checks accept both `string` and `string[]` audience claims.
|
||||
- Issuer check is exact-match.
|
||||
- External AAD propagates through to the underlying COSE verify.
|
||||
- Distinct error codes per failure (`CWT_SIGNATURE_INVALID`, `CWT_EXPIRED`,
|
||||
`CWT_NOT_YET_VALID`, `CWT_AUDIENCE_MISMATCH`, `CWT_ISSUER_MISMATCH`) so
|
||||
callers can branch on the root cause.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { verifyCwtSign1 } from "@suaveplan/cbor/cwt";
|
||||
|
||||
const result = verifyCwtSign1(tokenBytes, publicKey, {
|
||||
audience: "my-service",
|
||||
issuer: "trusted-issuer",
|
||||
clockSkewMs: 30_000,
|
||||
});
|
||||
console.log(result.claims.sub);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
- `verifyCwtSign1(bytes, key, options?): CwtVerifyResult`
|
||||
- `verifyCwtMac0(bytes, key, options?): CwtVerifyResult`
|
||||
- `CwtVerifyOptions`: `externalAad`, `audience`, `issuer`, `nowMs`,
|
||||
`clockSkewMs`.
|
||||
- `CwtVerifyResult`: `{ valid: true; claims: CwtClaims }` — the function
|
||||
throws rather than returning `valid: false`, so the type carries no
|
||||
failure variant. Callers `try/catch` on the typed `CborErrorCode`.
|
||||
|
||||
## Notes
|
||||
|
||||
Detached payloads (a `null` payload field on the COSE wrapper) are
|
||||
explicitly rejected — RFC 8392 §1 says "The CWT structure is a CBOR
|
||||
representation of a JWT-style claims set", which presumes the claim bytes
|
||||
are carried inline. A future enhancement could accept a caller-provided
|
||||
detached payload buffer; for now the failure mode is loud.
|
||||
Reference in New Issue
Block a user