go-jwt - Documentation
July 13, 2026 · View on GitHub
Back to README
Prerequisites
- Go 1.24 or higher
- Redis server
Installation
Using go get
go get github.com/pardnchiu/go-jwt
From Source
git clone https://github.com/pardnchiu/go-jwt.git
cd go-jwt
go build ./...
Configuration
Config Structure
| Field | Type | Required | Description |
|---|---|---|---|
Redis | Redis | Yes | Redis connection settings |
File | *File | No | PEM key file paths |
Option | *Option | No | Token parameter tuning |
Cookie | *Cookie | No | Cookie attribute settings |
CheckAuth | func(Auth) (bool, error) | No | Custom user validation callback |
Redis Settings
| Field | Type | Required | Description |
|---|---|---|---|
Host | string | Yes | Redis host address |
Port | int | Yes | Redis port |
Password | string | No | Redis password |
DB | int | Yes | Redis database number |
Option Defaults
| Field | Default | Description |
|---|---|---|
AccessTokenExpires | 15m | Access Token expiration |
RefreshIdExpires | 7d | Refresh ID expiration |
AccessTokenCookieKey | access_token | Access Token cookie key |
RefreshIdCookieKey | refresh_id | Refresh ID cookie key |
MaxVersion | 5 | Refresh count before Refresh ID rebuild |
RefreshTTL | 0.5 | TTL ratio threshold for Refresh ID rebuild |
Cookie Settings
| Field | Type | Default | Description |
|---|---|---|---|
Domain | *string | None | Cookie domain |
Path | *string | / | Cookie path |
SameSite | *http.SameSite | Lax | SameSite attribute |
Secure | *bool | false | HTTPS only |
HttpOnly | *bool | true | HttpOnly flag |
PEM Keys
Three configuration methods are supported, in priority order:
File.PrivateKeyPath/File.PublicKeyPath— specify file pathsOption.PrivateKey/Option.PublicKey— provide PEM text directly- Auto-detect
./keys/private-key.pemand./keys/public-key.pem; generates a new ECDSA P-256 key pair if not found
Usage
Basic
package main
import (
"log"
"net/http"
"github.com/pardnchiu/go-jwt/core"
)
func main() {
jwtAuth, err := goJwt.New(goJwt.Config{
Redis: goJwt.Redis{
Host: "localhost",
Port: 6379,
DB: 0,
},
})
if err != nil {
log.Fatalf("init failed: %v", err)
}
defer jwtAuth.Close()
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
result := jwtAuth.Create(w, r, &goJwt.Auth{
ID: "user-1",
Name: "Alice",
Email: "alice@example.com",
Role: "admin",
})
if !result.Success {
http.Error(w, result.Error, result.StatusCode)
return
}
w.WriteHeader(result.StatusCode)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Advanced
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/pardnchiu/go-jwt/core"
)
func main() {
secure := true
jwtAuth, err := goJwt.New(goJwt.Config{
Redis: goJwt.Redis{
Host: "localhost",
Port: 6379,
Password: "secret",
DB: 1,
},
Option: &goJwt.Option{
AccessTokenExpires: 10 * time.Minute,
RefreshIdExpires: 14 * 24 * time.Hour,
MaxVersion: 3,
RefreshTTL: 0.4,
},
Cookie: &goJwt.Cookie{
Secure: &secure,
},
CheckAuth: func(auth goJwt.Auth) (bool, error) {
// return false when the user no longer exists
return auth.ID != "", nil
},
})
if err != nil {
log.Fatalf("init failed: %v", err)
}
defer jwtAuth.Close()
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
result := jwtAuth.Create(c.Writer, c.Request, &goJwt.Auth{
ID: "user-1",
Name: "Alice",
Email: "alice@example.com",
Scope: []string{"read", "write"},
})
if !result.Success {
c.JSON(result.StatusCode, gin.H{"error": result.Error})
return
}
c.JSON(result.StatusCode, gin.H{
"token": result.Token.Token,
"refresh_id": result.Token.RefreshId,
})
})
auth := r.Group("/")
auth.Use(jwtAuth.GinMiddleware())
auth.GET("/me", func(c *gin.Context) {
user, ok := goJwt.GetAuthDataFromGinContext(c)
if !ok {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
c.JSON(http.StatusOK, user)
})
auth.POST("/logout", func(c *gin.Context) {
result := jwtAuth.Revoke(c.Writer, c.Request)
c.JSON(result.StatusCode, gin.H{"success": result.Success, "error": result.Error})
})
log.Fatal(r.Run(":8080"))
}
API Reference
New
func New(c Config) (*JWTAuth, error)
Creates a JWTAuth instance, connects Redis, and loads or generates ECDSA keys.
Close
func (j *JWTAuth) Close() error
Closes the Redis connection.
Create
func (j *JWTAuth) Create(w http.ResponseWriter, r *http.Request, auth *Auth) JWTAuthResult
Issues Access Token and Refresh ID, sets cookies, and stores refresh state plus JTI in Redis.
Verify
func (j *JWTAuth) Verify(w http.ResponseWriter, r *http.Request) JWTAuthResult
Verifies the Access Token, checks revocation and device fingerprint, and refreshes transparently when expired.
Revoke
func (j *JWTAuth) Revoke(w http.ResponseWriter, r *http.Request) JWTAuthResult
Revokes the current session: clears cookies, shortens Refresh ID TTL, and marks the Access Token as revoked.
GinMiddleware
func (j *JWTAuth) GinMiddleware() gin.HandlerFunc
Gin middleware that runs Verify and stores *Auth under the user context key.
HTTPMiddleware
func (j *JWTAuth) HTTPMiddleware(next http.Handler) http.Handler
Standard library middleware that runs Verify and stores *Auth in request.Context.
GetAuthDataFromGinContext
func GetAuthDataFromGinContext(c *gin.Context) (*Auth, bool)
Reads authenticated user data from a Gin context.
GetAuthDataFromHTTPRequest
func GetAuthDataFromHTTPRequest(r *http.Request) (*Auth, bool)
Reads authenticated user data from an http.Request context.
Auth
| Field | Type | Description |
|---|---|---|
ID | string | User ID |
Name | string | Display name |
Email | string | |
Thumbnail | string | Avatar URL |
Scope | []string | Permission scopes |
Role | string | Role |
Level | int | Level |
JWTAuthResult
| Field | Type | Description |
|---|---|---|
StatusCode | int | HTTP status code |
Success | bool | Whether the operation succeeded |
Data | *Auth | Authenticated user data |
Token | *TokenResult | Issued token pair |
Error | string | Error message |
ErrorTag | string | Machine-readable error tag |
Headers
| Header | Direction | Description |
|---|---|---|
Authorization: Bearer <token> | Request | Access Token (cookie alternative) |
X-Refresh-ID | Request | Refresh ID (cookie alternative) |
X-Device-FP | Request | Override device fingerprint |
X-Device-ID | Request | Stable device ID |
X-New-Access-Token | Response | New Access Token after refresh |
X-New-Refresh-ID | Response | New Refresh ID after full rebuild |
©️ 2025 邱敬幃 Pardn Chiu