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)
})
}