JSON web tokens in OCaml

JWT and CWT Implementation TODO#

RFC 7519 (JWT) and RFC 8392 (CWT) compliance tracking for jsonwt.

Implementation Status#

JSON Web Token (JWT) - RFC 7519#

  • Type definitions for all registered claims
  • Type definitions for JOSE header parameters
  • Type definitions for algorithms
  • Base64url encoding/decoding
  • Basic JWT structure parsing (3-part split)
  • JSON parsing with jsont
  • Signature creation and verification (HMAC, ECDSA, EdDSA)
  • Claims validation
  • JWK parsing and serialization
  • Structured error types
  • Comprehensive tests (30 tests passing)

CBOR Web Token (CWT) - RFC 8392#

  • Type definitions for COSE algorithms (HMAC, ECDSA, EdDSA)
  • Type definitions for COSE keys (Symmetric, Ec2, Okp)
  • Claims with integer keys (1=iss, 2=sub, 3=aud, 4=exp, 5=nbf, 6=iat, 7=cti)
  • CBOR encoding via cbort library
  • COSE_Mac0 structure (MACed CWT)
  • COSE_Sign1 structure (Signed CWT)
  • Signature creation and verification (HMAC, ECDSA, EdDSA)
  • Claims validation (same as JWT)
  • Structured error types
  • Comprehensive tests (28 tests passing)

CBOR Codec - RFC 8949#

  • Low-level CBOR encoding primitives (Cbor_rw)
  • Major type constants and encoding functions
  • Integer encoding (positive and negative)
  • Float encoding (IEEE 754 double precision)
  • Text string and byte string encoding
  • Array and map encoding
  • Tag encoding
  • Simple value encoding (false, true, null, undefined)
  • Comprehensive tests with RFC 8949 Appendix A vectors (46 tests passing)

Completed Phases#

Phase 0: Error Types and Core Infrastructure - DONE#

  • Structured error type with all RFC-compliant variants
  • pp_error and error_to_string functions
  • StringOrURI validation (validates URIs per RFC 3986)

Phase 1: JSON Parsing with jsont - DONE#

  • Header JSON codec (Header.of_json, Header.to_json)
  • Claims JSON codec with strict mode for duplicate detection
  • JWK JSON codec for all key types (Oct, RSA, EC, OKP)

Phase 2: Signature Operations - PARTIALLY DONE#

  • HMAC Signatures (HS256, HS384, HS512) - using digestif
  • ECDSA Signatures (ES256, ES384, ES512) - using mirage-crypto-ec
  • EdDSA Signatures (Ed25519) - using mirage-crypto-ec
  • Unsecured JWT ("none") - with explicit ~allow_none:true opt-in
  • Nested JWT Support - parse_nested with max_depth protection
  • RSA Signatures (RS256, RS384, RS512) - STUBBED, needs JWK-to-RSA key parsing

Phase 3: Claims Validation - DONE#

  • Time-based claims (exp, nbf) with leeway support
  • Issuer validation (iss)
  • Audience validation (aud)
  • is_expired helper function
  • time_to_expiry helper function

Phase 4: Full JWT Creation Flow - DONE#

  • create function for signing JWTs
  • Algorithm/key type validation

Phase 5: Tests - DONE (104 tests passing)#

JWT Tests (30 tests)#

RFC Test Vectors:

  • RFC 7519 Section 3.1 HS256 JWT
  • RFC 7519 Section 6.1 Unsecured JWT

Algorithm Coverage:

  • HS256 sign/verify
  • HS384 sign/verify
  • HS512 sign/verify
  • RS256 sign/verify (stubbed)
  • RS384 sign/verify (stubbed)
  • RS512 sign/verify (stubbed)
  • ES256 sign/verify
  • ES384 sign/verify
  • ES512 sign/verify
  • EdDSA sign/verify
  • none (unsecured) with opt-in

Validation Tests:

  • Expired token rejection
  • Not-yet-valid token rejection
  • Issuer mismatch rejection
  • Audience mismatch rejection
  • Leeway handling

Error Cases:

  • Invalid base64url
  • Invalid JSON
  • Wrong number of parts
  • Signature mismatch
  • Algorithm not in allowed list
  • Unsecured JWT without allow_none

CWT Tests (28 tests)#

RFC Test Vectors:

  • RFC 8392 Appendix A claims timestamps
  • RFC 8392 example values (hex test vectors)

Algorithm Coverage:

  • HMAC_256_64 (alg=4)
  • HMAC_256 (alg=5)
  • HMAC_384 (alg=6)
  • HMAC_512 (alg=7)
  • ES256 (alg=-7)
  • ES384 (alg=-35)
  • ES512 (alg=-36)
  • EdDSA (alg=-8)

COSE Key Tests:

  • Symmetric key creation
  • Ed25519 key creation
  • P-256 key creation
  • Key ID (kid) support

Claims Tests:

  • Claims builder
  • Timestamp claims (exp, nbf, iat)
  • Single and multiple audience
  • CWT ID (cti)
  • CBOR serialization

Validation Tests:

  • Expired token rejection
  • Not-yet-valid token rejection
  • Issuer validation
  • Audience validation
  • Leeway handling

CBOR Tests (46 tests)#

RFC 8949 Appendix A Test Vectors:

  • Unsigned integers (0-1000000000000)
  • Negative integers (-1 to -1000)
  • Booleans and null
  • Floats (1.0, 1.1, -4.1, 1.0e+300, Infinity, NaN)
  • Text strings (empty, ASCII, UTF-8 with Unicode)
  • Byte strings
  • Arrays (empty, nested, 25 items)
  • Maps (empty, int keys, string keys, nested)
  • Tags (epoch timestamp)
  • Constants (major types, simple values, additional info)

Remaining Work#

JWT: RSA Signatures (RS256, RS384, RS512)#

Status: Stubbed - returns Key_type_mismatch "RSA signing/verification not yet implemented"

Required:

  1. Implement JWK-to-RSA key parsing for n, e (public) and d, p, q, dp, dq, qi (private) fields
  2. Use mirage-crypto-pk for RSASSA-PKCS1-v1_5 signatures
  3. Add tests with RFC test vectors

CWT: CBOR Decoding#

Status: Not implemented - current implementation only encodes CWTs

Required:

  1. Add CBOR decoding functions to Cbor_rw module
  2. Implement Cwt.parse to decode CWT from CBOR bytes
  3. Add tests with RFC 8392 Appendix A encoded test vectors

CWT: COSE Key Encoding/Decoding#

Status: Keys are created in memory but not serialized

Required:

  1. Add Cose_key.to_cbor and Cose_key.of_cbor functions
  2. Follow RFC 9052 Section 7 (COSE Key) format
  3. Add tests with RFC test vectors

CWT: CWT Tag Support#

Status: Partial - encodes COSE structures but not outer CWT tag

Required:

  1. Add support for CWT tag (61) wrapping per RFC 8392 Section 2
  2. Add support for optional outer COSE tag per RFC 9052

Future Work (Not in Current Scope)#

  1. JWK Set (JWKS): RFC 7517 Section 5 support for multiple keys

    • Useful for key rotation and fetching keys from JWKS endpoints
    • Example: /.well-known/jwks.json
    • Consider adding Jwks.t type and Jwks.find_key : kid:string -> Jwks.t -> Jwk.t option
  2. JWE Support: RFC 7516 JSON Web Encryption

    • Required for encrypted JWTs (as opposed to signed JWTs)
    • Lower priority unless needed for specific use cases

Design Decisions (Implemented)#

  1. StringOrURI validation: YES - Validates that iss/sub values containing ":" are valid URIs per RFC 3986.

  2. Duplicate claims: STRICT MODE - Rejects JWTs with duplicate claim names by default. ~strict:false allows lenient parsing.

  3. "none" algorithm: REQUIRE OPT-IN - ~allow_none:bool parameter to verify (default false). Unsecured JWTs rejected unless explicitly allowed.

  4. Error types: STRUCTURED - Proper error variant type for pattern matching and error handling.

  5. Algorithm allowlist: YES - ~allowed_algs parameter to verify, defaulting to all algorithms (except none).

  6. Clock source: EXPLICIT - Always requires ~now:Ptime.t parameter. No implicit system clock usage.

  7. Nested JWTs: YES - Support via parse_nested with ~max_depth protection (default 2).


File Summary#

File Lines Description
lib/jsonwt.ml ~1010 JWT implementation
lib/jsonwt.mli ~480 JWT interface with RFC documentation
lib/cwt.ml ~760 CWT implementation
lib/cwt.mli ~400 CWT interface with RFC documentation
cbort/cbor_rw.ml ~200 Low-level CBOR encoding primitives
cbort/cbor_rw.mli ~200 CBOR encoding interface
cbort/cbort.ml ~300 CBOR codec for jsont types
cbort/cbort.mli ~100 CBOR codec interface
test/test_jsonwt.ml ~440 30 JWT tests
test/test_cwt.ml ~500 28 CWT tests
test/test_cbor.ml ~320 46 CBOR encoding tests

References#

JWT (JSON Web Token)#

CWT (CBOR Web Token)#

  • RFC 8392 - CBOR Web Token (CWT)
  • RFC 9052 - CBOR Object Signing and Encryption (COSE) Structures
  • RFC 9053 - CBOR Object Signing and Encryption (COSE) Algorithms
  • RFC 8949 - Concise Binary Object Representation (CBOR)