Skip to content
Security
Skill

/guide

Guide for developing Go applications with github.com/lestrrat-go/jwx v4 — parse/sign JWTs, work with JWS/JWE/JWK, pick algorithms, and avoid the common footguns. For developers using jwx, not for developing the library itself.

BOOST
From plugin
jwx
2.4k2 skills9 agents
Install
$ npx -y skills add lestrrat-go/jwx --skill guide --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/guide

Context preview

The summary Claude sees to decide when to auto-load this skill.

Guide for developing Go applications with github.com/lestrrat-go/jwx v4 — parse/sign JWTs, work with JWS/JWE/JWK, pick algorithms, and avoid the common footguns. For developers using jwx, not for developing the library itself.

SKILL.md

guide.SKILL.md
name: jwx-guide-v4
description: Guide for developing Go applications with github.com/lestrrat-go/jwx v4 — parse/sign JWTs, work with JWS/JWE/JWK, pick algorithms, and avoid the common footguns. For developers using jwx, not for developing the library itself.

jwx-guide-v4

This skill helps you assist Go developers who are **using** `github.com/lestrrat-go/jwx/v4` in their own projects. It is scoped to **v4** only. There is no equivalent skill for v3 or v2; for those, work from the version's own `docs/` directory and pkg.go.dev. Do not apply v4 rules to a v3 or v2 codebase — the `jwa` identifiers are constants there, not functions, and the `jwk` and error APIs differ.

Where to look things up

You will not have this repository checked out. To verify an API claim before answering, use these sources, in order of preference:

1. **Go module cache** — if the user's project depends on jwx, the source is on disk:

   echo "$(go env GOMODCACHE)/github.com/lestrrat-go/jwx/v4@$(go list -m -f '{{.Version}}' github.com/lestrrat-go/jwx/v4)"

This directory contains the full source tree including `docs/`, `jwt/`, `jws/`, `jwe/`, `jwk/`, `jwa/`. 2. **pkg.go.dev** — canonical API reference for any exported symbol:

  • `https://pkg.go.dev/github.com/lestrrat-go/jwx/v4`
  • `https://pkg.go.dev/github.com/lestrrat-go/jwx/v4/jwt` (etc. per subpackage)

3. **GitHub** — narrative documentation lives at the repo:

  • `https://github.com/lestrrat-go/jwx/blob/develop/v4/docs/01-jwt.md`
  • `https://github.com/lestrrat-go/jwx/blob/develop/v4/docs/02-jws.md`
  • `https://github.com/lestrrat-go/jwx/blob/develop/v4/docs/03-jwe.md`
  • `https://github.com/lestrrat-go/jwx/blob/develop/v4/docs/04-jwk.md`
  • `https://github.com/lestrrat-go/jwx/blob/develop/v4/docs/10-extensions.md`
  • `https://github.com/lestrrat-go/jwx/blob/develop/v4/docs/99-faq.md`

4. **Examples repo** — runnable usage patterns. The repo's `README.md` is a topical index that maps "what do I want to do" → "which `*_example_test.go` file." Fetch the README first when looking for an example by topic; then fetch the linked file:

  • `https://github.com/jwx-go/examples/blob/develop/v4/README.md`

When the user's question goes beyond the patterns in this skill (custom claim types, JWE recipients with per-recipient headers, custom key providers, base64 backend swap, performance tuning), fetch from these sources rather than guessing.

Prerequisites

  • **Go 1.26+** is required. v4 uses generics features and stdlib additions (`errors.AsType[T]`) that are new in 1.26.
  • **`GOEXPERIMENT=jsonv2`** must be set on Go 1.26 for every `go build`/`go test`/`go run`, because v4 depends on `encoding/json/v2`. Without it → builds fail with `build constraints exclude all Go files`. NEVER set it on Go 1.27+ → `encoding/json/v2` is in the standard library there, and naming the experiment rebuilds the standard library under a non-default configuration.

Sub-package map:

| Package | Role | |---------|------| | `jwa` | Algorithm identifiers as **functions**: `jwa.RS256()`, `jwa.ES256()`, `jwa.HS256()`, `jwa.A256GCM()`, `jwa.RSA_OAEP_256()`, `jwa.EdDSAEd25519()`, etc. | | `jwk` | JSON Web Keys: parsing, generating, import/export between `jwk.Key` and `crypto.*` keys, key sets. | | `jws` | Sign and verify arbitrary payloads (compact or JSON serialization). | | `jwe` | Encrypt and decrypt arbitrary payloads. | | `jwt` | JWT tokens — claims, signing, verification + validation. Wraps `jws`. | | `jwt/openid` | OpenID Connect ID-token-flavored claims. |

Critical rules (read first)

1. **`jwt.Parse` verifies AND validates by default.** A bare `jwt.Parse(data)` errors because no key was supplied. To intentionally skip both, use `jwt.ParseInsecure`. To verify but skip claim validation, pass `jwt.WithValidate(false)`. This is deliberately asymmetric vs. `jws.Parse`/`jwe.Parse` (which only parse). Do not "correct" it. 2. **Always pin the algorithm on the verify side.** `jwt.WithKey(jwa.RS256(), key)`, `jws.WithKey(jwa.ES256(), key)`. Never trust the `alg` from the incoming header alone. 3. **Never use `jwt.ParseInsecure` for tokens received from the network.** It is for testing or for extracting claims from a token whose origin is already trusted by other means. 4. **`jwa` algorithms are functions in v4, not constants.** Write `jwa.RS256()`, not `jwa.RS256`. This trips up users migrating from v2/v3. 5. **`kid` matching is enforced when verifying with a JWK Set.** If a token omits `kid` and the set contains exactly one key, use `jwt.WithKeySet(set, jws.WithUseDefault(true))`. Use `jws.WithRequireKid(false)` only when verification must consider multiple keys without matching `kid` values. 6. **`jku` (key URL in the JWS header) is attacker-controlled.** Use `jwt.WithVerifyAuto` only with a `jwkfetch.Client` configured with a `jwkfetch.NewMapWhitelist()` of allowed URLs. 7. **HMAC keys are `[]byte`, not `string`.** Pass `[]byte("secret")`, or better, a `jwk.Key` imported from those bytes. 8. **`jwk.Import` and `jwk.Export` require explicit type parameters.** `jwk.Import[jwk.Key](raw)`, `jwk.Export[*rsa.PublicKey](key)`. Their type argument is not inferable from the call, so bare `jwk.Import(raw)` does **not** compile. 9. **`jwk.ParseKey` is not generic.** `jwk.ParseKey(data)` returns `(jwk.Key, error)`. Use `jwk.ParseKeyAs[jwk.RSAPublicKey](data)` when a concrete JWK type is required.

Verifying a JWT (the 90% case)

import (
    "github.com/lestrrat-go/jwx/v4/jwa"
    "github.com/lestrrat-go/jwx/v4/jwt"
)

tok, err := jwt.Parse(raw, jwt.WithKey(jwa.RS256(), publicKey))
if err != nil {
    // signature failed, claim validation failed, or parse failed
    return err
}
// tok is verified + validated; safe to read claims

`publicKey` may be:

  • a `*rsa.PublicKey` / `*ecdsa.PublicKey` / `ed25519.PublicKey` from `crypto/*`
  • `[]byte` for HMAC algorithms
  • a `jwk.Key`

To validate against expected claim values, pass `jwt.WithIssuer(

Read more
Ships withjwx

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.

Get the whole plugin
Stats
2,440
Stars
197
Forks
Active
Maintenance
Go
Language
MIT
License
1d ago
Last commit
10y ago
Created
4h ago
Added

Repo: lestrrat-go/jwx

Other skills on jwx.