gwmiddleware

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 8 Imported by: 0

README

github.com/mawarpay/pkg-gwmiddleware

Shared Gin middleware for the IlonaPay API gateway. The package provides:

  • required merchant integration-header checks;
  • merchant API-key validation through a consumer-owned repository;
  • public-client IP allowlisting through a consumer-owned repository; and
  • typed helpers for API-key context.

The package does not own database models, open database connections, register routes, or authenticate the merchant integration headers.

Installation

For a published version:

go get github.com/mawarpay/pkg-gwmiddleware@latest

API

Middleware
Function Result
RequireHeaders(names...) Requires every named header to contain a non-whitespace value
RequireMerchantAPIHeaders() Requires X-MERCHANT-ID, X-PAY-METHOD-ID, and X-USER-ID
APIKey(repo, cfg) Validates the configured API-key header and adds merchant data to the Gin context
IPWhitelist(repo) Bypasses loopback/private addresses and checks public addresses with the repository
Context helpers

These helpers return data set by a successful APIKey call:

Function Result
GetMerchantID(c) Validated merchant_id and whether it exists with the expected uint64 type
GetApiKeyRecord(c) Consumer-specific ApiKeyInfo.Record and whether it exists
Headers
Constant Value
HeaderMerchantID X-MERCHANT-ID
HeaderPayMethodID X-PAY-METHOD-ID
HeaderUserID X-USER-ID
DefaultAPIKeyHeader X-API-KEY

Usage

Implement the repository interfaces in the consuming gateway. This package deliberately stays independent of gateway entities and database packages.

type apiKeyRepo struct {
    // Site-database dependency for api_keys.
}

func (r *apiKeyRepo) GetByKeyAndValidAt(
    ctx context.Context,
    key string,
    at time.Time,
) (gwmiddleware.ApiKeyInfo, error) {
    // Translate the gateway entity into ApiKeyInfo.
}

func (r *apiKeyRepo) UpdateLastUsedAt(
    ctx context.Context,
    id uint64,
    at time.Time,
) error {
    // Persist last_used_at.
}

type ipWhitelistRepo struct {
    // Site-database dependency for ip_whitelists.
}

func (r *ipWhitelistRepo) IsAllowed(
    ctx context.Context,
    ip netip.Addr,
    at time.Time,
) (bool, error) {
    // Return true only for an active matching entry.
}

Mount only the policies required by a route or route group:

import gwmiddleware "github.com/mawarpay/pkg-gwmiddleware"

merchant := router.Group("/api/v2")

// Presence validation only; see Security boundaries below.
merchant.Use(gwmiddleware.RequireMerchantAPIHeaders())

// Optional policies after their repository adapters are wired.
merchant.Use(gwmiddleware.IPWhitelist(ipWhitelistRepo))
merchant.Use(gwmiddleware.APIKey(apiKeyRepo, gwmiddleware.APIKeyConfig{
    Header:      gwmiddleware.DefaultAPIKeyHeader,
    UpdateUsage: true,
}))

ApiKeyInfo.Record may contain a gateway-specific entity. Retrieve it with GetApiKeyRecord and type-assert it in the consumer.

Runtime behaviour

Middleware Condition Result
RequireHeaders A header is absent, empty, or whitespace-only Aborts with 422 on the first missing header
APIKey Repository is nil Aborts with 503
APIKey API-key header is missing Aborts with 422
APIKey Lookup fails or returns ApiKeyInfo.ID == 0 Aborts with 422 as invalid or expired
APIKey UpdateUsage is enabled Updates usage synchronously with a 300 ms timeout
APIKey Usage update fails Logs the failure and continues the request
IPWhitelist Address is loopback or private Continues without a repository lookup
IPWhitelist Client IP cannot be parsed Aborts with 403
IPWhitelist Public-IP lookup fails or denies access Aborts with 403

IPWhitelist requires a non-nil repository for requests from public addresses. Repository errors fail closed.

Security boundaries

  • RequireMerchantAPIHeaders validates presence only. It does not verify header values, compare X-MERCHANT-ID with API-key context, or authenticate the caller.
  • APIKey validates the key and stores merchant context, but it does not rewrite or validate the three merchant integration headers.
  • IPWhitelist uses Gin's ClientIP(). The gateway must configure trusted proxies correctly before relying on forwarded client-IP headers.
  • Loopback and private addresses bypass the IP repository by design. Account for that trust boundary in the deployment network.
  • Do not log API-key values. Current middleware logs only key and merchant IDs.

Current gateway integration

The source of truth is api-gateway/internal/routes/routes.go. At present, merchant routes use RequireMerchantAPIHeaders() through the authMerchantHeaders policy. APIKey and IPWhitelist are available here but are not mounted by the gateway; they require repository adapters and gateway tests before activation.

AI contributor context

Use this section when an AI coding agent or a new contributor changes this package.

Package map
File Responsibility
headers.go Integration-header constants and presence middleware
api_key.go API-key validation, usage updates, and context helpers
ip_whitelist.go Client-IP parsing and allowlist decisions
types.go Public configuration, repository interfaces, and context types
*_test.go Executable behaviour contract with repository fakes
../../api-gateway/internal/routes/routes.go Actual gateway policy wiring
Invariants
  1. Keep storage and service entities outside this module; extend the small repository interfaces only when required by middleware behaviour.
  2. Treat routes.go, not README route examples, as the source of truth for active gateway policies.
  3. Do not describe merchant integration headers as authentication. Presence checks and API-key validation are separate controls.
  4. Preserve fail-closed behaviour for public-IP lookup errors and invalid addresses.
  5. Keep context keys private and expose context data through helper functions.
  6. Add or update focused unit tests for every status, control-flow, or context behaviour change.
  7. If gateway wiring changes, update the gateway route tests and the matching platform documentation in the monorepo.
Verification

From this directory:

gofmt -w *.go
go vet ./...
go test ./...

Tests use mocked repositories and require neither MySQL nor Redis.

Documentation

Index

Constants

View Source
const (
	HeaderMerchantID  = "X-MERCHANT-ID"
	HeaderUserID      = "X-USER-ID"
	HeaderPayMethodID = "X-PAY-METHOD-ID"
)

Merchant integration headers (gateway → production services).

View Source
const APIKeyUsageUpdateTimeout = 300 * time.Millisecond
View Source
const DefaultAPIKeyHeader = "X-API-KEY"

DefaultAPIKeyHeader is the default request header carrying the merchant API key.

Variables

MerchantAPIHeaders lists required headers on merchant API routes.

Functions

func APIKey

func APIKey(repo ApiKeyRepository, cfg APIKeyConfig) gin.HandlerFunc

APIKey validates the header against the api_keys table and attaches merchant context.

func GetApiKeyRecord

func GetApiKeyRecord(c *gin.Context) (any, bool)

GetApiKeyRecord returns the repository record stored by APIKey middleware (service-specific type).

func GetMerchantID

func GetMerchantID(c *gin.Context) (merchantID uint64, ok bool)

GetMerchantID returns the merchant_id set by APIKey middleware.

func IPWhitelist

func IPWhitelist(repo IPWhitelistRepository) gin.HandlerFunc

IPWhitelist allows loopback/private IPs without a DB check; other IPs must match the whitelist repository.

func RequireHeaders

func RequireHeaders(headers ...string) gin.HandlerFunc

RequireHeaders aborts with 422 when any listed header is missing or empty.

func RequireMerchantAPIHeaders

func RequireMerchantAPIHeaders() gin.HandlerFunc

RequireMerchantAPIHeaders requires X-MERCHANT-ID, X-PAY-METHOD-ID, and X-USER-ID.

Types

type APIKeyConfig

type APIKeyConfig struct {
	Header      string
	UpdateUsage bool
}

APIKeyConfig configures API key middleware.

type ApiKeyInfo

type ApiKeyInfo struct {
	ID         uint64
	MerchantID uint64
	Record     any
}

ApiKeyInfo is the validated key context stored on the Gin request.

type ApiKeyRepository

type ApiKeyRepository interface {
	GetByKeyAndValidAt(ctx context.Context, key string, at time.Time) (ApiKeyInfo, error)
	UpdateLastUsedAt(ctx context.Context, id uint64, at time.Time) error
}

ApiKeyRepository validates merchant API keys (typically site DB api_keys table).

type IPWhitelistRepository

type IPWhitelistRepository interface {
	IsAllowed(ctx context.Context, ip netip.Addr, at time.Time) (bool, error)
}

IPWhitelistRepository checks whether a client IP is globally whitelisted.

Jump to

Keyboard shortcuts

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