### Install Echo JWT Middleware Source: https://github.com/labstack/echo-jwt/blob/main/README.md Add the middleware dependency to your project using Go modules. ```bash go get github.com/labstack/echo-jwt/v5 ``` -------------------------------- ### Test JWT Authentication with Curl Source: https://github.com/labstack/echo-jwt/blob/main/README.md Sends a GET request with a Bearer token to verify the protected endpoint. ```bash curl -v -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" http://localhost:8080 ``` -------------------------------- ### Import Echo JWT Middleware Source: https://github.com/labstack/echo-jwt/blob/main/README.md Import the package into your Go source files. ```go import "github.com/labstack/echo-jwt/v5" ``` -------------------------------- ### Configure KeyFunc for Custom Key Resolution Source: https://context7.com/labstack/echo-jwt/llms.txt Use KeyFunc to verify signing algorithms or fetch keys dynamically from external sources like JWKS. ```go package main import ( "fmt" "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() signingKey := []byte("my-secret-key") e.Use(echojwt.WithConfig(echojwt.Config{ KeyFunc: func(token *jwt.Token) (interface{}, error) { // Verify signing algorithm if token.Method.Alg() != "HS256" { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } // Could fetch key from external source based on claims // kid := token.Header["kid"].(string) // key := fetchKeyFromJWKS(kid) return signingKey, nil }, })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, token.Claims) }) e.Start(":8080") } ``` -------------------------------- ### Configure JWT Middleware with Custom Options Source: https://context7.com/labstack/echo-jwt/llms.txt Create a JWT middleware with advanced configuration, including custom signing methods, token lookup locations, and error handlers. This method panics on invalid configuration; use `Config.ToMiddleware()` for production. ```go package main import ( "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), SigningMethod: "HS256", ContextKey: "user", TokenLookup: "header:Authorization:Bearer ", })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) claims := token.Claims.(jwt.MapClaims) return c.JSON(http.StatusOK, map[string]interface{}{ "user": claims["name"], "sub": claims["sub"], }) }) e.Start(":8080") } ``` -------------------------------- ### Initialize JWT Middleware with Signing Key Source: https://context7.com/labstack/echo-jwt/llms.txt Quickly add JWT authentication to Echo routes using a secret signing key. Uses default HS256 algorithm and Bearer token from Authorization header. The token is stored in the context under the 'user' key. ```go package main import ( "errors" "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() // Add JWT middleware with a secret key e.Use(echojwt.JWT([]byte("my-secret-key"))) e.GET("/protected", func(c *echo.Context) error { // Get the token from context (stored under "user" key by default) token, err := echo.ContextGet[*jwt.Token](c, "user") if err != nil { return echo.ErrUnauthorized.Wrap(err) } claims, ok := token.Claims.(jwt.MapClaims) if !ok { return errors.New("failed to cast claims as jwt.MapClaims") } return c.JSON(http.StatusOK, claims) }) e.Start(":8080") } // Test with curl: // curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" http://localhost:8080/protected // Response: {"admin":true,"name":"John Doe","sub":"1234567890"} ``` -------------------------------- ### Configure Echo JWT Middleware Source: https://github.com/labstack/echo-jwt/blob/main/README.md Apply the middleware to an Echo instance using either a simple secret key or a full configuration struct. ```go e.Use(echojwt.JWT([]byte("secret"))) ``` ```go e.Use(echojwt.WithConfig(echojwt.Config{ // ... SigningKey: []byte("secret"), // ... })) ``` -------------------------------- ### Implement JWT Authentication in Echo Source: https://github.com/labstack/echo-jwt/blob/main/README.md Configures the echo-jwt middleware with a signing key and retrieves claims from the request context. ```go package main import ( "errors" "log/slog" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware" "net/http" ) func main() { e := echo.New() e.Use(middleware.RequestLogger()) e.Use(middleware.Recover()) e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("secret"), })) e.GET("/", func(c *echo.Context) error { token, err := echo.ContextGet[*jwt.Token](c, "user") if err != nil { return echo.ErrUnauthorized.Wrap(err) } claims, ok := token.Claims.(jwt.MapClaims) // by default claims is of type `jwt.MapClaims` if !ok { return errors.New("failed to cast claims as jwt.MapClaims") } return c.JSON(http.StatusOK, claims) }) if err := e.Start(":8080"); err != nil { slog.Error("Failed to start server", "error", err) } } ``` -------------------------------- ### Implement SuccessHandler for Post-Authentication Source: https://context7.com/labstack/echo-jwt/llms.txt Executes custom logic after successful validation, such as logging or enriching the request context with user claims. ```go package main import ( "log" "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), SuccessHandler: func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) claims := token.Claims.(jwt.MapClaims) // Log successful authentication log.Printf("User authenticated: %v", claims["sub"]) // Add extracted info to context for easier access c.Set("userID", claims["sub"]) c.Set("userName", claims["name"]) // Could also do additional authorization check here // if !hasPermission(claims) { // return echo.ErrForbidden // } return nil }, })) e.GET("/protected", func(c *echo.Context) error { userID := c.Get("userID").(string) userName := c.Get("userName").(string) return c.JSON(http.StatusOK, map[string]string{ "userID": userID, "userName": userName, }) }) e.Start(":8080") } ``` -------------------------------- ### Create JWT Middleware Safely with Config.ToMiddleware Source: https://context7.com/labstack/echo-jwt/llms.txt Safely convert a JWT configuration struct into middleware, returning an error for invalid configurations instead of panicking. This is recommended for production environments requiring explicit error handling. ```go package main import ( "log" "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() config := echojwt.Config{ SigningKey: []byte("my-secret-key"), SigningMethod: "HS256", } middleware, err := config.ToMiddleware() if err != nil { log.Fatalf("Invalid JWT config: %v", err) } e.Use(middleware) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, token.Claims) }) e.Start(":8080") } ``` -------------------------------- ### Implement Custom Token Extractors Source: https://context7.com/labstack/echo-jwt/llms.txt Define custom extraction logic using TokenLookupFuncs for non-standard token locations. ```go package main import ( "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware" ) func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), TokenLookupFuncs: []middleware.ValuesExtractor{ // Custom extractor: get token from X-API-Key header func(c *echo.Context) ([]string, middleware.ExtractorSource, error) { token := c.Request().Header.Get("X-API-Key") if token == "" { return nil, middleware.ExtractorSourceHeader, middleware.ErrValuesExtractorError } return []string{token}, middleware.ExtractorSourceHeader, nil }, }, })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, token.Claims) }) e.Start(":8080") } ``` -------------------------------- ### Support Multiple Signing Keys with kid Source: https://context7.com/labstack/echo-jwt/llms.txt Use SigningKeys to support key rotation or multi-tenant scenarios by matching the 'kid' header in the JWT. ```go package main import ( "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() // Multiple keys identified by 'kid' claim in JWT header e.Use(echojwt.WithConfig(echojwt.Config{ SigningKeys: map[string]interface{}{ "key-1": []byte("first-secret"), "key-2": []byte("second-secret"), }, })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, map[string]interface{}{ "claims": token.Claims, "kid": token.Header["kid"], }) }) e.Start(":8080") } ``` -------------------------------- ### Use Custom Claims Types Source: https://context7.com/labstack/echo-jwt/llms.txt Implement NewClaimsFunc to return a custom struct for type-safe claim access. ```go package main import ( "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) // Custom claims struct type CustomClaims struct { jwt.RegisteredClaims Name string `json:"name"` Admin bool `json:"admin"` Role string `json:"role"` } func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), NewClaimsFunc: func(c *echo.Context) jwt.Claims { return new(CustomClaims) // Must return pointer for JSON unmarshaling }, })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) claims := token.Claims.(*CustomClaims) return c.JSON(http.StatusOK, map[string]interface{}{ "name": claims.Name, "admin": claims.Admin, "role": claims.Role, "subject": claims.Subject, }) }) e.Start(":8080") } ``` -------------------------------- ### Implement ContinueOnIgnoredError for Mixed Routes Source: https://context7.com/labstack/echo-jwt/llms.txt Enables authentication to be optional by returning nil in the ErrorHandler when a token is missing or invalid, allowing the request to proceed to the handler. ```go package main import ( "errors" "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), ContinueOnIgnoredError: true, ErrorHandler: func(c *echo.Context, err error) error { // Set a "public" token for unauthenticated users var extractionErr *echojwt.TokenExtractionError if errors.As(err, &extractionErr) { c.Set("user", "anonymous") return nil // Return nil to continue to handler } // For parsing errors, return the error return echo.ErrUnauthorized }, })) e.GET("/content", func(c *echo.Context) error { user := c.Get("user") switch v := user.(type) { case *jwt.Token: claims := v.Claims.(jwt.MapClaims) return c.JSON(http.StatusOK, map[string]interface{}{ "content": "Full premium content", "username": claims["name"], }) case string: return c.JSON(http.StatusOK, map[string]interface{}{ "content": "Limited public content", "user": v, }) } return echo.ErrUnauthorized }) e.Start(":8080") } ``` -------------------------------- ### Curl Response Output Source: https://github.com/labstack/echo-jwt/blob/main/README.md Expected response headers and JSON body from the authenticated request. ```bash * Trying 127.0.0.1:8080... * Connected to localhost (127.0.0.1) port 8080 (#0) > GET / HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.81.0 > Accept: */* > Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Content-Type: application/json; charset=UTF-8 < Date: Sun, 27 Nov 2022 21:34:17 GMT < Content-Length: 52 < {"admin":true,"name":"John Doe","sub":"1234567890"} ``` -------------------------------- ### Test JWT middleware integration in Go Source: https://context7.com/labstack/echo-jwt/llms.txt Use this test template to verify that token type assertions function correctly with the current JWT library version. It simulates a request with a valid JWT and checks the response status and body. ```go package main import ( "net/http" "net/http/httptest" "testing" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func TestJWTMiddlewareIntegration(t *testing.T) { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("secret"), })) // Handler that performs type assertion on token e.GET("/test", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) // Will panic if JWT library version mismatch claims := token.Claims.(jwt.MapClaims) return c.JSON(http.StatusOK, claims) }) // Create test request with valid JWT req := httptest.NewRequest(http.MethodGet, "/test", nil) req.Header.Set(echo.HeaderAuthorization, "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ") res := httptest.NewRecorder() e.ServeHTTP(res, req) if res.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", res.Code) } expected := `{"admin":true,"name":"John Doe","sub":"1234567890"}` + "\n" if res.Body.String() != expected { t.Errorf("Expected body %s, got %s", expected, res.Body.String()) } } ``` -------------------------------- ### Configure ErrorHandler for Custom Error Responses Source: https://context7.com/labstack/echo-jwt/llms.txt Use ErrorHandler to define custom responses for authentication failures, distinguishing between extraction and parsing errors. ```go package main import ( "errors" "net/http" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), ErrorHandler: func(c *echo.Context, err error) error { // Distinguish between extraction and parsing errors var extractionErr *echojwt.TokenExtractionError var parsingErr *echojwt.TokenParsingError if errors.As(err, &extractionErr) { return echo.NewHTTPError(http.StatusUnauthorized, "Token not provided") } if errors.As(err, &parsingErr) { return echo.NewHTTPError(http.StatusUnauthorized, "Invalid token") } return echo.NewHTTPError(http.StatusUnauthorized, "Authentication failed") }, })) e.GET("/protected", func(c *echo.Context) error { return c.String(http.StatusOK, "Access granted") }) e.Start(":8080") } ``` -------------------------------- ### Perform Integration Testing for JWT Middleware Source: https://github.com/labstack/echo-jwt/blob/main/README.md Use integration tests to ensure compatibility between the JWT library version and your handler's type assertions. ```go func TestIntegrationMiddlewareWithHandler(t *testing.T) { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("secret"), })) // use handler that gets token from context to fail your CI flow when `golang-jwt/jwt` library version changes // a) `token, ok := c.Get("user").(*jwt.Token)` // b) `token := c.Get("user").(*jwt.Token)` e.GET("/example", exampleHandler) req := httptest.NewRequest(http.MethodGet, "/example", nil) req.Header.Set(echo.HeaderAuthorization, "Bearer ") res := httptest.NewRecorder() e.ServeHTTP(res, req) if res.Code != 200 { t.Failed() } } ``` -------------------------------- ### Configure Token Extraction Sources Source: https://context7.com/labstack/echo-jwt/llms.txt Use TokenLookup to specify multiple comma-separated sources for JWT extraction, such as headers, cookies, or query parameters. ```go package main import ( "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() // Extract token from header (with Bearer prefix) OR cookie OR query parameter e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), TokenLookup: "header:Authorization:Bearer ,cookie:jwt,query:token", })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, token.Claims) }) e.Start(":8080") } ``` -------------------------------- ### Configure ParseTokenFunc for Custom Parsing Source: https://context7.com/labstack/echo-jwt/llms.txt Implement ParseTokenFunc to override default token parsing logic, useful for specific library versions or custom validation requirements. ```go package main import ( "errors" "fmt" "net/http" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() signingKey := []byte("my-secret-key") e.Use(echojwt.WithConfig(echojwt.Config{ ParseTokenFunc: func(c *echo.Context, auth string) (interface{}, error) { keyFunc := func(t *jwt.Token) (interface{}, error) { if t.Method.Alg() != "HS256" { return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"]) } return signingKey, nil } token, err := jwt.Parse(auth, keyFunc) if err != nil { return nil, err } if !token.Valid { return nil, errors.New("invalid token") } return token, nil }, })) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, token.Claims) }) e.Start(":8080") } ``` -------------------------------- ### Extract JWT Token in Handler Source: https://github.com/labstack/echo-jwt/blob/main/README.md Retrieve the authenticated user token from the Echo context within a request handler. ```go import "github.com/golang-jwt/jwt/v5" // ... e.GET("/", func(c *echo.Context) error { token, err := echo.ContextGet[*jwt.Token](c,"user") if err != nil { return echo.ErrUnauthorized.Wrap(err) } claims, ok := token.Claims.(jwt.MapClaims) // by default claims is of type `jwt.MapClaims` if !ok { return errors.New("failed to cast claims as jwt.MapClaims") } return c.JSON(http.StatusOK, claims) }) ``` -------------------------------- ### Configure Skipper for Conditional Bypass Source: https://context7.com/labstack/echo-jwt/llms.txt Uses the Skipper function to define paths that should bypass JWT authentication, such as health checks or public endpoints. ```go package main import ( "net/http" "strings" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v5" "github.com/labstack/echo/v5" ) func main() { e := echo.New() e.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte("my-secret-key"), Skipper: func(c *echo.Context) bool { // Skip JWT check for public paths path := c.Request().URL.Path return path == "/health" || path == "/login" || strings.HasPrefix(path, "/public/") }, })) // Public endpoints (no JWT required) e.GET("/health", func(c *echo.Context) error { return c.JSON(http.StatusOK, map[string]string{"status": "healthy"}) }) e.GET("/public/info", func(c *echo.Context) error { return c.JSON(http.StatusOK, map[string]string{"info": "public data"}) }) // Protected endpoint (JWT required) e.GET("/protected", func(c *echo.Context) error { token := c.Get("user").(*jwt.Token) return c.JSON(http.StatusOK, token.Claims) }) e.Start(":8080") } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.