How to Implement API Request Signing Verification in Gorig

Gorig provides a ready-to-use signing middleware that validates JWT tokens from HTTP headers, verifies signatures using the tokenx service, and injects authenticated user context into Gin handlers.

The jom-io/gorig framework includes a complete API request signing verification system built on Gin middleware. This implementation handles JWT extraction, signature validation, and user context injection without requiring custom security logic in your handlers. The architecture cleanly separates token generation, storage, and request-time verification, allowing you to swap between in-memory and Redis backends without modifying business logic.

How API Request Signing Verification Works

The verification flow in httpx/mid.sign.go executes seven distinct operations for every protected request.

  1. Extract the token. The GetTokenByCtx function retrieves the JWT from the Authorization: Bearer <token> header. Missing or malformed headers trigger response.ErrorTokenBaseInfo.

  2. Locate the token manager. The middleware accepts a tokenx.ManagerType (Memory or Redis) and invokes tokenx.Get to retrieve a TokenService containing a generator and a manager.

  3. Validate token existence. The TokenManager.GetUserID method checks the token's presence in the selected store. Unknown tokens return ErrorTokenAuthFail.

  4. Parse and verify the JWT. The TokenGenerator (implemented in mid/tokenx/jwt.generator.go) validates the token's signature and claims. Errors map to specific ErrorToken* responses.

  5. Apply optional user filters. If a userFilter map is supplied, filterUserInfo validates that the token's user payload contains required fields (e.g., consts.NotNull). Failures result in a 403 Forbidden response.

  6. Set request context. On success, the middleware stores the raw token (consts.TokenKey) and decoded user data using apix.SetUserID and apix.SetUserInfo for downstream handlers.

  7. Continue processing. Finally, c.Next() passes control to the actual API handler.

Core Components for Request Signing

  • httpx/mid.sign.go: Contains the Gin middleware that orchestrates token extraction, validation, and context injection.

  • mid/tokenx/serv.go: Factory that assembles a TokenService with the configured generator and manager implementations.

  • mid/tokenx/jwt.generator.go: Implements JWT creation and signature verification through the Generate and Parse methods.

  • mid/tokenx/mem.manager.go: In-memory token manager that handles token recording, expiration, and user-ID lookups.

  • apix/handle.go: Provides SetUserID and SetUserInfo helpers for injecting user data into the Gin context.

  • global/consts/consts.go: Defines token status codes (JwtTokenOK, JwtTokenExpired) and special filter values like NotNull.

Implementing API Request Signing Verification

Configuring the Signing Middleware

Attach the middleware to your Gin router using httpx.SignDef() for in-memory storage or httpx.SignRedis() for Redis-backed sessions. For role-based access control, use httpx.SignUserDef() with a filter map that specifies required user claims.

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/jom-io/gorig/httpx"
    "github.com/jom-io/gorig/global/consts"
)

func main() {
    r := gin.Default()

    // Basic memory-based verification
    r.Use(httpx.SignDef())

    // Or with Redis backend
    // r.Use(httpx.SignRedis())

    // Or with user filtering: require role="admin" and non-empty dept
    adminFilter := map[string]interface{}{
        "role": "admin",
        "dept": consts.NotNull,
    }
    r.Use(httpx.SignUserDef(adminFilter))

    r.GET("/protected", protectedHandler)
    r.Run(":8080")
}

Issuing and Recording Tokens

In your authentication endpoint, generate a JWT using the TokenGenerator and record it with the TokenManager so subsequent requests can validate against it.

package auth

import (
    "github.com/gin-gonic/gin"
    "github.com/jom-io/gorig/mid/tokenx"
    "github.com/jom-io/gorig/utils/configure"
)

func LoginHandler(c *gin.Context) {
    // ... validate credentials ...
    
    userID := "user-123"
    userInfo := map[string]interface{}{
        "role": "admin",
        "dept": "engineering",
    }

    // Get default memory service (or configure for Redis)
    svc := tokenx.GetDef()
    
    // Generate token with 7-day expiration
    expire := int64(configure.GetInt("Jwt.TokenExpireAt", 604800))
    token, err := svc.Generator.Generate(userID, userInfo, expire)
    if err != nil {
        c.JSON(500, gin.H{"error": "token generation failed"})
        return
    }

    // Record in manager for future validation
    svc.Manager.Record(token, userInfo)
    
    c.JSON(200, gin.H{"token": token})
}

Accessing Authenticated User Data

Retrieve the validated user ID and metadata from the Gin context using the apix helpers. The signing middleware has already verified the token and injected this data before your handler executes.

package handler

import (
    "github.com/gin-gonic/gin"
    "github.com/jom-io/gorig/apix"
)

func ProtectedHandler(c *gin.Context) {
    // Extract user data injected by Sign middleware
    userID := apix.GetUserID(c)
    userInfo := apix.GetUserInfo(c).(map[string]interface{})
    
    c.JSON(200, gin.H{
        "userId":   userID,
        "userInfo": userInfo,
        "message":  "Access granted to protected resource",
    })
}

Summary

  • Gorig's signing middleware in httpx/mid.sign.go automates JWT extraction and validation for Gin routers through a seven-step pipeline.
  • The tokenx service separates token generation (jwt.generator.go) from storage (mem.manager.go or Redis), allowing backend swaps without code changes.
  • User filtering via SignUserDef enforces role-based access control by validating claims against required fields like consts.NotNull.
  • Context injection through apix.GetUserID and apix.GetUserInfo makes authenticated data available to downstream handlers without repetitive validation logic.

Frequently Asked Questions

How do I switch from memory to Redis storage for tokens?

Change the middleware function from httpx.SignDef() to httpx.SignRedis() in your router configuration. The tokenx.Get function in mid/tokenx/serv.go automatically returns a TokenService configured with the appropriate manager implementation, so your token generation and validation code remains identical.

What happens when a JWT token expires?

When the TokenGenerator.Parse method in mid/tokenx/jwt.generator.go detects an expired token, it returns an error that maps to ErrorTokenExpired. The signing middleware catches this and returns an HTTP 401 response with the appropriate error code, preventing the request from reaching your handler.

Can I implement custom claim validation beyond the built-in user filter?

Yes. While httpx.SignUserDef handles basic field presence validation using consts.NotNull, you can implement custom logic by wrapping the standard middleware or by validating apix.GetUserInfo inside your handlers. For global custom validation, modify the filterUserInfo function in httpx/mid.sign.go to implement your specific business rules.

How do I retrieve the raw JWT string in my handler?

The signing middleware stores the original token string in the Gin context using the key consts.TokenKey. Access it using c.GetString(consts.TokenKey) if you need to pass the token to downstream microservices or include it in audit logs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →