or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

claims.mdindex.mdkey-parsing.mdrequest-extraction.mdsigning-methods.mdtoken-creation.mdtoken-parsing.md
tile.json

request-extraction.mddocs/

HTTP Request Token Extraction

This document covers extracting JWT tokens from HTTP requests using the request package.

Package Import

import "github.com/golang-jwt/jwt/v5/request"

Overview

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.

Main Functions

ParseFromRequest

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 from
  • extractor - The extractor implementation defining how to find the token
  • keyFunc - Key function for signature verification
  • options - Optional parser configuration

Returns: Parsed and verified token or error.

ParseFromRequestWithClaims (Deprecated)

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.

Extractor Interface

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.

Built-in Extractors

BearerExtractor

type BearerExtractor struct{}

Extracts tokens from the Authorization header in the format: Authorization: Bearer <token>

func (e BearerExtractor) ExtractToken(req *http.Request) (string, error)

HeaderExtractor

type HeaderExtractor []string

Extracts 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"}

ArgumentExtractor

type ArgumentExtractor []string

Extracts 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"}

MultiExtractor

type MultiExtractor []Extractor

Combines 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"},
}

PostExtractionFilter

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)

Pre-configured Extractors

AuthorizationHeaderExtractor

var AuthorizationHeaderExtractor *PostExtractionFilter

Extracts bearer tokens from the Authorization header and strips the "Bearer " prefix.

Configured as:

HeaderExtractor{"Authorization"} + stripBearerPrefixFromTokenString filter

OAuth2Extractor

var OAuth2Extractor *MultiExtractor

Standard OAuth 2.0 token extractor. Tries:

  1. Authorization header (with Bearer prefix stripping)
  2. access_token query parameter or form field

Configured as:

MultiExtractor{
    AuthorizationHeaderExtractor,
    ArgumentExtractor{"access_token"},
}

Parser Options

WithClaims

func WithClaims(claims jwt.Claims) ParseFromRequestOption

Parses the token with custom claims implementation.

WithParser

func WithParser(parser *jwt.Parser) ParseFromRequestOption

Uses a custom parser with specific validation options.

Errors

var ErrNoTokenInRequest error

Returned when no token is found in the request. Extractors must return this specific error when a token is not present.

Usage Examples

Basic Bearer Token Extraction

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

OAuth 2.0 Token Extraction

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
}

Custom Header Extraction

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
}

Query Parameter Extraction

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
}

Multiple Extraction Strategies

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
}

With Custom Claims

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
}

With Custom Parser Configuration

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
}

Custom Extractor Implementation

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
}

Post-Extraction Filtering

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
}

Complete Middleware Example

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
}

Error Handling

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
}

Best Practices

Security

  • Always validate the signing method in the key function to prevent algorithm confusion attacks
  • Use HTTPS for all requests containing JWT tokens
  • Don't accept tokens from untrusted sources (validate issuer and audience)
  • Set appropriate token expiration times and validate them

Extraction Strategy

  • Prefer Authorization header (Bearer token) as the primary method
  • Use MultiExtractor for backward compatibility when supporting multiple token locations
  • Avoid query parameters for sensitive tokens (they may be logged)
  • Consider cookies for web applications (with appropriate SameSite and Secure flags)

Error Handling

  • Return appropriate HTTP status codes:
    • 401 Unauthorized: Missing or invalid token
    • 403 Forbidden: Valid token but insufficient permissions
  • Don't expose sensitive error details in production
  • Log authentication failures for security monitoring

Middleware Design

  • Extract and validate tokens in middleware before reaching handlers
  • Add claims to request context for easy access in handlers
  • Create reusable middleware for consistent authentication across routes

Performance

  • Reuse Parser instances when possible
  • Cache public keys for asymmetric algorithms
  • Consider token caching for read-heavy applications (with appropriate invalidation)

Common Patterns

Multiple Authentication Methods

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

Role-Based Access Control

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

Optional Authentication

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