jwx
A complete, opinionated Go implementation of the JOSE stack: JWT, JWS, JWE, JWK, and JWA.
Repository Health
Technical Analysis
jwx is a Go module implementing the full family of JOSE (Javascript Object Signing and Encryption) technologies: JWA, JWE, JWK, JWS, and JWT. Rather than shipping a minimal JWT-only helper, it covers the wider spec surface, including JWS messages with multiple signatures, detached and unencoded payloads, and JWE messages with multiple recipients, in both compact and JSON serialization forms.
The API follows a consistent, symmetric convention across packages, jws.Parse/Verify/Sign, jwe.Parse/Encrypt/Decrypt, jwt.Parse/Sign, with explicit required parameters and optional WithXXXX()-style functional options built on lestrrat-go/option. Most operations accept either a jwk.Key or a raw Go crypto key (*rsa.PrivateKey, *ecdsa.PrivateKey, etc.), so teams already holding standard-library key types are not forced through a conversion step.
Beyond the core RFCs, jwx has been extended to cover post-quantum primitives (ML-KEM, ML-DSA, HPKE) and ships an opt-in extension-module architecture, letting the core module stay lean while companion repositories under the jwx-go GitHub org add niche algorithms (ES256K, Ed448, JWKS caching, migration tooling) without bloating the main dependency tree.
What You Get
- Full JWA/JWE/JWK/JWS/JWT coverage, not just a minimal JWT parser: multi-signature and multi-recipient JOSE messages, detached payloads, and RFC 7797 unencoded payloads
- A uniform, opinionated API surface (
Parse/Verify/Sign,Parse/Encrypt/Decrypt) with functionalWithXXXX()options instead of ad-hoc config structs - Interoperability with both
jwk.Keyand raw standard-library crypto keys (*rsa.PrivateKey,*ecdsa.PrivateKey) across signing, verification, and encryption calls - Post-quantum cryptography support (ML-KEM, ML-DSA, HPKE) alongside classical RSA/ECDSA/EdDSA/HMAC algorithms
- An opt-in extension-module system (companion repos for JWKS caching, ES256K, Ed448, migration helpers) that keeps the core module’s dependency footprint small
- First-class HTTP integration via
jwt.ParseRequestfor pulling and verifying bearer tokens straight off an*http.Request - Bazel build support (
BUILD.bazel,MODULE.bazel) alongside standardgo build/go testworkflows
Common Use Cases
- Issuing and verifying signed JWT access/ID tokens for an API or auth server, including OpenID Connect claims via the
jwt/openidsubpackage - Encrypting sensitive payloads (not just tokens) with JWE for secure transport between services
- Parsing and rotating JWKS (JSON Web Key Sets) fetched from an identity provider, with the
jwkfetchcompanion module keeping a key set continuously up to date - Verifying bearer tokens directly from incoming HTTP requests in a middleware layer via
jwt.ParseRequest - Building systems that need forward-looking, post-quantum-safe signing or key-encapsulation alongside today’s classical algorithms
Under The Hood
Architecture
jwx is organized as one package per JOSE specification, jwa (algorithm identifiers), jwk (key representation and conversion), jws (signing/verification), jwe (encryption), and jwt (token building/parsing), each independently importable, with shared low-level machinery (base64 handling, a pluggable JSON codec, object pooling, key-type conversion) centralized under internal/. The top-level jwx.go file documents module-wide design decisions, such as staying buffer-oriented rather than adding true streaming support, showing the maintainers reason explicitly about API-wide tradeoffs rather than letting each package diverge. Extension algorithms (ES256K, Ed448, ML-KEM/ML-DSA/HPKE variants) live in separate companion repositories tracked via companions.yaml, keeping the core module’s own dependency graph small while still allowing the ecosystem to grow.
Tech Stack
Built for Go 1.26+, jwx has adopted the still-experimental encoding/json/v2 API (gated behind GOEXPERIMENT=jsonv2 on 1.26, standard by 1.27), wrapped behind an internal JSON abstraction so the rest of the codebase isn’t coupled to a specific encoder. External dependencies are deliberately minimal: lestrrat-go/dsig and lestrrat-go/option from the same maintainer’s ecosystem, valyala/fastjson, and golang.org/x/crypto, with stretchr/testify reserved for tests. The project supports both standard go build and Bazel (MODULE.bazel, per-package BUILD.bazel files), and leans on code generation (go:generate directives driving jwxcodegen scripts and stringer) to keep accessors and option types consistent across packages instead of hand-maintaining boilerplate.
Code Quality
Testing is extensive and layered: roughly 124 test files against about 190 non-test source files, including fuzz tests in multiple packages (fuzz_test.go in jwk, jws, jwx itself) backed by a dedicated fuzz.yml GitHub Actions workflow, plus benchmark tests, CodeQL static analysis, a separate lint workflow, a smoke-test workflow, and a companion-tests.yml workflow that verifies the extension modules still build against the core. Errors are handled through per-package errors.go files defining typed error values rather than bare string errors, and comment density in core files is high (100+ doc comments per file in jwt.go, jwk.go, jws.go), indicating the API surface is documented close to the code rather than only in external docs.
API Design
The library’s defining choice is symmetry: every JOSE operation follows the same Parse/verb/WithXXXX()-option shape, so learning jws.Sign largely transfers to learning jwe.Encrypt or jwt.Sign. It ships a bundled Claude Code skill (jwx-dev-v4) specifically to help AI coding assistants pick the right algorithm and avoid common JOSE footguns, an unusually deliberate investment in developer experience for a cryptography library. A dedicated MIGRATION.md and versioned Changes-vN.md files give users a concrete upgrade path across the v2/v3/v4 major-version boundaries, which matters for a module whose import path changes on every semver-major bump under Go’s module versioning rules.