### Install Touka JWT Middleware Source: https://context7.com/fenthope/jwt/llms.txt Use this command to install the Touka JWT middleware. ```sh go get github.com/fenthope/jwt ``` -------------------------------- ### Complete JWT Authentication Example Source: https://github.com/fenthope/jwt/blob/main/docs/api.md This example demonstrates a full JWT authentication flow using Touka and MLDSA for key generation. It includes login, token refresh, and protected routes. ```go package main import ( "net/http" "time" "filippo.io/mldsa" jwtmw "github.com/fenthope/jwt" "github.com/infinite-iroha/touka" ) type User struct { UserName string } func main() { engine := touka.Default() privateKey, _ := mldsa.GenerateKey(mldsa.MLDSA65()) auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "test zone", Key: privateKey, Timeout: time.Hour, RefreshTokenTimeout: time.Hour * 24 * 7, IdentityKey: "id", Authenticator: func(c *touka.Context) (any, error) { var req struct { Username string `form:"username"` Password string `form:"password"` } c.ShouldBind(&req) if req.Username == "admin" && req.Password == "admin" { return &User{UserName: req.Username}, nil } return nil, jwtmw.ErrFailedAuthentication }, PayloadFunc: func(data any) jwtmw.MapClaims { if u, ok := data.(*User); ok { return jwtmw.MapClaims{"id": u.UserName} } return jwtmw.MapClaims{} }, IdentityHandler: func(c *touka.Context) any { claims := jwtmw.ExtractClaims(c) return &User{UserName: claims["id"].(string)} }, Authorizator: func(data any, c *touka.Context) bool { if u, ok := data.(*User); ok { return u.UserName == "admin" } return false }, }) if err != nil { panic(err) } // 公开路由 engine.POST("/login", auth.LoginHandler) engine.POST("/refresh_token", auth.RefreshHandler) // 受保护路由 authGroup := engine.Group("", auth.MiddlewareFunc()) authGroup.GET("/profile", func(c *touka.Context) { user, _ := c.Get("id") c.JSON(200, touka.H{"user": user}) }) http.ListenAndServe(":8000", engine) } ``` -------------------------------- ### Parse Token Example Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Example of parsing a JWT token from a request context using the ParseToken method. It extracts claims and token header information. ```go engine.GET("/verify", func(c *touka.Context) { token, err := auth.ParseToken(c) if err != nil { c.JSON(401, touka.H{"error": err.Error()}) return } claims := jwtmw.ExtractClaimsFromToken(token) c.JSON(200, touka.H{ "claims": claims, "header": token.Header, }) }) ``` -------------------------------- ### Logout Handler Example Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Example of using the LogoutHandler for token revocation. If RefreshTokenManager is configured, its revocation logic is used; otherwise, it relies on an in-memory refresh token chain. ```go engine.POST("/logout", auth.LogoutHandler) ``` -------------------------------- ### Configure and Use InMemoryRefreshTokenStore Source: https://context7.com/fenthope/jwt/llms.txt Demonstrates how to create and use the `InMemoryRefreshTokenStore` for managing refresh tokens. This store is suitable for single-instance deployments and can be replaced with an external store for multi-instance setups. It supports storing, retrieving, rotating, revoking, and cleaning up tokens. ```go import ( "context" "fmt" "time" "github.com/fenthope/jwt/store" ) // Create with a custom clock (useful for testing) s := store.NewInMemoryRefreshTokenStoreWithClock(time.Now) ctx := context.Background() // Store a refresh token _ = s.Set(ctx, "token-abc", map[string]any{"user_id": 42}, time.Now().Add(7*24*time.Hour)) // Retrieve user data userData, err := s.Get(ctx, "token-abc") fmt.Println(userData, err) // map[user_id:42] // Atomic rotation: old → new _ = s.Rotate(ctx, "token-abc", "token-xyz", map[string]any{"user_id": 42}, time.Now().Add(7*24*time.Hour)) // Batch revocation _ = s.Revoke(ctx, []string{"token-xyz"}) // Maintenance removed, _ := s.Cleanup(ctx) fmt.Println("expired tokens removed:", removed) active, _ := s.Count(ctx) fmt.Println("active tokens:", active) // Clear all tokens s.Clear() // Use a custom store instead of the default auth, _ := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", TokenStore: s, // must implement core.RefreshTokenRotator for RefreshHandler }) _ = auth ``` -------------------------------- ### JWT Middleware Integration Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Demonstrates how to use the JWT middleware with a Touka framework router group. This setup protects routes requiring authentication. ```go engine := touka.Default() privateKey, _ := mldsa.GenerateKey(mldsa.MLDSA65()) auth, _ := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "test zone", Key: privateKey, Authenticator: func(c *touka.Context) (any, error) { // ... }, }) // 作为中间件使用 authGroup := engine.Group("/api", auth.MiddlewareFunc()) authGroup.GET("/profile", func(c *touka.Context) { identity, _ := c.Get("identity") c.JSON(200, touka.H{"user": identity}) }) authGroup.GET("/protected", func(c *touka.Context) { c.JSON(200, touka.H{"message": "allowed"}) }) ``` -------------------------------- ### Implement Custom RedisRefreshManager Source: https://context7.com/fenthope/jwt/llms.txt Provides an example implementation of `core.RefreshTokenManager` using Redis (simulated with a map) for managing refresh token lifecycles. This allows for full control over persistence, rotation, and revocation, and is integrated into the JWT middleware by setting the `RefreshTokenManager` field. ```go import ( "context" "errors" "sync" "time" "github.com/fenthope/jwt/core" jwtmw "github.com/fenthope/jwt" ) // Example: Redis-backed refresh token manager (pseudocode) type RedisRefreshManager struct { mu sync.Mutex tokens map[string]any // replace with real Redis client } func (r *RedisRefreshManager) Store(ctx context.Context, token string, userData any, expiry time.Time) error { r.mu.Lock(); defer r.mu.Unlock() r.tokens[token] = userData return nil } func (r *RedisRefreshManager) Lookup(ctx context.Context, token string) (any, error) { r.mu.Lock(); defer r.mu.Unlock() data, ok := r.tokens[token] if !ok { return nil, core.ErrRefreshTokenNotFound } return data, nil } func (r *RedisRefreshManager) Rotate(ctx context.Context, oldToken, newToken string, userData any, expiry time.Time) error { r.mu.Lock(); defer r.mu.Unlock() if _, ok := r.tokens[oldToken]; !ok { return core.ErrRefreshTokenNotFound } delete(r.tokens, oldToken) r.tokens[newToken] = userData return nil } func (r *RedisRefreshManager) Revoke(ctx context.Context, token string) error { r.mu.Lock(); defer r.mu.Unlock() delete(r.tokens, token) return nil } // Wire into middleware auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", RefreshTokenManager: &RedisRefreshManager{tokens: make(map[string]any)}, // TokenStore is ignored when RefreshTokenManager is set }) _ = auth, err ``` -------------------------------- ### LogoutHandler Usage Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Example of using the LogoutHandler for user logout. ```APIDOC ## POST /logout ### Description Handles user logout by revoking tokens and clearing cookies. ### Method POST ### Endpoint /logout ### Parameters None directly exposed to the user. ### Request Example None provided. ### Response Depends on `RefreshTokenManager` configuration. If configured, revocation semantics are determined by that interface. If not configured, it revokes tokens within the current process and clears client cookies. In compatibility mode, the chain follows only non-expired successor mappings. ### Error Handling Refer to the specific `RefreshTokenManager` implementation or default behavior. ``` -------------------------------- ### Get Raw JWT Token String Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Helper function to retrieve the raw JWT token string from the request context. ```go // 获取原始 JWT token 字符串 func GetToken(c *touka.Context) string ``` -------------------------------- ### Configure JWT Middleware for Cookie Mode Source: https://context7.com/fenthope/jwt/llms.txt Enable `SendCookie: true` to automatically manage access and refresh tokens via HTTP cookies. This setup includes defining cookie names, domains, and security attributes like `SecureCookie` and `CookieHTTPOnly`. ```go import "net/http" auth, _ := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", Key: privateKey, Timeout: time.Hour, MaxRefresh: 7 * 24 * time.Hour, SendCookie: true, // write tokens as cookies // Access token cookie CookieName: "access_token", CookieDomain: "example.com", SecureCookie: true, // HTTPS only CookieHTTPOnly: true, // block JS access CookieSameSite: http.SameSiteStrictMode, // Refresh token cookie (Secure+HttpOnly auto-set when SendCookie=true) RefreshTokenCookieName: "refresh_token", Authenticator: func(c *touka.Context) (any, error) { return "user-123", nil }, PayloadFunc: func(data any) jwtmw.MapClaims { return jwtmw.MapClaims{"identity": data} }, }) engine := touka.Default() engine.POST("/login", auth.LoginHandler) engine.POST("/refresh_token", auth.RefreshHandler) engine.POST("/logout", auth.LogoutHandler) // Login — browser stores both cookies automatically: // curl -X POST http://localhost:8080/login \ // -c cookies.txt -d "username=user&password=pass" // Access protected route via cookie: // curl http://localhost:8080/api/me \ // -b cookies.txt // Refresh (cookie sent automatically, new cookies set): // curl -X POST http://localhost:8080/refresh_token \ // -c cookies.txt -b cookies.txt // Logout (cookies cleared): // curl -X POST http://localhost:8080/logout \ // -c cookies.txt -b cookies.txt ``` -------------------------------- ### Enforce HTTPS with HSTS Header Source: https://github.com/fenthope/jwt/blob/main/docs/security.md Example Nginx configuration to enforce HTTPS by setting the `Strict-Transport-Security` header. This ensures all future connections from the browser are made over HTTPS. ```nginx add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; ``` -------------------------------- ### Configure ToukaJWTMiddleware with ML-DSA-65 Keys Source: https://context7.com/fenthope/jwt/llms.txt Demonstrates different ways to configure the ToukaJWTMiddleware with ML-DSA-65 keys. Options include passing a key object directly, loading from raw seed bytes, loading from files, or using a public key only for verification. ```go // ML-DSA-65 (default) — all values are raw bytes, NOT PEM or PKCS#8 import "filippo.io/mldsa" privateKey, _ := mldsa.GenerateKey(mldsa.MLDSA65()) // Option A: pass key object directly (recommended for in-process key generation) mw1 := &jwtmw.ToukaJWTMiddleware{Key: privateKey} // Option B: load from raw 32-byte seed bytes mw2 := &jwtmw.ToukaJWTMiddleware{ PrivKeyBytes: seedBytes, // 32 raw bytes PubKeyBytes: privateKey.PublicKey().Bytes(), // raw encoded public key } // Option C: load from files (recommended for production) mw3 := &jwtmw.ToukaJWTMiddleware{ PrivKeyFile: "/run/secrets/jwt.key", // file contains 32 raw bytes PubKeyFile: "/run/secrets/jwt.pub", // file contains PublicKey.Bytes() } // Option D: verify-only (public key only, no signing) mw4 := &jwtmw.ToukaJWTMiddleware{ PubKeyBytes: privateKey.PublicKey().Bytes(), } ``` -------------------------------- ### Initialize Touka JWT Middleware Source: https://context7.com/fenthope/jwt/llms.txt Initialize the middleware with configuration, load keys, and return a ready-to-use *ToukaJWTMiddleware. This must be called before any handler is registered. It supports ML-DSA-65 by default and allows custom algorithms. ```go package main import ( "log" "time" "filippo.io/mldsa" jwtmw "github.com/fenthope/jwt" "github.com/infinite-iroha/touka" ) type User struct { UserName string Role string } func main() { privateKey, err := mldsa.GenerateKey(mldsa.MLDSA65()) if err != nil { log.Fatal(err) } auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", Key: privateKey, // *mldsa.PrivateKey for ML-DSA-65 Timeout: time.Minute * 15, // access token lifetime MaxRefresh: time.Hour * 24 * 7, // absolute refresh ceiling Authenticator: func(c *touka.Context) (any, error) { var req struct { Username string `form:"username" binding:"required"` Password string `form:"password" binding:"required"` } if err := c.ShouldBind(&req); err != nil { return nil, jwtmw.ErrMissingLoginValues } if req.Username == "admin" && req.Password == "secret" { return &User{UserName: req.Username, Role: "admin"}, nil } return nil, jwtmw.ErrFailedAuthentication }, PayloadFunc: func(data any) jwtmw.MapClaims { if u, ok := data.(*User); ok { return jwtmw.MapClaims{"identity": u.UserName, "role": u.Role} } return jwtmw.MapClaims{} }, }) if err != nil { log.Fatal("JWT init error:", err) } engine := touka.Default() engine.POST("/login", auth.LoginHandler) engine.POST("/refresh_token", auth.RefreshHandler) engine.POST("/logout", auth.LogoutHandler) protected := engine.Group("/api", auth.MiddlewareFunc()) protected.GET("/me", func(c *touka.Context) { claims := jwtmw.ExtractClaims(c) c.JSON(200, touka.H{"user": claims["identity"], "role": claims["role"]}) }) _ = engine // http.ListenAndServe(":8080", engine) } ``` -------------------------------- ### Configure ToukaJWTMiddleware with Dynamic KeyFunc Source: https://context7.com/fenthope/jwt/llms.txt Sets up dynamic key selection for the ToukaJWTMiddleware using a KeyFunc. This allows the middleware to choose the appropriate public key for verification based on a 'kid' (key ID) claim in the token header, facilitating key rotation. ```go // Dynamic key selection via KeyFunc (for key rotation) mw6 := &jwtmw.ToukaJWTMiddleware{ KeyFunc: func(token *jwt.Token) (any, error) { kid, _ := token.Header["kid"].(string) switch kid { case "v2": return v2PublicKey, nil default: return v1PublicKey, nil } }, } ``` -------------------------------- ### New - Initialize the middleware Source: https://context7.com/fenthope/jwt/llms.txt Initializes the JWT middleware with provided configuration. It validates settings, loads keys, and returns a ready-to-use middleware instance. This function must be called before registering any handlers. ```APIDOC ## New ### Description Initializes the JWT middleware with provided configuration. It validates settings, loads keys, and returns a ready-to-use middleware instance. This function must be called before registering any handlers. ### Function Signature `jwtmw.New(config *jwtmw.ToukaJWTMiddleware) (*ToukaJWTMiddleware, error)` ### Parameters #### Configuration (`jwtmw.ToukaJWTMiddleware`) - **Realm** (string) - Required - The realm for the JWT. - **Key** (any) - Required - The private key for signing tokens. Can be `*mldsa.PrivateKey` or other compatible types. - **Timeout** (time.Duration) - Required - The lifetime of the access token. - **MaxRefresh** (time.Duration) - Required - The absolute refresh token ceiling. - **Authenticator** (func(*touka.Context) (any, error)) - Required - A callback function to authenticate users. It receives the Touka context and should return user data or an error. - **PayloadFunc** (func(any) jwtmw.MapClaims) - Required - A callback function to generate JWT claims from user data. ### Example Usage ```go import ( "log" "time" "filippo.io/mldsa" jwtmw "github.com/fenthope/jwt" "github.com/infinite-iroha/touka" ) type User struct { UserName string Role string } func main() { privateKey, err := mldsa.GenerateKey(mldsa.MLDSA65()) if err != nil { log.Fatal(err) } auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", Key: privateKey, // *mldsa.PrivateKey for ML-DSA-65 Timeout: time.Minute * 15, // access token lifetime MaxRefresh: time.Hour * 24 * 7, // absolute refresh ceiling Authenticator: func(c *touka.Context) (any, error) { var req struct { Username string `form:"username" binding:"required"` Password string `form:"password" binding:"required"` } if err := c.ShouldBind(&req); err != nil { return nil, jwtmw.ErrMissingLoginValues } if req.Username == "admin" && req.Password == "secret" { return &User{UserName: req.Username, Role: "admin"}, nil } return nil, jwtmw.ErrFailedAuthentication }, PayloadFunc: func(data any) jwtmw.MapClaims { if u, ok := data.(*User); ok { return jwtmw.MapClaims{"identity": u.UserName, "role": u.Role} } return jwtmw.MapClaims{} }, }) if err != nil { log.Fatal("JWT init error:", err) } engine := touka.Default() engine.POST("/login", auth.LoginHandler) engine.POST("/refresh_token", auth.RefreshHandler) engine.POST("/logout", auth.LogoutHandler) protected := engine.Group("/api", auth.MiddlewareFunc()) protected.GET("/me", func(c *touka.Context) { claims := jwtmw.ExtractClaims(c) c.JSON(200, touka.H{"user": claims["identity"], "role": claims["role"]}) }) _ = engine // http.ListenAndServe(":8080", engine) } ``` ``` -------------------------------- ### New Function Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Creates and initializes a new JWT middleware instance. ```APIDOC ## New(mw *ToukaJWTMiddleware) ### Description Creates and initializes a new JWT middleware instance. It validates the configuration, loads keys, and sets default values for various middleware options. ### Method `jwtmw.New` ### Endpoint N/A (Constructor function) ### Parameters - **mw** (`*ToukaJWTMiddleware`) - Required - A struct containing the middleware configuration options. ### Request Example ```go privateKey, _ := mldsa.GenerateKey(mldsa.MLDSA65()) auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "test zone", Key: privateKey, // Other configuration options... }) if err != nil { // Handle error } ``` ### Response - **Success Response**: Returns an initialized `*ToukaJWTMiddleware` instance and `nil` error. - **Error Response**: Returns `nil` for the middleware instance and an `error` if initialization fails due to invalid configuration. ### Default Values - `SigningAlgorithm`: `ML-DSA-65` - `Timeout`: `1 hour` - `RefreshTokenTimeout`: `7 days` - `IdentityKey`: `identity` - `TokenHeadName`: `Bearer` - `TokenLookup`: `header:Authorization` - `CookieName`: `jwt` - `RefreshTokenCookieName`: `refresh_token` - `TimeFunc`: `time.Now` - `ExpField`: `"exp"` - `Authorizator`: Always returns `true`. - `IdentityHandler`: Reads identity from `JWT_PAYLOAD[mw.IdentityKey]`. - `TokenStore`: `store.NewInMemoryRefreshTokenStoreWithClock(mw.TimeFunc)`. - `Unauthorized`: Returns JSON `{"code": , "message": }`. - `LoginResponse`: Returns JSON with `access_token`, `refresh_token`, and `expire`. - `RefreshResponse`: Same as `LoginResponse`. - `LogoutResponse`: Returns JSON `{"code": }`. ``` -------------------------------- ### Register and Use Custom Signing Algorithm Source: https://context7.com/fenthope/jwt/llms.txt Implements the `Algorithm` interface for a custom signing scheme and registers it. This allows using custom algorithms with the JWT middleware. ```go import ( jwtlib "github.com/golang-jwt/jwt/v5" jwtmw "github.com/fenthope/jwt" ) // Implement the Algorithm interface for a custom scheme type myHS256Algorithm struct{} func (myHS256Algorithm) Alg() string { return "HS256" } func (myHS256Algorithm) SigningMethod() jwtlib.SigningMethod { return jwtlib.SigningMethodHS256 } func (myHS256Algorithm) LoadSigningKey(key []byte) (any, error) { if len(key) == 0 { return nil, errors.New("empty key") } return key, nil } func (myHS256Algorithm) LoadVerificationKey(key []byte) (any, error) { return key, nil } func (myHS256Algorithm) VerificationKeyFromSigningKey(k any) (any, error) { return k, nil } func init() { jwtmw.RegisterAlgorithm(myHS256Algorithm{}) } // Use the custom algorithm auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", SigningAlgorithm: "HS256", Key: []byte("my-hs256-secret-key"), Timeout: time.Hour, Authenticator: func(c *touka.Context) (any, error) { return "user", nil }, }) // Lookup registered algorithm at runtime alg, ok := jwtmw.LookupAlgorithm("HS256") if ok { fmt.Println("registered:", alg.Alg()) // "HS256" } ``` -------------------------------- ### JWT Middleware Initialization Errors Source: https://context7.com/fenthope/jwt/llms.txt These errors indicate issues during the initialization of the JWT middleware, such as missing configuration parameters or invalid key formats. ```go import jwtmw "github.com/fenthope/jwt" // Middleware initialization errors _ = jwtmw.ErrMissingRealm // Realm not set _ = jwtmw.ErrMissingSecretKey // no signing/verify key configured _ = jwtmw.ErrInvalidSigningAlgorithm // unknown algorithm name _ = jwtmw.ErrInvalidPrivKey // private key load failed _ = jwtmw.ErrInvalidPubKey // public key load failed _ = jwtmw.ErrInvalidTokenLookup // malformed TokenLookup string ``` -------------------------------- ### RegisterAlgorithm / LookupAlgorithm Source: https://context7.com/fenthope/jwt/llms.txt Allows for the registration and lookup of custom JWT signing algorithms. The `Algorithm` interface decouples signing logic, enabling support for various cryptographic schemes beyond the defaults. ```APIDOC ## Custom Algorithm Registration and Lookup ### Description Enables the use of custom signing algorithms by implementing the `Algorithm` interface and registering it. ### Registering an Algorithm ```go import ( jwtlib "github.com/golang-jwt/jwt/v5" jwtmw "github.com/fenthope/jwt" ) type myHS256Algorithm struct{} func (myHS256Algorithm) Alg() string { return "HS256" } func (myHS256Algorithm) SigningMethod() jwtlib.SigningMethod { return jwtlib.SigningMethodHS256 } func (myHS256Algorithm) LoadSigningKey(key []byte) (any, error) { return key, nil } func (myHS256Algorithm) LoadVerificationKey(key []byte) (any, error) { return key, nil } func (myHS256Algorithm) VerificationKeyFromSigningKey(k any) (any, error) { return k, nil } func init() { jwtmw.RegisterAlgorithm(myHS256Algorithm{}) } ``` ### Looking Up an Algorithm ```go // Lookup registered algorithm at runtime alg, ok := jwtmw.LookupAlgorithm("HS256") if ok { fmt.Println("registered:", alg.Alg()) // "HS256" } ``` ``` -------------------------------- ### Configure JWT Middleware for Verify-Only Mode Source: https://context7.com/fenthope/jwt/llms.txt Sets up the JWT middleware in verify-only mode by providing only a public key. This configuration is suitable for services that only need to validate tokens signed by a private key, disabling token signing capabilities within the middleware. It demonstrates how to integrate with a web framework like Touka. ```go import ( "filippo.io/mldsa" jwtmw "github.com/fenthope/jwt" "github.com/infinite-iroha/touka" ) // Assume publicKeyBytes was loaded from config or secret store privateKey, _ := mldsa.GenerateKey(mldsa.MLDSA65()) publicKeyBytes := privateKey.PublicKey().Bytes() auth, err := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "gateway", PubKeyBytes: publicKeyBytes, // no Key / PrivKeyBytes SigningAlgorithm: jwtmw.SigningAlgorithmMLDSA65, IdentityKey: "user_id", IdentityHandler: func(c *touka.Context) any { claims := jwtmw.ExtractClaims(c) return claims["user_id"] }, }) if err != nil { panic(err) } engine := touka.Default() protected := engine.Group("/api", auth.MiddlewareFunc()) protected.GET("/data", func(c *touka.Context) { c.JSON(200, touka.H{ "user_id": c.MustGet("user_id"), "status": "verified", }) }) // curl http://localhost:8080/api/data \ // -H "Authorization: Bearer " ``` -------------------------------- ### Configure ToukaJWTMiddleware with HS512 Symmetric Key Source: https://context7.com/fenthope/jwt/llms.txt Configures the ToukaJWTMiddleware to use the HS512 signing algorithm with a symmetric secret key. Ensure the key is at least 32 bytes of random data. ```go // HS512 (symmetric) mw5 := &jwtmw.ToukaJWTMiddleware{ SigningAlgorithm: jwtmw.SigningAlgorithmHS512, Key: []byte("at-least-32-bytes-of-random-secret-key"), } ``` -------------------------------- ### JWT Authentication and Token Creation Errors Source: https://context7.com/fenthope/jwt/llms.txt These errors relate to the authentication process and the creation of JWT tokens, including failed authentication attempts and issues during token signing. ```go import jwtmw "github.com/fenthope/jwt" // Authentication / token errors _ = jwtmw.ErrFailedAuthentication // wrong username or password _ = jwtmw.ErrMissingLoginValues // username or password not provided _ = jwtmw.ErrMissingAuthenticatorFunc // Authenticator callback is nil _ = jwtmw.ErrFailedTokenCreation // access token signing or storage failed _ = jwtmw.ErrMissingPrivateKey // signing attempted without private key _ = jwtmw.ErrExpiredToken // access token is expired ``` -------------------------------- ### Parse JWT from Request Source: https://context7.com/fenthope/jwt/llms.txt Extracts the raw token string, parses and verifies it, and stores the raw string in the context. Use this when token metadata is needed outside the standard middleware flow. ```go engine.GET("/token-info", func(c *touka.Context) { token, err := auth.ParseToken(c) if err != nil { c.JSON(401, touka.H{"error": err.Error()}) return } claims := jwtmw.ExtractClaimsFromToken(token) c.JSON(200, touka.H{ "algorithm": token.Method.Alg(), "header": token.Header, "claims": claims, "raw": jwtmw.GetToken(c), }) }) // curl http://localhost:8080/token-info \ // -H "Authorization: Bearer " ``` -------------------------------- ### Register LoginHandler for Authentication Source: https://context7.com/fenthope/jwt/llms.txt Register the LoginHandler on a public POST route to handle user authentication and token issuance. Ensure an Authenticator is configured. ```go // Route registration engine.POST("/login", auth.LoginHandler) // HTTP request // curl -X POST http://localhost:8080/login \ // -d "username=admin&password=secret" // Default JSON response: // { // "code": 200, // "access_token": "", // "refresh_token": "", // "expire": "2025-01-01T12:15:00Z" // } // Error responses: // 500 ErrMissingAuthenticatorFunc — Authenticator not configured // 401 ErrFailedAuthentication — wrong credentials // 401 ErrFailedTokenCreation — token generation/storage failed // 500 ErrMissingPrivateKey — no signing key configured ``` -------------------------------- ### JWT Claims and Raw Token Helpers Source: https://context7.com/fenthope/jwt/llms.txt Retrieves JWT data from the Touka context after the middleware has run. Use these helpers to access claims and the raw JWT string. ```go protected.GET("/dashboard", func(c *touka.Context) { // Extract all claims from context (set by MiddlewareFunc) claims := jwtmw.ExtractClaims(c) userID := claims["identity"] role := claims["role"] // Get the raw JWT string from context rawToken := jwtmw.GetToken(c) // Alternatively, parse again and extract from the token object token, _ := auth.ParseToken(c) claimsFromToken := jwtmw.ExtractClaimsFromToken(token) c.JSON(200, touka.H{ "user_id": userID, "role": role, "token_prefix": rawToken[:10], "exp": claimsFromToken["exp"], }) }) ``` -------------------------------- ### LoginHandler Source: https://context7.com/fenthope/jwt/llms.txt Authenticates a user and issues a pair of JWT tokens (access and refresh). This handler should be registered on a public POST route. ```APIDOC ## POST /login ### Description Authenticates a user and issues a pair of JWT tokens (access and refresh). Optionally sets cookies and invokes a custom login response. ### Method POST ### Endpoint /login ### Parameters #### Request Body - **username** (string) - Required - The user's username. - **password** (string) - Required - The user's password. ### Request Example ``` curl -X POST http://localhost:8080/login \ -d "username=admin&password=secret" ``` ### Response #### Success Response (200) - **code** (integer) - HTTP status code, typically 200. - **access_token** (string) - The generated JWT access token. - **refresh_token** (string) - The generated opaque refresh token. - **expire** (string) - The expiration time of the access token in ISO 8601 format. #### Response Example ```json { "code": 200, "access_token": "", "refresh_token": "", "expire": "2025-01-01T12:15:00Z" } ``` ### Error Responses - **500** ErrMissingAuthenticatorFunc — Authenticator not configured. - **401** ErrFailedAuthentication — Wrong credentials provided. - **401** ErrFailedTokenCreation — Token generation or storage failed. - **500** ErrMissingPrivateKey — No signing key configured. ``` -------------------------------- ### JWT Store and Golang-JWT Parsing Errors Source: https://context7.com/fenthope/jwt/llms.txt This section lists errors originating from the core storage package (e.g., `ErrRefreshTokenNotFound`) and from the underlying `golang-jwt` library for token parsing (e.g., `ErrTokenExpired`). ```go import "github.com/fenthope/jwt/core" import "github.com/golang-jwt/jwt/v5" // Store errors (from core package) _ = core.ErrRefreshTokenNotFound // refresh token absent or revoked _ = core.ErrRefreshTokenExpired // refresh token or MaxRefresh exceeded // Token parsing errors (from golang-jwt) _ = jwt.ErrTokenExpired // JWT exp claim in the past _ = jwt.ErrTokenNotValidYet // JWT nbf claim in the future _ = jwt.ErrTokenMalformed // JWT format invalid ``` -------------------------------- ### Extract Claims from Context Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Helper function to extract JWT claims from the request context. ```go // 从 context 中提取 JWT claims func ExtractClaims(c *touka.Context) jwt.MapClaims ``` -------------------------------- ### ParseToken Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Parses the JWT token from the request context. ```APIDOC ## ParseToken(c *touka.Context) ### Description Parses the JWT token from the request context based on the `TokenLookup` configuration. It validates the token's signature and checks its expiration. ### Method `(*ToukaJWTMiddleware) ParseToken` ### Endpoint N/A (Method on middleware instance) ### Parameters - **c** (`*touka.Context`) - Required - The Touka framework context. ### Request Example ```go engine.GET("/verify", func(c *touka.Context) { token, err := auth.ParseToken(c) if err != nil { c.JSON(401, touka.H{"error": err.Error()}) return } claims := jwtmw.ExtractClaimsFromToken(token) c.JSON(200, touka.H{ "claims": claims, "header": token.Header, }) }) ``` ### Response - **Success Response (200)**: Returns the parsed JWT token object and its claims. - **token** (`*jwt.Token`) - The parsed JWT token. - **claims** (`jwt.MapClaims`) - The claims extracted from the token. - **Error Response**: Returns an appropriate HTTP status code and error message. ### Error Handling - **ErrEmptyAuthHeader**: No token found in the configured locations. - **ErrInvalidSigningAlgorithm**: The token's signing algorithm does not match the configuration. - **jwt.ErrTokenExpired**: The token has expired. - **jwt.ErrTokenNotValidYet**: The token is not yet valid. - **jwt.ErrTokenMalformed**: The token is malformed. ``` -------------------------------- ### Enable CookieHTTPOnly for JavaScript Protection Source: https://github.com/fenthope/jwt/blob/main/docs/security.md Set `RefreshTokenCookieHTTPOnly` to `true` to prevent JavaScript from accessing cookies via `document.cookie`. This is a core defense against token leakage. ```go RefreshTokenCookieHTTPOnly: true, // 启用后 JS 无法访问 ``` -------------------------------- ### Custom LoginResponse for JWT Handler Source: https://context7.com/fenthope/jwt/llms.txt Customize the JSON response format for successful logins by providing a custom LoginResponse function. This allows control over the fields returned in the response. ```go // Custom LoginResponse example: auth2, _ := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", // ... other fields ... LoginResponse: func(c *touka.Context, code int, token *core.Token) { c.JSON(code, touka.H{ "access_token": token.AccessToken, "refresh_token": token.RefreshToken, "expires_in": token.ExpiresIn(), // seconds remaining "refresh_expires_at": token.RefreshExpiresAt, }) }, }) _ = auth2 ``` -------------------------------- ### Apply JWT Middleware to Protect Routes Source: https://context7.com/fenthope/jwt/llms.txt Use the MiddlewareFunc to protect API routes by validating JWTs. It extracts tokens, verifies signatures and expiry, and populates the context with claims and identity. Ensure the 'Authorization: Bearer ' header is sent. ```go import "github.com/infinite-iroha/touka" // Apply to a route group api := engine.Group("/api", auth.MiddlewareFunc()) api.GET("/profile", func(c *touka.Context) { // JWT_PAYLOAD is set by the middleware claims := jwtmw.ExtractClaims(c) // Identity is set under IdentityKey (default "identity") user, _ := c.Get("identity") c.JSON(200, touka.H{ "claims": claims, "identity": user, "token": jwtmw.GetToken(c), // raw JWT string }) }) // curl http://localhost:8080/api/profile \ // -H "Authorization: Bearer " // Errors → Unauthorized callback: // 401 empty / invalid / expired token // 400 missing exp field or wrong exp format // 403 Authorizator returned false ``` -------------------------------- ### Programmatic Token Pair Generation Source: https://context7.com/fenthope/jwt/llms.txt Generates an access and refresh token pair, storing the refresh token in the configured manager/store. Use this to issue tokens outside of the standard LoginHandler, such as after OAuth callbacks. ```go engine.POST("/oauth/callback", func(c *touka.Context) { // After verifying OAuth code exchange... userData := map[string]any{"user_id": "oauth-user-42", "provider": "github"} tokenPair, err := auth.TokenGenerator(c.Request.Context(), userData) if err != nil { c.JSON(500, touka.H{"error": err.Error()}) return } c.JSON(200, touka.H{ "access_token": tokenPair.AccessToken, "refresh_token": tokenPair.RefreshToken, "expires_at": tokenPair.ExpiresAt, "refresh_expires_at": tokenPair.RefreshExpiresAt, "expires_in": tokenPair.ExpiresIn(), }) }) ``` -------------------------------- ### MiddlewareFunc Source: https://github.com/fenthope/jwt/blob/main/docs/api.md JWT verification middleware that extracts and validates access tokens from requests. ```APIDOC ## MiddlewareFunc() ### Description JWT verification middleware that extracts and validates access tokens from requests. It handles token parsing, validation, and setting identity information in the context. ### Method Applies as middleware to routes. ### Endpoint Used with route groups, e.g., `/api`. ### Parameters None directly. Returns a `touka.HandlerFunc` for routing. ### Request Example ```go engine := touka.Default() privateKey, _ := mldsa.GenerateKey(mldsa.MLDSA65()) auth, _ := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "test zone", Key: privateKey, Authenticator: func(c *touka.Context) (any, error) { // ... }, }) // As middleware authGroup := engine.Group("/api", auth.MiddlewareFunc()) authGroup.GET("/profile", func(c *touka.Context) { identity, _ := c.Get("identity") c.JSON(200, touka.H{"user": identity}) }) ``` ### Response - **Authorization Header**: If `SendAuthorization` is true, the response will include an `Authorization: ` header. ### Error Handling - **401**: Token parsing failed (invalid, signature mismatch, format error, expired, or not yet valid). - **400**: Missing or incorrectly formatted `exp` field. - **403**: Authorization failed via `Authorizator`. ``` -------------------------------- ### Enable SecureCookie for HTTPS Transport Source: https://github.com/fenthope/jwt/blob/main/docs/security.md Set `SecureCookie` to `true` to ensure cookies are only sent over HTTPS connections. This is mandatory for production environments to prevent man-in-the-middle attacks. ```go SecureCookie: true, // 启用后仅 HTTPS 传输 ``` -------------------------------- ### ExtractClaims / ExtractClaimsFromToken / GetToken Source: https://context7.com/fenthope/jwt/llms.txt These package-level helpers retrieve JWT data from the Touka context after the middleware has run. They provide convenient access to claims and the raw token string. ```APIDOC ## GET /dashboard ### Description Retrieves user information and JWT details from the context after middleware authentication. ### Method GET ### Endpoint /dashboard ### Response #### Success Response (200) - **user_id** (any) - The user identifier extracted from claims. - **role** (any) - The user role extracted from claims. - **token_prefix** (string) - The first 10 characters of the raw JWT. - **exp** (any) - The expiration timestamp of the token. ``` -------------------------------- ### Configure CookieSameSite for CSRF Prevention Source: https://github.com/fenthope/jwt/blob/main/docs/security.md Configure `CookieSameSite` to prevent Cross-Site Request Forgery (CSRF) attacks. `http.SameSiteStrictMode` is recommended for high-security scenarios. ```go CookieSameSite: http.SameSiteStrictMode, ``` -------------------------------- ### Set CookieDomain for Scope Control Source: https://github.com/fenthope/jwt/blob/main/docs/security.md Configure `CookieDomain` to control the scope of cookie validity. Narrowing the scope to necessary subdomains enhances security by limiting exposure. ```go CookieDomain: ".example.com", // 包含所有子域名 ``` ```go CookieDomain: "api.example.com", // 仅限特定子域 ``` -------------------------------- ### Extract Claims from Token Object Source: https://github.com/fenthope/jwt/blob/main/docs/api.md Helper function to extract JWT claims directly from a JWT token object. ```go // 从 token 对象提取 claims func ExtractClaimsFromToken(token *jwt.Token) jwt.MapClaims ``` -------------------------------- ### Custom LogoutResponse for Logout Handler Source: https://context7.com/fenthope/jwt/llms.txt Customize the response format for logout operations by providing a custom LogoutResponse function. This allows for tailored success messages or status codes. ```go // Custom LogoutResponse: auth3, _ := jwtmw.New(&jwtmw.ToukaJWTMiddleware{ Realm: "my-app", LogoutResponse: func(c *touka.Context, code int) { c.JSON(code, touka.H{"message": "logged out", "code": code}) }, }) _ = auth3 ``` -------------------------------- ### ParseToken Source: https://context7.com/fenthope/jwt/llms.txt `ParseToken` extracts, parses, and verifies a JWT from the request. It stores the raw token string and returns the parsed `*jwt.Token`. Use this when you need token metadata outside the standard middleware flow. ```APIDOC ## GET /token-info ### Description Extracts and parses a JWT from the request, returning its metadata. ### Method GET ### Endpoint /token-info ### Request Example ```bash curl http://localhost:8080/token-info \ -H "Authorization: Bearer " ``` ### Response #### Success Response (200) - **algorithm** (string) - The signing algorithm used. - **header** (object) - The JWT header. - **claims** (object) - The extracted claims from the token. - **raw** (string) - The raw JWT string. ``` -------------------------------- ### JWT Authorization Error Source: https://context7.com/fenthope/jwt/llms.txt This error is returned when the authorization check fails, typically because the `Authorizator` callback returned `false`. ```go import jwtmw "github.com/fenthope/jwt" // Authorization error _ = jwtmw.ErrForbidden // Authorizator returned false ``` -------------------------------- ### Register RefreshHandler for Token Rotation Source: https://context7.com/fenthope/jwt/llms.txt Register the RefreshHandler on a POST route to allow clients to rotate their refresh tokens. Supports both cookie-based and form-body clients. Ensure the refresh token is provided either as a cookie or in the form data. ```go engine.POST("/refresh_token", auth.RefreshHandler) // Cookie-based client (browser): // curl -X POST http://localhost:8080/refresh_token \ // --cookie "refresh_token=" \ // -c cookies.txt -b cookies.txt // Form-body client (mobile / non-browser): // curl -X POST http://localhost:8080/refresh_token \ // -d "refresh_token=" // Success response (same shape as LoginHandler): // {"code":200,"access_token":"","refresh_token":"","expire":"..."} // Error responses: // 400 ErrMissingRefreshToken — no refresh token provided // 401 ErrRefreshTokenNotFound — token not in store / already revoked // 401 ErrRefreshTokenExpired — token or MaxRefresh ceiling exceeded // 500 ErrUnsafeRefreshRotation — TokenStore doesn't implement RefreshTokenRotator ``` -------------------------------- ### JWT Refresh Token Errors Source: https://context7.com/fenthope/jwt/llms.txt These errors are specific to the refresh token mechanism, covering cases where the refresh token is missing or when unsafe rotation is attempted. ```go import jwtmw "github.com/fenthope/jwt" // Refresh token errors _ = jwtmw.ErrMissingRefreshToken // refresh token not present in request _ = jwtmw.ErrUnsafeRefreshRotation // TokenStore doesn't implement RefreshTokenRotator ```