# jwtauth: two Chi middlewares that verify a JWT and put it in the request context

> go-chi/jwtauth splits JWT handling into a Verifier that decodes and validates a token and an Authenticator that turns a failed check into a 401, which lets you replace only the part you need to change in a Go service.

**go-chi/jwtauth** — JWT authentication middleware for Go HTTP services

- Repository: https://github.com/go-chi/jwtauth
- Stars: 651 · Forks: 95
- Language: Go
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/go-chi-jwtauth

## A router agnostic middleware that lives in the Chi group

jwtauth is a package of HTTP middleware for Go services that verifies a JWT token from a request and sends the result down the request context. The README is explicit that it works with any Go HTTP router, and that it resides under the go-chi group for maintenance and organization rather than technical necessity. Its only third party dependency is the underlying JWT library, github.com/lestrrat-go/jwx, and the module file confirms a short dependency list: Chi v5 for the example, jwx v3, and a handful of indirect packages pulled in by jwx itself, including crypto and secp256k1 curve support and a JSON implementation. The audience is a Go team that has already chosen Chi and wants authentication to be a line in the middleware chain rather than a helper invoked at the top of every handler. The licence is MIT and the package is a single file, jwtauth.go, with a matching test file and an example application under _example/.

## Verifier and Authenticator are two separate middlewares

The design decision that makes this package useful is the split. The README describes a complete JWT flow as capturing the token from the request, decoding it, verifying it, and validating that it is correctly signed and has not expired, and says the jwtauth.Verifier middleware handler takes care of all of that. The Verifier sets two values on the request context, on the keys jwtauth.TokenCtxKey and jwtauth.ErrorCtxKey. It always calls the next handler in sequence, which means a failed verification does not stop the chain. Handling the failure is a separate job for the jwtauth.Authenticator, which responds with a 401 Unauthorized plain text payload for all unverified tokens and passes good ones through. The README points out that you can copy the Authenticator and customise it, for example to return a JSON error body, so the default behaviour can be replaced without reimplementing the verification step.

## The token is looked up in a bearer header, then in a jwt cookie

By default the Verifier searches a request in a fixed order, and the order matters for browser clients that send both. First the Authorization header in the form BEARER T, then a cookie named jwt. The first JWT string found in either place is decoded by the lestrrat-go/jwx library, and a jwt.Token object is placed on the request context. If the signature decoding fails, the Verifier also sets the error on the context, so the two are read together by the next handler. The lookup is configurable, and the README says custom verification sequences are supported by using the Verify middleware instantiator directly, with the default Verifier instantiated by calling Verify(ja, TokenFromHeader, TokenFromCookie). That is the extension point: you decide where the token comes from, and the rest of the flow is unchanged. The README does not document token revocation, key rotation, or audience and issuer validation.

## A complete Chi server with a protected and a public group

The example is the quickest way to see the wiring, and it is small enough to read in one pass. The JWT manager is created with an algorithm, a secret and a nil options argument, and the same object encodes a sample token at start-up so you can see the shape of a token. The router then uses a Chi group for protected routes, where the Verifier and the Authenticator are both registered with Use, and a separate group for the public route. Inside the protected handler the claims are pulled back out of the context.

```go
import (
	"fmt"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/jwtauth/v5"
)
```

The two middleware calls are the whole mechanism, and the README comments make the same point in prose: the Verifier seeks, verifies and validates the token, and the Authenticator handles the valid and invalid cases, with a note that you can write your own instead by looking at the Authenticator method in jwtauth.go.

```go
		r.Use(jwtauth.Verifier(tokenAuth))
```

```go
		r.Use(jwtauth.Authenticator(tokenAuth))
```

Claims come back out of the context with jwtauth.FromContext, and the example handler formats the user_id claim straight into the response body, which is the pattern to copy when you need identity in the handler.

## Generating tokens is a separate tool, jwtutil

jwtauth verifies tokens, it does not issue them, and the README points to a separate utility for that, jwtutil from the goware project, installed with a plain go install of github.com/goware/jwtutil. The usage is a single command with a secret, an encode flag and a claims payload, and the README shows the resulting token so you can see the three segment structure of a JWT.
That separation is a fair division of labour for a middleware package, but it means an adopting team has two dependencies to track rather than one, and the tool lives in a different organisation. The jwtauth package itself does expose an Encode method, since the example calls it to produce the debug token, so a server that issues its own tokens does not strictly need jwtutil.

## Dependency footprint, Go version and release cadence

The module file is the most useful thing in the repository for anyone deciding on this package. The module path is github.com/go-chi/jwtauth/v5, the language version is Go 1.23.0 with a toolchain directive of go1.24.2, and the direct requirements are Chi v5.2.1 and lestrrat-go/jwx/v3 v3.0.2. The major version suffix on the jwx import is worth noting, and the README still refers to the library by its unversioned name. The Makefile shows the development loop, with a build target, a test target that runs the suite with the race detector under GOGC=off after clearing the test cache, a bench target with a 25 minute timeout, and a todo target that greps for TODO comments. The release history is v5.1.0 in 2022, v5.3.3 in March 2025, and v5.4.0 in February 2026, and the last push to the repository was on 2026-07-10.

## Where jwtauth is the wrong choice

There are clear cases for looking elsewhere. If your service needs to issue tokens with a signing key that rotates, or needs to validate a token's audience or issuer, the README documents no such support, and you would be writing that logic yourself on top of the middleware. If you want a single dependency that both mints and verifies, jwtutil plus jwtauth is two modules rather than one, and jwtutil belongs to a different project. If your team is not on Chi, note that the package still works with any Go router, but the example and the maintenance arrangement are Chi's, and the go-chi group is an organisation rather than a vendor.

## Conclusion

Adopt jwtauth if you run a Go HTTP service on Chi and want token verification that is a middleware rather than a block of code in every handler, with the freedom to write your own error response. Do not adopt it if you need a library that also issues tokens, handles key rotation, or validates audience and issuer claims, since the README documents none of those. Verify first that the token lookup order, bearer header then jwt cookie, matches the clients you already have, and check which jwx major version your code imports.

## FAQ

### How does the go-chi jwtauth middleware find a JWT token?

By default the Verifier searches the Authorization header in the form BEARER T, then a cookie named jwt, and decodes the first one it finds. You can change that sequence by calling the Verify instantiator yourself with your own token extractors.

### What is the difference between jwtauth.Verifier and jwtauth.Authenticator?

The Verifier seeks, verifies and validates the token and sets the token and error values on the request context, then always calls the next handler. The Authenticator is the middleware that responds with a 401 Unauthorized plain text payload for unverified tokens, and you can copy it to customise the error response.

### How do I generate a JWT token to test with jwtauth?

The README points to the jwtutil utility from the goware project, installed with go install github.com/goware/jwtutil and run as jwtutil -secret=secret -encode -claims='{"user_id":111}'. The jwtauth package also exposes an Encode method, which the example uses to print a sample token.

### What are the dependencies and Go version for go-chi/jwtauth?

The go.mod file declares the module github.com/go-chi/jwtauth/v5, Go 1.23.0 with a go1.24.2 toolchain, and direct requirements on github.com/go-chi/chi/v5 v5.2.1 and github.com/lestrrat-go/jwx/v3 v3.0.2. The README notes that the only third party dependency of the middleware itself is the jwx library.

## Sources

- [go-chi/jwtauth on GitHub](https://github.com/go-chi/jwtauth)
- [Issues](https://github.com/go-chi/jwtauth/issues)
- [License: MIT](https://github.com/go-chi/jwtauth/blob/master/LICENSE)
- [README](https://github.com/go-chi/jwtauth/blob/master/README.md)
- [Releases](https://github.com/go-chi/jwtauth/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/go-chi-jwtauth
