### Start Demo Server Source: https://github.com/appleboy/gin-jwt/blob/master/README.zh-CN.md Run the example server to test API endpoints. Ensure you have httpie installed for API testing. ```sh go run _example/basic/server.go ``` -------------------------------- ### Run Authorization Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Start the server for the authorization example. ```bash go run _example/authorization/main.go ``` -------------------------------- ### Run Basic Authentication Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Start the server for the basic authentication example. ```bash go run _example/basic/server.go ``` -------------------------------- ### Run Example Server Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Execute the basic example server using the Go command. This command starts a local development server for testing. ```sh go run _example/basic/server.go ``` -------------------------------- ### Quick Start Example Source: https://github.com/appleboy/gin-jwt/blob/master/README.md A basic example demonstrating how to set up and use the Gin JWT middleware for authentication. ```go package main import ( jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() // the jwt middleware auth, err := jwt.New(&jwt.GinJWTMiddleware{ Realm: "Access Realm", Key: []byte("secret"), IdentityKey: "id", // The identity key for the claims. PayloadFunc: func(data interface{}) jwt.MapClaims { return jwt.MapClaims{ "sub": "user:12345", } }, IdentityHandler: func(c *gin.Context) interface{} { claims := jwt.ExtractClaims(c) return &User{ID: claims["sub"].(string)} }, Authenticator: func(c *gin.Context) (interface{}, error) { var loginVals login if err := c.ShouldBindJSON(&loginVals); err != nil { return "", jwt.ErrMissingLoginValues } userID := loginVals.Username password := loginVals.Password if (userID == "admin" && password == "secret") || jwt.GetSigningKey(c) == []byte(password) { return &User{ID: userID, Name: "Admin User"}, } return "", jwt.ErrFailedAuthentication }, Authorizator: func(data interface{}, c *gin.Context) bool { if v, ok := data.(*User); ok && v.ID != "" { return true } return false }, Unauthorized: func(c *gin.Context, code int, message string) { c.JSON(code, gin.H{ "code": code, "message": message, }) }, // TokenLookup is a list of places to look for the token, in order. // If one is found, it is used and the rest are ignored. // Optional. Default: "header:Authorization", "query:access_token", "cookie:access_token" TokenLookup: []string{"header: Authorization", "query: token", "cookie: jwt"}, // TokenLookup: []string{"header:Authorization, query:access_token, cookie:jwt"}, // TimeFunc provides the current time. You can override it to use // a specific reference time for testing. TimeFunc: func() time.Time { return time.Now() }, }) if err != nil { panic(err) } // Public is a group of routes that do not require authentication r.POST("/login", auth.LoginHandler) // Private is a group of routes that require authentication authRequired := r.Group("/auth") authRequired.Use(auth.MiddlewareFunc()) authRequired.GET("/refresh_token", auth.RefreshHandler) authRequired.GET("/hello", helloHandler) r.Run(":8080") } type login struct { Username string `form:"username" json:"username"` Password string `form:"password" json:"password"` } type User struct { ID string `json:"id"` Name string `json:"name"` } // Hello handler func helloHandler(c *gin.Context) { claims := jwt.ExtractClaims(c) user, _ := c.Get(jwt.IdentityKey) c.JSON(http.StatusOK, gin.H{ "message": "Hello, " + user.(*User).Name + ", your sub is " + claims["sub"].(string), }) } ``` -------------------------------- ### Run OAuth SSO Integration Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Start the server for the OAuth SSO integration example. ```bash go run _example/oauth_sso/server.go ``` -------------------------------- ### Complete Gin JWT Setup Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md A full example demonstrating how to set up JWT authentication with Gin, including login, refresh, protected routes, and logout. ```go package main import ( "log" "net/http" "time" jwt "github.com/appleboy/gin-jwt/v3" "github.com/gin-gonic/gin" ) type User struct { ID string Name string } func main() { mw := &jwt.GinJWTMiddleware{ Key: []byte("secret"), Timeout: 15 * time.Minute, MaxRefresh: 7 * 24 * time.Hour, IdentityKey: "id", Authenticator: func(c *gin.Context) (any, error) { var login struct { Username string `json:"username"` Password string `json:"password"` } if err := c.ShouldBindJSON(&login); err != nil { return nil, jwt.ErrMissingLoginValues } if login.Username == "admin" && login.Password == "secret" { return &User{ID: "1", Name: "Admin"}, } return nil, jwt.ErrFailedAuthentication }, } authMiddleware, err := jwt.New(mw) if err != nil { log.Fatal(err) } router := gin.Default() // Public endpoints router.POST("/login", authMiddleware.LoginHandler) router.POST("/refresh", authMiddleware.RefreshHandler) // Protected endpoints protected := router.Group("/api", authMiddleware.MiddlewareFunc()) protected.GET("/profile", func(c *gin.Context) { user := c.MustGet("id").(*User) c.JSON(200, user) }) protected.POST("/logout", authMiddleware.LogoutHandler) if err := router.Run(":8080"); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Run Redis Simple Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Execute the example demonstrating a simple Redis store. ```bash go run _example/redis_simple/main.go ``` -------------------------------- ### Run Redis Store Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Execute the example demonstrating the Redis store implementation. ```bash go run _example/redis_store/main.go ``` -------------------------------- ### Complete Configuration Example with Redis Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/RedisOptions.md A full example demonstrating the setup of Gin JWT middleware with Redis as the refresh token store, including TLS configuration and route definitions. ```go package main import ( "crypto/tls" "log" "time" jwt "github.com/appleboy/gin-jwt/v3" "github.com/gin-gonic/gin" ) func main() { engine := gin.Default() // Configure TLS tlsConfig := &tls.Config{ ServerName: "redis.example.com", InsecureSkipVerify: false, } // Setup middleware with Redis mw := &jwt.GinJWTMiddleware{ Key: []byte("very-long-secret-key-with-at-least-256-bits"), Timeout: 15 * time.Minute, MaxRefresh: 7 * 24 * time.Hour, Realm: "example", IdentityKey: "id", Authenticator: func(c *gin.Context) (any, error) { var login struct { Username string `json:"username"` Password string `json:"password"` } if err := c.ShouldBindJSON(&login); err != nil { return nil, err } // Validate credentials if login.Username == "admin" && login.Password == "secret" { return "admin", nil } return nil, jwt.ErrFailedAuthentication }, } // Enable Redis store with custom configuration mw.EnableRedisStore( jwt.WithRedisAddr("redis.example.com:6379"), jwt.WithRedisAuth("redis_password", 0), jwt.WithRedisCache(256*1024*1024, 30*time.Second), jwt.WithRedisPool(20, 45*time.Minute, 2*time.Hour), jwt.WithRedisKeyPrefix("myapp:"), jwt.WithRedisTLS(tlsConfig), ) // Initialize middleware authMiddleware, err := jwt.New(mw) if err != nil { log.Fatal(err) } // Setup routes engine.POST("/login", authMiddleware.LoginHandler) engine.POST("/refresh", authMiddleware.RefreshHandler) protected := engine.Group("/api", authMiddleware.MiddlewareFunc()) protected.GET("/profile", getProfile) protected.POST("/logout", authMiddleware.LogoutHandler) engine.Run(":8080") } func getProfile(c *gin.Context) { claims := jwt.ExtractClaims(c) c.JSON(200, gin.H{"user": claims["id"]}) } ``` -------------------------------- ### Run Token Generator Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Execute the example demonstrating direct token creation. ```bash go run _example/token_generator/main.go ``` -------------------------------- ### Complete Gin JWT Middleware Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/HelperFunctions.md A full example demonstrating the setup and usage of Gin JWT middleware, including authentication, protected routes, and programmatic token generation. ```go package main import ( "context" "log" "time" jwt "github.com/appleboy/gin-jwt/v3" "github.com/gin-gonic/gin" ) type User struct { ID string Name string } func main() { mw := &jwt.GinJWTMiddleware{ Key: []byte("secret"), Timeout: 15 * time.Minute, IdentityKey: "id", Authenticator: func(c *gin.Context) (any, error) { return &User{ID: "1", Name: "Admin"}, nil }, PayloadFunc: func(data any) *jwt.MapClaims { user := data.(*User) return &jwt.MapClaims{ "id": user.ID, "name": user.Name, } }, } authMiddleware, err := jwt.New(mw) if err != nil { log.Fatal(err) } defer authMiddleware.ClearSensitiveData() router := gin.Default() router.POST("/login", authMiddleware.LoginHandler) protected := router.Group("/api", authMiddleware.MiddlewareFunc()) protected.GET("/profile", func(c *gin.Context) { // Extract claims claims := jwt.ExtractClaims(c) userID := claims["id"].(string) // Get token tokenString := jwt.GetToken(c) c.JSON(200, gin.H{ "user_id": userID, "token_length": len(tokenString), }) }) // Programmatic token generation router.POST("/admin/generate-token", func(c *gin.Context) { userData := &User{ID: "admin-1", Name: "Admin"} token, err := authMiddleware.TokenGenerator( context.Background(), userData, ) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{ "access_token": token.AccessToken, "expires_in": token.ExpiresIn(), }) }) router.Run(":8080") } ``` -------------------------------- ### Install Development Tools Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Install necessary development tools, such as golangci-lint. ```bash make install-tools ``` -------------------------------- ### Install Gin JWT Middleware Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Install the gin-jwt middleware using go get. ```bash go get -u github.com/appleboy/gin-jwt/v2 ``` -------------------------------- ### Run Redis TLS Example Source: https://github.com/appleboy/gin-jwt/blob/master/CLAUDE.md Execute the example demonstrating Redis store with TLS enabled. ```bash go run _example/redis_tls/main.go ``` -------------------------------- ### Install Dependencies Source: https://github.com/appleboy/gin-jwt/blob/master/_example/oauth_sso/README.md Run this command to download all necessary Go modules for the project. ```bash go mod download ``` -------------------------------- ### Run the Go Application Source: https://github.com/appleboy/gin-jwt/blob/master/_example/redis_simple/README.md Execute the main Go application file to start the server. ```bash go run main.go ``` -------------------------------- ### Example Usage of NewRedisStore Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Shows how to create a Redis token store using default Redis configuration. ```go config := store.DefaultRedisConfig() redisStore, err := store.NewRedisStore(config) ``` -------------------------------- ### Run Server with Makefile Source: https://github.com/appleboy/gin-jwt/blob/master/_example/oauth_sso/README.md An alternative method to start the server using the provided Makefile. ```bash make run ``` -------------------------------- ### Complete Azure AD Integration Example Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Example demonstrating integration with Azure AD for authentication. This typically involves using Azure AD's token validation mechanisms. ```go package main import ( "fmt" "log" "net/http" "strings" "time" jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" "github.com/lestrrat-go/jwx/jwk" "github.com/lestrrat-go/jwx/jwt/jws" ) // AzureADConfig holds Azure AD configuration details type AzureADConfig struct { TenantID string ClientID string ClientSecret string // Use with caution, prefer certificate-based auth } // getAzureADSigningKeys fetches the public keys from Azure AD's JWKS endpoint func getAzureADSigningKeys(config AzureADConfig) (jwk.Set, error) { jwksURL := fmt.Sprintf("https://login.microsoftonline.com/%s/discovery/v2.0/keys", config.TenantID) keySet, err := jwk.Fetch(jwksURL) if err != nil { return nil, fmt.Errorf("failed to fetch JWKS from %s: %w", jwksURL, err) } return keySet, nil } // validateAzureADToken validates a token issued by Azure AD func validateAzureADToken(tokenString string, config AzureADConfig, keySet jwk.Set) (jwt.MapClaims, error) { parsedToken, err := jws.ParseString(tokenString) if err != nil { return nil, fmt.Errorf("failed to parse token: %w", err) } header := parsedToken.Header() keyID, ok := header["kid"].(string) if !ok { return nil, fmt.Errorf("token header missing 'kid' field") } key, ok := keySet.LookupKeyID(keyID) if !ok { return nil, fmt.Errorf("no matching key found for kid: %s", keyID) } // Verify the token signature using the fetched public key verifiedToken, err := jws.VerifyWithKey(parsedToken, key.Material()) if err != nil { return nil, fmt.Errorf("token signature verification failed: %w", err) } // Parse claims claims, err := jwt.Parse(verifiedToken.Payload()) if err != nil { return nil, fmt.Errorf("failed to parse claims: %w", err) } // Basic validation (issuer, audience, expiry) now := time.Now() if claims.Issuer != fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", config.TenantID) { log.Printf("Warning: Unexpected issuer: %s", claims.Issuer) } if !claims.Audience() || !claims.AudienceContains(config.ClientID) { return nil, fmt.Errorf("token audience mismatch, expected %s", config.ClientID) } if claims.ExpiredAt().Before(now) { return nil, fmt.Errorf("token has expired") } // Convert claims to MapClaims for compatibility mapClaims := make(jwt.MapClaims) claims.Range(func(key string, value interface{}) error { mapClaims[key] = value return nil }) return mapClaims, nil } func main() { // Configure Azure AD details azureConfig := AzureADConfig{ TenantID: "YOUR_TENANT_ID", ClientID: "YOUR_CLIENT_ID", // ClientSecret: "YOUR_CLIENT_SECRET", // Only if using client secret flow } // Fetch Azure AD signing keys keySet, err := getAzureADSigningKeys(azureConfig) if err != nil { log.Fatalf("Error getting Azure AD signing keys: %v", err) } // Initialize Gin JWT middleware auth, err := jwt.New(&jwt.GinJWTMiddleware{ Realm: "Azure AD Access", Key: []byte("dummy-key"), // This key is not used for validation when GetSigningKey is set IdentityKey: "sub", // Typically 'sub' or 'oid' from Azure AD token // Custom function to validate the token using Azure AD keys Authenticator: func(c *gin.Context) (interface{}, error) { tokenString := jwt.GetToken(c) // Get token from request (header, query, cookie) if tokenString == "" { return nil, jwt.ErrMissingToken } claims, err := validateAzureADToken(tokenString, azureConfig, keySet) if err != nil { log.Printf("Token validation error: %v", err) return nil, jwt.ErrFailedAuthentication } // You can extract specific claims to identify the user userID, ok := claims["sub"].(string) if !ok { return nil, fmt.Errorf("token missing 'sub' claim") } // Return a user object or identifier return &User{ID: userID, Name: claims["name"].(string)} // Assuming 'name' claim exists }, // Authorizator can be used for role-based access control if roles are in claims Authorizator: func(data interface{}, c *gin.Context) bool { // Example: Check if user has a specific role claim // claims := jwt.ExtractClaims(c) // if roles, ok := claims["roles"].([]interface{}); ok { // for _, role := range roles { // if role == "Admin" { // return true // } // } // } return true // Allow access if no specific authorization needed here }, // Unauthorized handles errors during authentication or authorization Unauthorized: func(c *gin.Context, code int, message string) { c.JSON(code, gin.H{ "status": "error", "message": message, }) }, // TokenLookup specifies where to find the token (e.g., Authorization header) TokenLookup: []string{"header: Authorization"}, // TokenLookup: []string{"header: Authorization, query: token, cookie: jwt"}, // TimeFunc for consistent time reference TimeFunc: func() time.Time { return time.Now() }, }) if err != nil { log.Fatalf("JWT middleware initialization error: %v", err) } r := gin.Default() // Public route (no auth required) r.GET("/public", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Public endpoint"}) }) // Protected route requiring Azure AD authentication r.GET("/protected", auth.MiddlewareFunc(), func(c *gin.Context) { user, exists := c.Get(jwt.IdentityKey) var userName string if exists { userName = user.(*User).Name } c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("Welcome %s! You are authenticated via Azure AD.", userName), }) }) log.Println("Server starting on :8080") r.Run(":8080") } // User struct to hold authenticated user information type User struct { ID string Name string } // Helper function to extract token from header (Bearer ...) func GetToken(c *gin.Context) string { authorization := c.Request.Header.Get("Authorization") const BearerSchema = "Bearer " if strings.HasPrefix(authorization, BearerSchema) { return authorization[len(BearerSchema):] } return "" } ``` -------------------------------- ### Basic Gin JWT Setup Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/README.md This Go code demonstrates the basic setup for gin-jwt middleware, including defining the JWT configuration and setting up Gin routes for login, refresh, and protected API endpoints. ```go package main import ( "log" "time" jwt "github.com/appleboy/gin-jwt/v3" "github.com/gin-gonic/gin" ) func main() { mw := &jwt.GinJWTMiddleware{ Key: []byte("your-256-bit-secret-key"), Timeout: 15 * time.Minute, Authenticator: func(c *gin.Context) (any, error) { // Validate credentials return "user123", nil }, } authMiddleware, err := jwt.New(mw) if err != nil { log.Fatal(err) } router := gin.Default() router.POST("/login", authMiddleware.LoginHandler) router.POST("/refresh", authMiddleware.RefreshHandler) protected := router.Group("/api", authMiddleware.MiddlewareFunc()) protected.GET("/profile", func(c *gin.Context) { claims := jwt.ExtractClaims(c) c.JSON(200, gin.H{"user": claims["id"]}) }) protected.POST("/logout", authMiddleware.LogoutHandler) router.Run(":8080") } ``` -------------------------------- ### Example Usage of NewMemoryStore Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Demonstrates the creation of an in-memory token store. ```go store := store.NewMemoryStore() ``` -------------------------------- ### Get Refresh Token Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Shows how to retrieve user data associated with a refresh token. Handles cases where the token is not found or has expired. ```go userData, err := store.Get(context.Background(), "base64encodedtoken") if err != nil { // Token not found or expired return } userID := userData.(string) ``` -------------------------------- ### Method and Path Based Authorization Example Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Implement authorization based on both HTTP method and request path. Admins have full access, users can GET their profile, and are restricted from modifying resources. ```go func authorizeHandler() func(c *gin.Context, data any) bool { return func(c *gin.Context, data any) bool { user, ok := data.(*User) if !ok { return false } path := c.Request.URL.Path method := c.Request.Method // Admins have full access if user.Role == "admin" { return true } // Users can only GET their own profile if path == "/auth/profile" && method == "GET" { return true } // Users cannot modify or delete resources if method == "POST" || method == "PUT" || method == "DELETE" { return false } return true // Allow other GET requests } } ``` -------------------------------- ### Start Redis Server Source: https://github.com/appleboy/gin-jwt/blob/master/_example/redis_simple/README.md This command starts a Redis server. It is optional as the application will fall back to an in-memory store if Redis is not available. ```bash redis-server ``` -------------------------------- ### Gin JWT Middleware Setup and Route Registration Source: https://github.com/appleboy/gin-jwt/blob/master/README.md This Go code demonstrates the complete setup of the gin-jwt middleware, including initialization, parameter configuration, and registration of public and protected routes in a Gin application. It covers login, token refresh, and protected endpoint access. ```go package main import ( "log" "net/http" "os" "time" jwt "github.com/appleboy/gin-jwt/v3" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) type login struct { Username string `form:"username" json:"username" binding:"required" Password string `form:"password" json:"password" binding:"required" } var ( identityKey = "id" port string ) // User demo type User struct { UserName string FirstName string LastName string } func init() { port = os.Getenv("PORT") if port == "" { port = "8000" } } func main() { engine := gin.Default() // the jwt middleware authMiddleware, err := jwt.New(initParams()) if err != nil { log.Fatal("JWT Error:" + err.Error()) } // initialize middleware errInit := authMiddleware.MiddlewareInit() if errInit != nil { log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error()) } // register route registerRoute(engine, authMiddleware) // start http server if err = http.ListenAndServe(":"+port, engine); err != nil { log.Fatal(err) } } func registerRoute(r *gin.Engine, handle *jwt.GinJWTMiddleware) { // Public routes r.POST("/login", handle.LoginHandler) r.POST("/refresh", handle.RefreshHandler) // RFC 6749 compliant refresh endpoint r.NoRoute(handle.MiddlewareFunc(), handleNoRoute()) // Protected routes auth := r.Group("/auth", handle.MiddlewareFunc()) auth.GET("/hello", helloHandler) auth.POST("/logout", handle.LogoutHandler) // Logout with refresh token revocation } func initParams() *jwt.GinJWTMiddleware { return &jwt.GinJWTMiddleware{ Realm: "test zone", Key: []byte("secret key"), Timeout: time.Hour, MaxRefresh: time.Hour, IdentityKey: identityKey, PayloadFunc: payloadFunc(), IdentityHandler: identityHandler(), Authenticator: authenticator(), Authorizer: authorizer(), Unauthorized: unauthorized(), LogoutResponse: logoutResponse(), TokenLookup: "header: Authorization, query: token, cookie: jwt", // TokenLookup: "query:token", // TokenLookup: "cookie:token", TokenHeadName: "Bearer", TimeFunc: time.Now, } } func payloadFunc() func(data any) jwt.MapClaims { return func(data any) jwt.MapClaims { if v, ok := data.(*User); ok { return jwt.MapClaims{ identityKey: v.UserName, } } return jwt.MapClaims{} } } func identityHandler() func(c *gin.Context) any { return func(c *gin.Context) any { claims := jwt.ExtractClaims(c) return &User{ UserName: claims[identityKey].(string), } } } func authenticator() func(c *gin.Context) (any, error) { return func(c *gin.Context) (any, error) { var loginVals login if err := c.ShouldBind(&loginVals); err != nil { return "", jwt.ErrMissingLoginValues } userID := loginVals.Username password := loginVals.Password if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") { return &User{ UserName: userID, LastName: "Bo-Yi", FirstName: "Wu", }, nil } return nil, jwt.ErrFailedAuthentication } } func authorizer() func(c *gin.Context, data any) bool { return func(c *gin.Context, data any) bool { if v, ok := data.(*User); ok && v.UserName == "admin" { return true } return false } } func unauthorized() func(c *gin.Context, code int, message string) { return func(c *gin.Context, code int, message string) { c.JSON(code, gin.H{ "code": code, "message": message, }) } } func logoutResponse() func(c *gin.Context) { return func(c *gin.Context) { // This demonstrates that claims are now accessible during logout claims := jwt.ExtractClaims(c) user, exists := c.Get(identityKey) response := gin.H{ "code": http.StatusOK, "message": "Successfully logged out", } // Show that we can access user information during logout if len(claims) > 0 { response["logged_out_user"] = claims[identityKey] } if exists { response["user_info"] = user.(*User).UserName } c.JSON(http.StatusOK, response) } } func handleNoRoute() func(c *gin.Context) { return func(c *gin.Context) { c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"}) } } func helloHandler(c *gin.Context) { claims := jwt.ExtractClaims(c) user, _ := c.Get(identityKey) c.JSON(200, gin.H{ "userID": claims[identityKey], "userName": user.(*User).UserName, "text": "Hello World.", }) } ``` -------------------------------- ### TokenStore Usage Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Demonstrates how to use the TokenStore interface with both in-memory and Redis backends. Includes setting, retrieving, counting, and deleting tokens. ```go package main import ( "context" "log" "time" "github.com/appleboy/gin-jwt/v3/core" "github.com/appleboy/gin-jwt/v3/store" ) func main() { // In-memory store for single instance memStore := store.NewMemoryStore() // Store a refresh token userData := "user123" token := "base64encodedtoken123" expiry := time.Now().Add(7 * 24 * time.Hour) if err := memStore.Set(context.Background(), token, userData, expiry); err != nil { log.Fatal(err) } // Retrieve the token data, err := memStore.Get(context.Background(), token) if err != nil { log.Fatal(err) } log.Printf("Retrieved user data: %v", data) // Count active tokens count, _ := memStore.Count(context.Background()) log.Printf("Active tokens: %d", count) // Delete the token (on logout) memStore.Delete(context.Background(), token) // Or use Redis for distributed systems redisConfig := store.DefaultRedisConfig() redisConfig.Addr = "redis.example.com:6379" redisStore, err := store.NewRedisStore(redisConfig) if err != nil { log.Fatal(err) } defer redisStore.Close() // Same interface, distributed backend redisStore.Set(context.Background(), token, userData, expiry) data, _ = redisStore.Get(context.Background(), token) } ``` -------------------------------- ### Example cURL Request and Responses Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md Illustrates how to make a valid authenticated request using cURL and shows example responses for successful authorization, expired token, and forbidden access. ```bash # Valid request curl http://localhost:8080/api/profile \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." # Response (if authorized): # 200 OK # { # "user_id": "123", # "user": { # "id": "123", # "name": "John", # "role": "admin" # } # } # Invalid token: # 401 Unauthorized # { # "code": 401, # "message": "token is expired" # } # No authorization: # 403 Forbidden # { # "code": 403, # "message": "you don't have permission to access this resource" # } ``` -------------------------------- ### Example Usage of NewStore Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Illustrates creating a token store, specifically a Redis store, using the NewStore function with a configuration object. ```go config := &store.Config{ Type: store.RedisStore, Redis: &store.RedisConfig{ Addr: "localhost:6379", }, } tokenStore, err := store.NewStore(config) ``` -------------------------------- ### Example Usage of ExpiresIn Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Demonstrates how to obtain a token and calculate its remaining validity period using the ExpiresIn method. ```go token, _ := authMiddleware.TokenGenerator(ctx, userData) expiresIn := token.ExpiresIn() // 3600 seconds ``` -------------------------------- ### Login Request Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/README.md Demonstrates a POST request to the /login endpoint with JSON payload for authentication. ```http POST /login Content-Type: application/json {"username": "admin", "password": "secret"} 200 OK { "access_token": "eyJ...", "refresh_token": "base64...", "expires_in": 3600, "token_type": "Bearer" } ``` -------------------------------- ### Set Refresh Token Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/TokenStore.md Demonstrates how to store a refresh token with associated user data and an expiration time using an in-memory store. ```go store := store.NewInMemoryRefreshTokenStore() err := store.Set( context.Background(), "base64encodedtoken", "user123", time.Now().Add(7*24*time.Hour), ) ``` -------------------------------- ### Configure GinJWTMiddleware with Signing Algorithm Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/types.md Shows how to configure the `GinJWTMiddleware` with a specific signing algorithm and private/public key files. This example uses RS256 for signing. ```go mw := &jwt.GinJWTMiddleware{ SigningAlgorithm: "RS256", PrivKeyFile: "/path/to/private.pem", PubKeyFile: "/path/to/public.pem", } ``` -------------------------------- ### ErrNoPrivKeyFile Usage Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/errors.md Shows how to catch ErrNoPrivKeyFile during middleware initialization when the PrivKeyFile path is invalid. The solution involves verifying the file path and permissions. ```go mw := &jwt.GinJWTMiddleware{ SigningAlgorithm: "RS256", PrivKeyFile: "/nonexistent/path.pem", } authMiddleware, err := jwt.New(mw) if errors.Is(err, jwt.ErrNoPrivKeyFile) { // Check file path and permissions } ``` -------------------------------- ### Run Server Without OAuth Source: https://github.com/appleboy/gin-jwt/blob/master/_example/oauth_sso/README.md Execute this command to start the server and view the API structure without full OAuth functionality. ```bash go run server.go ``` -------------------------------- ### ErrInvalidSigningAlgorithm Usage Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/errors.md Demonstrates how to catch ErrInvalidSigningAlgorithm during middleware initialization when an unsupported SigningAlgorithm is provided. The solution is to use a valid algorithm. ```go mw := &jwt.GinJWTMiddleware{ SigningAlgorithm: "INVALID", // Not supported } authMiddleware, err := jwt.New(mw) if errors.Is(err, jwt.ErrInvalidSigningAlgorithm) { // Use valid algorithm } ``` -------------------------------- ### Get Default Config Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Returns the default configuration, which is set up for an in-memory store. Use this when a simple, single-instance store is sufficient. ```go config := store.DefaultConfig() // Type: MemoryStore ``` -------------------------------- ### Example 3: Method and Path Based Authorization Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Implement authorization based on both HTTP method and requested path. This provides more granular control over API access. ```go package main import ( jwt "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" "strings" ) func main() { // ... JWT middleware setup ... auth, err := jwt.New(&jwt.GinJWTMiddleware{ // ... other configurations ... Authorizator: func(data interface{}, c *gin.Context) bool { method := c.Request.Method path := c.Request.URL.Path // Example: Allow GET requests to /users/* for any authenticated user if method == "GET" && strings.HasPrefix(path, "/users/") { return true } // Example: Allow POST requests to /users/* only for 'admin' users user, ok := data.(*User) // Assuming User struct with Role field if ok && method == "POST" && strings.HasPrefix(path, "/users/") && user.Role == "admin" { return true } // Deny access by default return false }, // ... other configurations ... }) if err != nil { panic(err) } // ... rest of your Gin app setup ... r := gin.Default() r.GET("/users/:id", auth.MiddlewareFunc(), func(c *gin.Context) { c.JSON(200, gin.H{"message": "User details"}) }) r.POST("/users", auth.MiddlewareFunc(), func(c *gin.Context) { c.JSON(200, gin.H{"message": "User created"}) }) // ... } ``` -------------------------------- ### Path-Based Authorization Example Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Implement path-based authorization. Admins can access all routes, while regular users are restricted to specific paths like '/auth/profile' and '/auth/hello'. ```go func authorizeHandler() func(c *gin.Context, data any) bool { return func(c *gin.Context, data any) bool { user, ok := data.(*User) if !ok { return false } path := c.Request.URL.Path // Admin can access all routes if user.Role == "admin" { return true } // Regular users can only access /auth/profile and /auth/hello allowedPaths := []string{"/auth/profile", "/auth/hello"} for _, allowedPath := range allowedPaths { if path == allowedPath { return true } } return false } } ``` -------------------------------- ### Curl Example for Login Endpoint Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md Demonstrates how to use curl to send a POST request to the login endpoint with JSON payload and shows a typical successful response. ```bash curl -X POST http://localhost:8080/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"secret"}' # Response: # 200 OK { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJuYW1lIjoiQWRtaW4iLCJleHAiOjE3MDQ4MDczNjB9.Sig", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "base64encodedtoken" } ``` -------------------------------- ### Role-Based Authorization Example Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Implement role-based authorization by checking the user's role from the provided data. Only users with the 'admin' role are granted access. ```go func authorizeHandler() func(c *gin.Context, data any) bool { return func(c *gin.Context, data any) bool { if v, ok := data.(*User); ok && v.UserName == "admin" { return true // Only admin users can access } return false } } ``` -------------------------------- ### ErrMissingExpField Usage Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/errors.md Shows that the framework automatically checks for the 'exp' claim and returns a 400 response if it's missing, even before the Authorizer is called. ```go // Framework automatically checks for exp claim // If missing, 400 returned before Authorizer is called ``` -------------------------------- ### Go: Basic Role-Based Authorizer Source: https://github.com/appleboy/gin-jwt/blob/master/README.zh-CN.md Implement an Authorizer function that grants access only to users with the 'admin' username. This is a fundamental example of role-based access control. ```go func authorizeHandler() func(c *gin.Context, data any) bool { return func(c *gin.Context, data any) bool { if v, ok := data.(*User); ok && v.UserName == "admin" { return true // 只有 admin 用户可以访问 } return false } } ``` -------------------------------- ### Extract JWT Claims Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/types.md Demonstrates how to extract JWT claims from a context. This is useful for accessing user ID and role information stored within the claims. ```go claims := jwt.ExtractClaims(c) userID := claims["id"].(string) role := claims["role"].(string) ``` -------------------------------- ### Create Various Token Stores Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Demonstrates creating in-memory, Redis, and factory-pattern based token stores. Includes examples for production and development environments, and 'must' versions that panic on error. ```go package main import ( "log" "time" "github.com/appleboy/gin-jwt/v3/store" ) func main() { // 1. In-memory store for development memStore := store.NewMemoryStore() // 2. Redis store for production redisConfig := &store.RedisConfig{ Addr: "redis.example.com:6379", Password: "secret", CacheSize: 256 * 1024 * 1024, CacheTTL: time.Minute, KeyPrefix: "myapp:", } redisStore, err := store.NewRedisStore(redisConfig) if err != nil { log.Fatal(err) } defer redisStore.Close() // 3. Using factory pattern config := &store.Config{ Type: store.RedisStore, Redis: redisConfig, } factoryStore, err := store.NewStore(config) if err != nil { log.Fatal(err) } // 4. Must versions (panic on error) safeStore := store.MustNewMemoryStore() } ``` -------------------------------- ### Protected Request Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/README.md Shows how to make a GET request to a protected API endpoint with an Authorization header. ```http GET /api/profile Authorization: Bearer eyJ... 200 OK {"user": "admin"} ``` -------------------------------- ### Logout Cookie Deletion Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md Examples of Set-Cookie headers indicating the deletion of JWT and refresh token cookies. ```http Set-Cookie: jwt=; Max-Age=-1; Path=/; ... Set-Cookie: refresh_token=; Max-Age=-1; Path=/; ... ``` -------------------------------- ### Curl Example: Refresh Success Response Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md Example of a successful 200 OK response received after a refresh token request. ```json # Response: # 200 OK { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJuYW1lIjoiQWRtaW4iLCJleHAiOjE3MDQ4MDczNjB9.NewSig", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "base64encodedtokenNEW" } ``` -------------------------------- ### Using a Memory Store with TokenStore Interface Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Shows how to create and use a memory store, demonstrating common operations like Set, Get, Delete, Count, and Cleanup, which are consistent across all store types. ```Go // Works with any store type store := store.NewMemoryStore() ctx := context.Background() store.Set(ctx, "token123", userData, expiry) data, err := store.Get(ctx, "token123") store.Delete(ctx, "token123") count, err := store.Count(ctx) cleaned, err := store.Cleanup(ctx) ``` -------------------------------- ### Enable Redis with Full Options Source: https://github.com/appleboy/gin-jwt/blob/master/_example/redis_simple/README.md Provide Redis server address, password, and database number using `EnableRedisStoreWithOptions` for a more customized connection. ```go middleware.EnableRedisStoreWithOptions("localhost:6379", "password", 0) ``` -------------------------------- ### Initialize GinJWTMiddleware Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/GinJWTMiddleware.md Demonstrates how to create and configure a new GinJWTMiddleware instance. Ensure all necessary fields like Key, Timeout, IdentityKey, and Authenticator are properly set before calling jwt.New. ```go mw := &jwt.GinJWTMiddleware{ Key: []byte("your-secret-key"), Timeout: time.Hour, IdentityKey: "id", Authenticator: func(c *gin.Context) (any, error) { // validate credentials return user, nil }, } authMiddleware, err := jwt.New(mw) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Authorization Example Endpoint Source: https://github.com/appleboy/gin-jwt/blob/master/README.md Demonstrates authorization failure for a protected endpoint. ```APIDOC ## GET /auth/hello (Authorization Failure Example) ### Description This demonstrates a scenario where a user attempts to access a protected resource without sufficient permissions, resulting in a 403 Forbidden response. ### Method GET ### Endpoint /auth/hello ### Parameters #### Header Parameters - **Authorization** (string) - Required - Bearer token for authentication (e.g., "Bearer xxxxxxxxx"). ### Response #### Error Response (403) - **code** (integer) - The HTTP status code, indicating forbidden access. - **message** (string) - A message explaining the reason for the error. #### Response Example ```json { "code": 403, "message": "You don't have permission to access." } ``` ``` -------------------------------- ### Create a Store or Panic on Error Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Creates a store instance using the provided configuration. This function will panic if an error occurs during store creation. ```Go tokenStore := store.MustNewStore(&store.Config{ Type: store.MemoryStore, }) ``` -------------------------------- ### Logout Success Response Body Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md The JSON body returned on a successful logout. ```json { "code": 200 } ``` -------------------------------- ### Create a Store Directly Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Abstracts the factory pattern to create a store instance directly using a configuration object. Handles default configurations if none are provided. ```Go config := store.DefaultConfig() tokenStore, err := store.NewStore(config) ``` -------------------------------- ### Create a Token Store from Configuration Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Creates a token store based on the provided configuration. If the config is nil, it defaults to MemoryStore. Errors can occur if Redis connection fails or an unsupported store type is specified. ```Go config := &store.Config{ Type: store.MemoryStore, } tokenStore, err := factory.CreateStore(config) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Login Request Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/endpoints.md A typical JSON payload for the login request, containing username and password. ```json { "username": "admin", "password": "secret" } ``` -------------------------------- ### Creating a Redis Store with Error Handling Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/api-reference/StoreFactory.md Demonstrates how to create a Redis store and handle potential errors, including a fallback to a memory store if connection fails. ```Go store, err := store.NewRedisStore(config) if err != nil { if strings.Contains(err.Error(), "failed to connect to Redis") { // Handle connection error store = store.NewMemoryStore() // Fallback } else { log.Fatal(err) } } ``` -------------------------------- ### Logout Request Example Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/README.md Demonstrates a POST request to the /api/logout endpoint to invalidate the current session. ```http POST /api/logout Authorization: Bearer eyJ... 200 OK {"code": 200} ``` -------------------------------- ### Catch ErrRefreshTokenNotFound Source: https://github.com/appleboy/gin-jwt/blob/master/_autodocs/errors.md Example of catching ErrRefreshTokenNotFound when retrieving a token from storage. Verify the token exists and has not been revoked. ```go userData, err := mw.RefreshTokenStore.Get(ctx, token) if errors.Is(err, core.ErrRefreshTokenNotFound) { // Token not in storage } ``` -------------------------------- ### Refresh Token Endpoint Test Source: https://github.com/appleboy/gin-jwt/blob/master/_example/redis_simple/README.md Send a GET request with the refresh token to the refresh token endpoint. ```bash curl -X GET localhost:8000/auth/refresh_token -d '{"refresh_token": "YOUR_REFRESH_TOKEN"}' -H "Content-Type: application/json" ```