A Go implementation of JSON Web Tokens (JWT) with support for multiple signing algorithms and comprehensive validation options
This document covers extracting JWT tokens from HTTP requests using the request package.
import "github.com/golang-jwt/jwt/v5/request"The request package provides utilities to extract JWT tokens from HTTP requests using various strategies including Authorization headers, query parameters, form data, and custom extractors.
func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc, options ...ParseFromRequestOption) (token *jwt.Token, err error)Extracts and parses a JWT token from an HTTP request. This behaves like jwt.Parse but accepts a request and an extractor instead of a token string.
Parameters:
req - The HTTP request to extract token fromextractor - The extractor implementation defining how to find the tokenkeyFunc - Key function for signature verificationoptions - Optional parser configurationReturns: Parsed and verified token or error.
func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error)DEPRECATED: Use ParseFromRequest with the WithClaims option instead.
type Extractor interface {
ExtractToken(*http.Request) (string, error)
}The core interface for token extraction. Implementations define where to look for tokens in requests. Must return ErrNoTokenInRequest if no token is found.
type BearerExtractor struct{}Extracts tokens from the Authorization header in the format: Authorization: Bearer <token>
func (e BearerExtractor) ExtractToken(req *http.Request) (string, error)type HeaderExtractor []stringExtracts tokens from HTTP headers. Tries each specified header name in order until a match is found.
func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error)Example:
extractor := request.HeaderExtractor{"X-Auth-Token", "Authorization"}type ArgumentExtractor []stringExtracts tokens from request arguments including POST form data or GET query parameters. Tries argument names in order until a match is found.
func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error)Note: Calls ParseMultipartForm on the request.
Example:
extractor := request.ArgumentExtractor{"token", "access_token"}type MultiExtractor []ExtractorCombines multiple extractors. Tries each extractor in order until one returns a token or an error (other than ErrNoTokenInRequest).
func (e MultiExtractor) ExtractToken(req *http.Request) (string, error)Example:
extractor := request.MultiExtractor{
request.AuthorizationHeaderExtractor,
request.ArgumentExtractor{"access_token"},
}type PostExtractionFilter struct {
Extractor Extractor
Filter func(string) (string, error)
}Wraps an extractor to post-process the extracted token string before returning it.
func (e *PostExtractionFilter) ExtractToken(req *http.Request) (string, error)var AuthorizationHeaderExtractor *PostExtractionFilterExtracts bearer tokens from the Authorization header and strips the "Bearer " prefix.
Configured as:
HeaderExtractor{"Authorization"} + stripBearerPrefixFromTokenString filtervar OAuth2Extractor *MultiExtractorStandard OAuth 2.0 token extractor. Tries:
access_token query parameter or form fieldConfigured as:
MultiExtractor{
AuthorizationHeaderExtractor,
ArgumentExtractor{"access_token"},
}func WithClaims(claims jwt.Claims) ParseFromRequestOptionParses the token with custom claims implementation.
func WithParser(parser *jwt.Parser) ParseFromRequestOptionUses a custom parser with specific validation options.
var ErrNoTokenInRequest errorReturned when no token is found in the request. Extractors must return this specific error when a token is not present.
import (
"net/http"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request"
)
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Create parser with validation options
parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
func(token *jwt.Token) (interface{}, error) {
// Return verification key
return []byte("secret-key"), nil
},
request.WithParser(parser),
)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
if !token.Valid {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Token is valid, continue
next.ServeHTTP(w, r)
})
}import (
"net/http"
"github.com/golang-jwt/jwt/v5/request"
)
func handleOAuth2Request(w http.ResponseWriter, r *http.Request) {
// Extract from Authorization header OR access_token parameter
token, err := request.ParseFromRequest(r,
request.OAuth2Extractor,
keyFunc,
)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Process request with validated token
}import (
"net/http"
"github.com/golang-jwt/jwt/v5/request"
)
func extractFromCustomHeader(w http.ResponseWriter, r *http.Request) {
// Try X-API-Token header, then X-Auth-Token
extractor := request.HeaderExtractor{"X-API-Token", "X-Auth-Token"}
token, err := request.ParseFromRequest(r, extractor, keyFunc)
if err != nil {
http.Error(w, "Missing or invalid token", http.StatusUnauthorized)
return
}
// Use token
}import (
"net/http"
"github.com/golang-jwt/jwt/v5/request"
)
func extractFromQueryParam(w http.ResponseWriter, r *http.Request) {
// Extract from ?token=... or ?jwt=...
extractor := request.ArgumentExtractor{"token", "jwt"}
token, err := request.ParseFromRequest(r, extractor, keyFunc)
if err != nil {
http.Error(w, "Missing token parameter", http.StatusUnauthorized)
return
}
// Use token
}import (
"net/http"
"github.com/golang-jwt/jwt/v5/request"
)
func flexibleTokenExtraction(w http.ResponseWriter, r *http.Request) {
// Try multiple sources in order:
// 1. Authorization header
// 2. X-API-Token header
// 3. token query parameter
extractor := request.MultiExtractor{
request.AuthorizationHeaderExtractor,
request.HeaderExtractor{"X-API-Token"},
request.ArgumentExtractor{"token"},
}
token, err := request.ParseFromRequest(r, extractor, keyFunc)
if err != nil {
http.Error(w, "No valid token found", http.StatusUnauthorized)
return
}
// Use token
}import (
"net/http"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request"
)
type CustomClaims struct {
UserID string `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func extractWithCustomClaims(w http.ResponseWriter, r *http.Request) {
claims := &CustomClaims{}
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
keyFunc,
request.WithClaims(claims),
)
if err != nil || !token.Valid {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Access custom claims
userID := claims.UserID
role := claims.Role
}import (
"time"
"net/http"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request"
)
func extractWithCustomParser(w http.ResponseWriter, r *http.Request) {
// Create parser with validation options
parser := jwt.NewParser(
jwt.WithValidMethods([]string{"RS256", "RS384"}),
jwt.WithIssuer("my-service"),
jwt.WithLeeway(5*time.Second),
)
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
keyFunc,
request.WithParser(parser),
)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Use validated token
}import (
"net/http"
"github.com/golang-jwt/jwt/v5/request"
)
// Custom extractor for cookies
type CookieExtractor string
func (e CookieExtractor) ExtractToken(req *http.Request) (string, error) {
cookie, err := req.Cookie(string(e))
if err != nil {
return "", request.ErrNoTokenInRequest
}
return cookie.Value, nil
}
// Usage
func extractFromCookie(w http.ResponseWriter, r *http.Request) {
extractor := CookieExtractor("auth_token")
token, err := request.ParseFromRequest(r, extractor, keyFunc)
if err != nil {
http.Error(w, "Missing auth cookie", http.StatusUnauthorized)
return
}
// Use token
}import (
"strings"
"net/http"
"github.com/golang-jwt/jwt/v5/request"
)
func customPostProcessing(w http.ResponseWriter, r *http.Request) {
// Extract from header and remove custom prefix
baseExtractor := request.HeaderExtractor{"X-Custom-Auth"}
extractor := &request.PostExtractionFilter{
Extractor: baseExtractor,
Filter: func(token string) (string, error) {
// Remove "Token " prefix if present
return strings.TrimPrefix(token, "Token "), nil
},
}
token, err := request.ParseFromRequest(r, extractor, keyFunc)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Use token
}import (
"context"
"net/http"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request"
)
type contextKey string
const claimsContextKey contextKey = "claims"
func JWTAuthMiddleware(secretKey []byte) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Try multiple extraction methods
extractor := request.MultiExtractor{
request.AuthorizationHeaderExtractor,
request.ArgumentExtractor{"access_token"},
}
// Create parser with validation options
parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
token, err := request.ParseFromRequest(r,
extractor,
func(token *jwt.Token) (interface{}, error) {
// Validate signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrInvalidKeyType
}
return secretKey, nil
},
request.WithParser(parser),
)
if err != nil || !token.Valid {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Add claims to request context
ctx := context.WithValue(r.Context(), claimsContextKey, token.Claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// Helper to extract claims from context
func GetClaims(r *http.Request) jwt.MapClaims {
claims, ok := r.Context().Value(claimsContextKey).(jwt.MapClaims)
if !ok {
return nil
}
return claims
}
// Usage in handlers
func protectedHandler(w http.ResponseWriter, r *http.Request) {
claims := GetClaims(r)
userID := claims["sub"].(string)
// Handle request for authenticated user
}import (
"errors"
"net/http"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request"
)
func handleTokenErrors(w http.ResponseWriter, r *http.Request) {
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
keyFunc,
)
if err != nil {
// Check specific error types
switch {
case errors.Is(err, request.ErrNoTokenInRequest):
http.Error(w, "No token provided", http.StatusUnauthorized)
case errors.Is(err, jwt.ErrTokenExpired):
http.Error(w, "Token expired", http.StatusUnauthorized)
case errors.Is(err, jwt.ErrTokenSignatureInvalid):
http.Error(w, "Invalid signature", http.StatusUnauthorized)
case errors.Is(err, jwt.ErrTokenMalformed):
http.Error(w, "Malformed token", http.StatusBadRequest)
default:
http.Error(w, "Authentication failed", http.StatusUnauthorized)
}
return
}
if !token.Valid {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Process valid token
}// Support both JWT and API keys
func multiAuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Try JWT first
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
jwtKeyFunc,
)
if err == nil && token.Valid {
// JWT authentication successful
next.ServeHTTP(w, r)
return
}
// Try API key
apiKey := r.Header.Get("X-API-Key")
if validateAPIKey(apiKey) {
next.ServeHTTP(w, r)
return
}
http.Error(w, "Unauthorized", http.StatusUnauthorized)
})
}func requireRole(role string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
keyFunc,
)
if err != nil || !token.Valid {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
claims := token.Claims.(jwt.MapClaims)
userRole, ok := claims["role"].(string)
if !ok || userRole != role {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}func optionalAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := request.ParseFromRequest(r,
request.AuthorizationHeaderExtractor,
keyFunc,
)
// Add token to context if present and valid
if err == nil && token.Valid {
ctx := context.WithValue(r.Context(), claimsContextKey, token.Claims)
r = r.WithContext(ctx)
}
// Continue regardless of token presence
next.ServeHTTP(w, r)
})
}Install with Tessl CLI
npx tessl i tessl/golang-github-com-golang-jwt--jwt--v5