middleware

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 3, 2026 License: MIT Imports: 15 Imported by: 1

README

JWT Middleware for Go

A flexible and feature-rich JWT middleware for Go applications that supports token validation, scope checking, and service-to-service authentication.

For new OpenCHAMI service integrations, start with the Casbin-first AuthN/AuthZ flow in ../docs/getting-started.md and ../docs/casbin-first-guide.md.

This package remains available for compatibility with existing integrations.

Features

  • JWT token validation with support for:
    • RSA key pairs
    • JSON Web Key Sets (JWKS)
    • Automatic JWKS refresh
  • Configurable validation options:
    • Token expiration
    • Issuer validation
    • Audience validation
    • Required claims
  • Scope-based authorization
  • Service-to-service token validation
  • Extensible claims handling
  • Context-based claims access

Installation

go get github.com/openchami/tokensmith/middleware

Quick Start

package main

import (
    "net/http"
    "github.com/openchami/tokensmith/middleware"
)

func main() {
    // Create middleware options
    opts := middleware.DefaultMiddlewareOptions()
    opts.JWKSURL = "https://your-auth-server/.well-known/jwks.json"

    // Create the JWT middleware
    jwtMiddleware := middleware.JWTMiddleware(nil, opts)

    // Create your handler
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Get claims from context
        claims, err := middleware.GetClaimsFromContext(r.Context())
        if err != nil {
            http.Error(w, "Failed to get claims", http.StatusInternalServerError)
            return
        }

        // Use claims
        w.Write([]byte("Hello, " + claims.Subject))
    })

    // Apply middleware
    http.Handle("/protected", jwtMiddleware(handler))
    http.ListenAndServe(":8080", nil)
}

Configuration

MiddlewareOptions
type MiddlewareOptions struct {
    AllowEmptyToken     bool          // Allow requests without a token
    ValidateExpiration  bool          // Enable expiration validation
    ValidateIssuer      bool          // Enable issuer validation
    ValidateAudience    bool          // Enable audience validation
    RequiredClaims      []string      // List of required claims
    JWKSURL            string        // URL to fetch JWKS from
    JWKSRefreshInterval time.Duration // How often to refresh JWKS
}
Default Options
opts := middleware.DefaultMiddlewareOptions()

Usage Examples

Basic Token Validation
opts := middleware.DefaultMiddlewareOptions()
jwtMiddleware := middleware.JWTMiddleware(publicKey, opts)
handler := jwtMiddleware(yourHandler)
Using JWKS
opts := &middleware.MiddlewareOptions{
    JWKSURL:            "https://your-auth-server/.well-known/jwks.json",
    JWKSRefreshInterval: time.Hour,
}
jwtMiddleware := middleware.JWTMiddleware(nil, opts)
Scope-Based Authorization
// Require a single scope
scopeMiddleware := middleware.RequireScope("read")
handler := scopeMiddleware(yourHandler)

// Require multiple scopes
scopesMiddleware := middleware.RequireScopes([]string{"read", "write"})
handler := scopesMiddleware(yourHandler)
Service-to-Service Authentication
// Require service token with specific target service
serviceMiddleware := middleware.RequireServiceToken("target-service")
handler := serviceMiddleware(yourHandler)
Accessing Claims
func handler(w http.ResponseWriter, r *http.Request) {
    // Get standard claims
    claims, err := middleware.GetClaimsFromContext(r.Context())
    if err != nil {
        http.Error(w, "Failed to get claims", http.StatusInternalServerError)
        return
    }

    // Access claim fields
    subject := claims.Subject
    scopes := claims.Scope
    email := claims.Email

    // Get raw claims for custom fields
    rawClaims, err := middleware.GetRawClaimsFromContext(r.Context())
    if err != nil {
        http.Error(w, "Failed to get raw claims", http.StatusInternalServerError)
        return
    }

    // Access custom claims
    customField := rawClaims["custom_field"]
}

Claims Structure

The middleware supports the following standard claims:

type Claims struct {
    Issuer         string   `json:"iss,omitempty"`
    Subject        string   `json:"sub,omitempty"`
    Audience       []string `json:"aud,omitempty"`
    ExpirationTime int64    `json:"exp,omitempty"`
    NotBefore      int64    `json:"nbf,omitempty"`
    IssuedAt       int64    `json:"iat,omitempty"`
    Scope          []string `json:"scope,omitempty"`
    Name           string   `json:"name,omitempty"`
    Email          string   `json:"email,omitempty"`
    EmailVerified  bool     `json:"email_verified,omitempty"`
    ClusterID      string   `json:"cluster_id,omitempty"`
    OpenCHAMIID    string   `json:"openchami_id,omitempty"`
    Groups         []string `json:"groups,omitempty"`
}

Error Handling

The middleware returns appropriate HTTP status codes:

  • 401 Unauthorized: Invalid or missing token
  • 403 Forbidden: Insufficient scopes or invalid service token
  • 500 Internal Server Error: Server-side errors (e.g., JWKS fetch failures)

Best Practices

  1. Always use HTTPS in production
  2. Configure appropriate token expiration times
  3. Use JWKS for dynamic key rotation
  4. Implement proper error handling
  5. Validate all required claims
  6. Use scope-based authorization for fine-grained access control

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetClaimsFromContext

func GetClaimsFromContext(ctx context.Context) (*token.TSClaims, error)

GetClaimsFromContext retrieves the JWT claims from the request context

func GetRawClaimsFromContext deprecated

func GetRawClaimsFromContext(ctx context.Context) (*token.TSClaims, error)

GetRawClaimsFromContext retrieves the raw JWT claims from the request context.

Deprecated: prefer mapping verified claims into an authz.Principal and storing it in context for use by AuthZ middleware.

func JWTMiddleware

func JWTMiddleware(key interface{}, opts *MiddlewareOptions) func(http.Handler) http.Handler

JWTMiddleware creates a new JWT middleware using golang-jwt/jwt/v5

func RequireScope

func RequireScope(requiredScope string) func(http.Handler) http.Handler

RequireScope creates a middleware that checks if the token has the required scope

func RequireScopes

func RequireScopes(requiredScopes []string) func(http.Handler) http.Handler

RequireScopes creates a middleware that checks if the token has all the required scopes

func RequireServiceToken

func RequireServiceToken(requiredService string) func(http.Handler) http.Handler

RequireServiceToken creates middleware that validates service-to-service tokens

Types

type ContextKey

type ContextKey string

ContextKey is the key used to store the claims in the context

const (
	// ClaimsContextKey is the key used to store the claims in the context
	ClaimsContextKey ContextKey = "jwt_claims"
	// RawClaimsContextKey is the key used to store raw claims in the context
	RawClaimsContextKey ContextKey = "jwt_raw_claims"
)

type MiddlewareOptions

type MiddlewareOptions struct {
	// AllowEmptyToken allows requests without a token
	AllowEmptyToken bool
	// ValidateExpiration enables expiration validation
	ValidateExpiration bool
	// ValidateIssuer enables issuer validation
	ValidateIssuer bool
	// ValidateAudience enables audience validation
	ValidateAudience bool
	// RequiredClaims is a list of claims that are required to be present in the token
	RequiredClaims []string
	// JWKSURL is the URL to fetch the JSON Web Key Set from
	JWKSURL string
	// JWKSRefreshInterval is how often to refresh the JWKS cache
	JWKSRefreshInterval time.Duration
	// NonEnforcing allows the middleware to skip validation checks.  It still logs errors.
	NonEnforcing bool
	// PolicyModelFile sets the path to an access model file used by Casbin
	PolicyModelFile string
	// PolicyPermissionsFile sets the path to an user permission file used by Casbin
	PolicyPermissionsFile string
}

MiddlewareOptions contains options for the JWT middleware

func DefaultMiddlewareOptions

func DefaultMiddlewareOptions() *MiddlewareOptions

DefaultMiddlewareOptions returns the default middleware options

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL