github.com/lestrrat-go/jwx/v4

Go module implementing various JWx (JWA/JWE/JWK/JWS/JWT, otherwise known as JOSE) technologies.
If you are using this module in your product or your company, please add your product and/or company name in the Wiki! It really helps keeping up our motivation.
Requirements
- Go 1.26 or later
GOEXPERIMENT=jsonv2
Install
go get github.com/lestrrat-go/jwx/v4
Claude Code Skill
If you use Claude Code, install the bundled jwx-dev-v4 skill so the assistant can guide you through using this library — picking algorithms, parsing/signing JWTs, working with JWS/JWE/JWK, and avoiding common footguns:
/plugin marketplace add lestrrat-go/jwx
/plugin install jwx-dev-v4
The skill is scoped to v4 only. It is intended for developers using jwx, not for working on the library itself.
Migrating from v3
If you are migrating from github.com/lestrrat-go/jwx/v3, see MIGRATION.md for a step-by-step guide with before/after code examples. For a complete list of breaking changes and new features, see Changes-v4.md.
Features
| Feature | Description |
|---|
| Complete JWA/JWE/JWK/JWS/JWT coverage | Not just JWT + minimum tool set. Supports JWS messages with multiple signatures (compact and JSON serialization), JWS with detached payload, JWS with unencoded payload (RFC 7797), JWE messages with multiple recipients (compact and JSON serialization). Most operations work with either JWK or raw keys (e.g. *rsa.PrivateKey, *ecdsa.PrivateKey). |
| Opinionated, uniform API | Everything is symmetric and follows a standard convention: jws.Parse/Verify/Sign, jwe.Parse/Encrypt/Decrypt. Arguments are organized as explicit required parameters and optional WithXXXX() style options. |
| Post-quantum cryptography | Supports ML-KEM, ML-DSA, and HPKE. |
| Extension module architecture | Opt-in features via extension modules. See Extension Modules. |
| JWK Caching | jwkcache extension to always keep a JWKS up-to-date. |
| Bazel Support | Bazel-ready. |
SYNOPSIS
package examples_test
import (
"bytes"
"fmt"
"net/http"
"time"
"github.com/lestrrat-go/jwx/v4/jwa"
"github.com/lestrrat-go/jwx/v4/jwe"
"github.com/lestrrat-go/jwx/v4/jwk"
"github.com/lestrrat-go/jwx/v4/jws"
"github.com/lestrrat-go/jwx/v4/jwt"
)
func Example() {
// Parse, serialize, slice and dice JWKs!
privkey, err := jwk.ParseKey(jsonRSAPrivateKey)
if err != nil {
fmt.Printf("failed to parse JWK: %s\n", err)
return
}
pubkey, err := jwk.PublicKeyOf(privkey)
if err != nil {
fmt.Printf("failed to get public key: %s\n", err)
return
}
// Work with JWTs!
{
// Build a JWT!
tok, err := jwt.NewBuilder().
Issuer(`github.com/lestrrat-go/jwx`).
IssuedAt(time.Now()).
Build()
if err != nil {
fmt.Printf("failed to build token: %s\n", err)
return
}
// Sign a JWT!
signed, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256(), privkey))
if err != nil {
fmt.Printf("failed to sign token: %s\n", err)
return
}
// Verify a JWT!
{
verifiedToken, err := jwt.Parse(signed, jwt.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify JWS: %s\n", err)
return
}
_ = verifiedToken
}
// Work with *http.Request!
{
req, _ := http.NewRequest(http.MethodGet, `https://github.com/lestrrat-go/jwx`, nil)
req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, signed))
verifiedToken, err := jwt.ParseRequest(req, jwt.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify token from HTTP request: %s\n", err)
return
}
_ = verifiedToken
}
}
// Encrypt and Decrypt arbitrary payload with JWE!
{
encrypted, err := jwe.Encrypt(payloadLoremIpsum, jwe.WithKey(jwa.RSA_OAEP_256(), jwkRSAPublicKey))
if err != nil {
fmt.Printf("failed to encrypt payload: %s\n", err)
return
}
decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP_256(), jwkRSAPrivateKey))
if err != nil {
fmt.Printf("failed to decrypt payload: %s\n", err)
return
}
if !bytes.Equal(decrypted, payloadLoremIpsum) {
fmt.Printf("verified payload did not match\n")
return
}
}