golangJwtAuth

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2025 License: MIT Imports: 21 Imported by: 0

README

JWT Auth (Golang)

A JWT authentication package providing both Access Token and Refresh Token mechanisms, featuring fingerprint recognition, Redis storage, and automatic refresh functionality.
Node.js version can be found here

version

Features

  • Dual Token System
    • Access Token (short-lived, default 15 minutes) + Refresh Token (long-lived, default 7 days)
    • Automatic token refresh without re-login
    • ES256 algorithm (Elliptic Curve Digital Signature) for high security
  • Device Fingerprinting
    • Generate unique fingerprints based on user agent, device ID, OS, and browser
    • Prevent token abuse across different devices
    • Automatic device type detection (desktop, mobile, tablet)
    • Persistent device ID tracking with automatic cookie management
  • Security Protection
    • Token revocation: Add access tokens to blacklist on logout
    • Version control: Prevent replay attacks, auto-generate new ID after 5 refreshes by default
    • Smart refresh: Auto-regenerate based on remaining TTL (default under 50%)
    • Concurrency protection: Redis lock mechanism prevents concurrent refresh conflicts
  • Zero Configuration
    • Auto-generate ECDSA key pairs on first startup if not provided
    • Multiple authentication methods: Cookie, Bearer Token, custom headers
    • Flexible deployment: Support for development/testing/production environments
    • Middleware support: Gin and standard HTTP middleware

Dependencies

Usage

  • Installation
    go get github.com/pardnchiu/golang-jwt-auth
    
  • Minimal Configuration
    package main
    
    import (
      "log"
    
      "github.com/pardnchiu/golang-jwt-auth"
    )
    
    func main() {
      // Minimal configuration - keys will be auto-generated
      config := &golangJwtAuth.Config{
          Redis: golangJwtAuth.Redis{
              Host: "localhost",
              Port: 6379,
              DB:   0,
          },
      }
    
      jwtAuth, err := golangJwtAuth.New(config)
      if err != nil {
          log.Fatal("Initialization failed:", err)
      }
      defer jwtAuth.Close()
    
      // Start using...
    }
    
  • Complete Configuration Example
    type Config struct {
      // Redis connection configuration (required)
      Redis Redis {
          Host     string  // Redis host address (required)
          Port     int     // Redis port (required)
          Password string  // Redis password (default: "")
          DB       int     // Redis database number (required)
      }
    
      // User authentication callback (optional)
      CheckAuth func(Auth) (bool, error)  // User authentication function (default: nil)
    
      // File path configuration (optional)
      File *File {
          PrivateKeyPath string  // Private key file path (default: "")
          PublicKeyPath  string  // Public key file path (default: "")
      }
    
      // Log configuration (optional)
      Log *Log {
          Path    string  // Log file path (default: "./logs/golangJwtAuth")
          Stdout  bool    // Whether to output to stdout (default: false)
          MaxSize int64   // Maximum log file size (default: 16777216 = 16MB)
      }
    
      // System parameter configuration (optional)
      Option *Option {
          PrivateKey           string        // Private key content (default: "")
          PublicKey            string        // Public key content (default: "")
          AccessTokenExpires   time.Duration // Access Token expiration (default: 15 * time.Minute)
          RefreshIdExpires     time.Duration // Refresh ID expiration (default: 7 * 24 * time.Hour)
          AccessTokenCookieKey string        // Access Token Cookie name (default: "access_token")
          RefreshIdCookieKey   string        // Refresh ID Cookie name (default: "refresh_id")
          MaxVersion           int           // Maximum refresh version count (default: 5)
          RefreshTTL           float64       // Refresh TTL threshold (default: 0.5)
      }
    
      // Cookie security configuration (optional)
      Cookie *Cookie {
          Domain   *string        // Cookie domain (default: nil)
          Path     *string        // Cookie path (default: nil, uses "/")
          SameSite *http.SameSite // SameSite attribute (default: nil, uses Lax)
          Secure   *bool          // HTTPS only transmission (default: nil, uses false)
          HttpOnly *bool          // Disable JavaScript access (default: nil, uses true)
      }
    }
    

API Usage Guide

  • Create - Create()
    func loginHandler(jwtAuth *golangJwtAuth.JWTAuth) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            // After validating login credentials...
    
            userData := &golangJwtAuth.Auth{
                ID:        "user123",
                Name:      "John Doe",
                Email:     "john@example.com",
                Thumbnail: "avatar.jpg",
                Role:      "user",
                Level:     1,
                Scope:     []string{"read", "write"},
            }
    
            result := jwtAuth.Create(w, r, userData)
            if !result.Success {
                w.WriteHeader(result.StatusCode)
                json.NewEncoder(w).Encode(map[string]string{
                    "error": result.Error,
                })
                return
            }
    
            // Automatically set cookies and return tokens
            json.NewEncoder(w).Encode(map[string]interface{}{
                "success":    true,
                "token":      result.Token.Token,
                "refresh_id": result.Token.RefreshId,
                "user":       result.Data,
            })
        }
    }
    
  • Verify - Verify()
    func protectedHandler(jwtAuth *golangJwtAuth.JWTAuth) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            result := jwtAuth.Verify(w, r)
    
            if !result.Success {
                w.WriteHeader(result.StatusCode)
                json.NewEncoder(w).Encode(map[string]string{
                    "error":     result.Error,
                    "error_tag": result.ErrorTag,
                })
                return
            }
    
            // Use authenticated user data
            user := result.Data
            json.NewEncoder(w).Encode(map[string]interface{}{
                "message": "Protected resource access successful",
                "user":    user,
            })
        }
    }
    
  • Revoke - Revoke()
    func logoutHandler(jwtAuth *golangJwtAuth.JWTAuth) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            result := jwtAuth.Revoke(w, r)
            if !result.Success {
                w.WriteHeader(result.StatusCode)
                json.NewEncoder(w).Encode(map[string]string{
                    "error":     result.Error,
                    "error_tag": result.ErrorTag,
                })
                return
            }
    
            json.NewEncoder(w).Encode(map[string]string{
                "message": "Successfully logged out",
            })
        }
    }
    
  • Middleware
    • Gin Framework
      package main
      
      import (
          "github.com/gin-gonic/gin"
          "github.com/pardnchiu/golang-jwt-auth"
      )
      
      func main() {
          // Initialize jwtAuth...
      
          r := gin.Default()
      
          // Protected route group
          protected := r.Group("/api/protected")
          protected.Use(jwtAuth.GinMiddleware())
          {
              protected.GET("/profile", func(c *gin.Context) {
                  // Get user data from Context
                  user, exists := golangJwtAuth.GetAuthDataFromGinContext(c)
                  if !exists {
                      c.JSON(500, gin.H{"error": "Unable to retrieve user data"})
                      return
                  }
      
                  c.JSON(200, gin.H{"user": user})
              })
          }
      
          r.Run(":8080")
      }
      
    • Standard HTTP
      package main
      
      import (
          "net/http"
          "github.com/pardnchiu/golang-jwt-auth"
      )
      
      func main() {
          // Initialize jwtAuth...
      
          mux := http.NewServeMux()
      
          mux.HandleFunc("/api/profile", func(w http.ResponseWriter, r *http.Request) {
              // Get user data from Request Context
              user, exists := golangJwtAuth.GetAuthDataFromHTTPRequest(r)
              if !exists {
                  http.Error(w, "Unable to retrieve user data", http.StatusInternalServerError)
                  return
              }
      
              json.NewEncoder(w).Encode(map[string]interface{}{"user": user})
          })
      
          // Apply middleware
          server := &http.Server{
              Addr:    ":8080",
              Handler: jwtAuth.HTTPMiddleware(mux),
          }
      
          server.ListenAndServe()
      }
      

Security Features

  • Device Fingerprinting: Generate unique fingerprints based on user agent, device ID, OS, browser, and device type with persistent tracking
  • Token Revocation: Add tokens to blacklist on logout
  • Automatic Expiration: Support TTL auto-cleanup for expired tokens
  • Version Control: Track refresh token versions to prevent replay attacks
  • Fingerprint Verification: Ensure tokens can only be used on the same device/browser
  • Auto Key Generation: Automatically generate secure ECDSA key pairs if not provided

Automatic Token Refresh Mechanism

The system automatically refreshes tokens in the following situations:

  1. Access Token Expired - Automatically update using refresh token
  2. Version Limit Reached - Regenerate refresh ID (default 5 times)
  3. TTL Below Threshold - Auto-regenerate based on remaining TTL (default under 50%)

New tokens are returned via:

  • HTTP Headers: X-New-Access-Token, X-New-Refresh-ID
  • Cookies: Automatic updates
  • Concurrency Safe: Redis locks prevent duplicate refreshes

Supported Authentication Methods

Method Format Priority
Custom Fingerprint X-Device-FP: <fingerprint> 1
Refresh ID X-Refresh-ID: <refresh_id> 1
Device ID X-Device-ID: <device_id> 1
Bearer Token Authorization: Bearer <token> 1
Cookie Automatically read configured cookie names 2

Error Handling

All major methods return a JWTAuthResult structure:

type JWTAuthResult struct {
    StatusCode int          `json:"status_code"`         // HTTP status code
    Success    bool         `json:"success"`             // Whether operation succeeded
    Data       *Auth        `json:"data,omitempty"`      // User data
    Token      *TokenResult `json:"token,omitempty"`     // Token information
    Error      string       `json:"error,omitempty"`     // Error message
    ErrorTag   string       `json:"error_tag,omitempty"` // Error classification tag
}
Error Tags
  • data_missing - Required data not provided
  • data_invalid - Invalid data format
  • unauthorized - Authentication failed
  • revoked - Token has been revoked
  • not_found - Resource not found
  • not_matched - Data mismatch
  • failed_to_update - Update operation failed
  • failed_to_create - Creation operation failed
  • failed_to_sign - Token signing failed
  • failed_to_store - Storage operation failed
  • failed_to_get - Retrieval operation failed

License

This source code project is licensed under the MIT license.

Creator

邱敬幃 Pardn Chiu


©️ 2025 邱敬幃 Pardn Chiu

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Auth added in v0.6.0

type Auth struct {
	ID        string   `json:"id"`
	Name      string   `json:"name"`
	Email     string   `json:"email"`
	Thumbnail string   `json:"thumbnail,omitempty"`
	Scope     []string `json:"scope,omitempty"`
	Role      string   `json:"role,omitempty"`
	Level     int      `json:"level,omitempty"`
}

func GetAuthDataFromGinContext

func GetAuthDataFromGinContext(c *gin.Context) (*Auth, bool)

func GetAuthDataFromHTTPRequest

func GetAuthDataFromHTTPRequest(r *http.Request) (*Auth, bool)

type Config

type Config struct {
	Redis     Redis                    `json:"redis"`               // Redis 設定
	CheckAuth func(Auth) (bool, error) `json:"-"`                   // 檢查使用者是否存在的函數
	File      *File                    `json:"file,omitempty"`      // 檔案設定
	Log       *Log                     `json:"log,omitempty"`       // 日誌設定
	Option    *Option                  `json:"parameter,omitempty"` // 可調參數
	Cookie    *Cookie                  `json:"cookie,omitempty"`    // Cookie 設定
}

Config 設定結構

type Cookie struct {
	Domain   *string        `json:"domain,omitempty"`    // Cookie 的網域
	Path     *string        `json:"path,omitempty"`      // Cookie 的路徑,預設 /
	SameSite *http.SameSite `json:"same_site,omitempty"` // Cookie 的 SameSite 屬性,預設 lax
	Secure   *bool          `json:"secure,omitempty"`    // Cookie 是否安全,預設 false
	HttpOnly *bool          `json:"http_only,omitempty"` // Cookie 是否 HttpOnly,預設 true
}

type File added in v0.6.0

type File struct {
	PrivateKeyPath string `json:"private_key_path,omitempty"`
	PublicKeyPath  string `json:"public_key_path,omitempty"`
}

type JWTAuth

type JWTAuth struct {
	// contains filtered or unexported fields
}

JWTAuth JWT 驗證主結構

func New

func New(c *Config) (*JWTAuth, error)

func (*JWTAuth) Close

func (j *JWTAuth) Close() error

func (*JWTAuth) Create

func (j *JWTAuth) Create(w http.ResponseWriter, r *http.Request, auth *Auth) JWTAuthResult

func (*JWTAuth) GinMiddleware

func (j *JWTAuth) GinMiddleware() gin.HandlerFunc

func (*JWTAuth) HTTPMiddleware

func (j *JWTAuth) HTTPMiddleware(next http.Handler) http.Handler

func (*JWTAuth) Revoke

func (*JWTAuth) Verify

type JWTAuthResult added in v0.6.0

type JWTAuthResult struct {
	StatusCode int          `json:"status_code"`
	Success    bool         `json:"success"`
	Data       *Auth        `json:"data,omitempty"`
	Token      *TokenResult `json:"token,omitempty"`
	Error      string       `json:"error,omitempty"`
	ErrorTag   string       `json:"error_tag,omitempty"`
}

type Log added in v0.6.0

type Log struct {
	Path    string `json:"path,omitempty"`     // 日誌檔案路徑,預設 `./logs/golangJwtAuth`
	Stdout  bool   `json:"stdout,omitempty"`   // 是否輸出到標準輸出,預設 false
	MaxSize int64  `json:"max_size,omitempty"` // 日誌檔案最大大小(位元組),預設 16 * 1024 * 1024
}

type Logger added in v0.4.0

type Logger struct {
	Stdout       bool
	DebugLogger  *log.Logger
	OutputLogger *log.Logger
	ErrorLogger  *log.Logger
	Path         string
	File         []*os.File

	MaxSize int64
	Closed  bool
	// contains filtered or unexported fields
}

func (*Logger) Close added in v0.5.0

func (l *Logger) Close() error

func (*Logger) Critical added in v0.5.0

func (l *Logger) Critical(err error, messages ...string) error

func (*Logger) Debug added in v0.5.0

func (l *Logger) Debug(messages ...string)

func (*Logger) Error added in v0.5.0

func (l *Logger) Error(err error, messages ...string) error

func (*Logger) Fatal added in v0.5.0

func (l *Logger) Fatal(err error, messages ...string) error

func (*Logger) Flush added in v0.5.0

func (l *Logger) Flush() error

func (*Logger) Info added in v0.5.0

func (l *Logger) Info(messages ...string)

func (*Logger) Notice added in v0.5.0

func (l *Logger) Notice(messages ...string)

func (*Logger) Trace added in v0.5.0

func (l *Logger) Trace(messages ...string)

func (*Logger) Warning added in v0.5.0

func (l *Logger) Warning(messages ...string)

type LoggerConfig added in v0.5.0

type LoggerConfig struct {
	Path    string
	MaxSize int64
	Stdout  bool
}

type Option added in v0.6.0

type Option struct {
	PrivateKey           string        `json:"private_key,omitempty"`             // 私鑰內容
	PublicKey            string        `json:"public_key,omitempty"`              // 公鑰內容
	AccessTokenExpires   time.Duration `json:"access_token_expires,omitempty"`    // Access Token 有效期限,預設 15 分鐘
	RefreshIdExpires     time.Duration `json:"refresh_id_expires,omitempty"`      // Refresh ID 有效期限,預設 7 天
	AccessTokenCookieKey string        `json:"access_token_cookie_key,omitempty"` // Access Token Cookie 鍵名,預設 access_token
	RefreshIdCookieKey   string        `json:"refresh_id_cookie_key,omitempty"`   // Refresh ID Cookie 鍵名,預設 refresh_id
	MaxVersion           int           `json:"max_version,omitempty"`             // 重刷 Refresh ID 次數,預設 5(更換 5 次 Access Token 後,Refresh ID 會被重刷)
	RefreshTTL           float64       `json:"refresh_ttl,omitempty"`             // 刷新 Refresh ID 的 TTL 閾值,預設 0.5(低於一半時間)
}

type Pem added in v0.6.0

type Pem struct {
	// contains filtered or unexported fields
}

type Redis added in v0.6.0

type Redis struct {
	Host     string `json:"host"`               // Redis 主機位址
	Port     int    `json:"port"`               // Redis 連接埠
	Password string `json:"password,omitempty"` // Redis 密碼
	DB       int    `json:"db"`                 // Redis 資料庫編號
}

Redis Redis 設定結構

type RefreshData

type RefreshData struct {
	Data        *Auth  `json:"data,omitempty"`
	Version     int    `json:"version"`
	Fingerprint string `json:"fp"`
	Exp         int64  `json:"exp"`
	Iat         int64  `json:"iat"`
	Jti         string `json:"jti"`
}

type RefreshId added in v0.6.0

type RefreshId struct {
	ID          string `json:"id"`    // 使用者 ID
	Name        string `json:"name"`  // 使用者名稱
	Email       string `json:"email"` // 電子郵件
	Fingerprint string `json:"fp"`    // 設備指紋
	Iat         int64  `json:"iat"`   // 發行時間
	Jti         string `json:"jti"`   // JWT ID
}

Refresh Data ID 結構

type TokenResult

type TokenResult struct {
	Token     string `json:"token"`
	RefreshId string `json:"refresh_id"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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