64 lines
middleware/claims.go
JWT claim validation: parses and verifies the token's signature, audience, issuer, and expiry.
// Package middleware provides JWT-based request authentication for HTTP services.
package middleware
 
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"strings"
)
 
// Claims holds the standard JWT payload fields validated by the middleware.
type Claims struct {
	Sub string `json:"sub"`
	Iss string `json:"iss"`
	Aud string `json:"aud"`
	Exp int64  `json:"exp"` // Unix timestamp; the token is invalid after this time.
}
 
var (
	ErrMalformedToken = errors.New("jwt: malformed token")
	ErrInvalidSig     = errors.New("jwt: signature verification failed")
	ErrWrongAudience  = errors.New("jwt: audience mismatch")
	ErrWrongIssuer    = errors.New("jwt: issuer mismatch")
	ErrExpiredToken   = errors.New("jwt: token expired")
)
 
// ValidateClaims parses a raw JWT string and returns the verified payload.
// Returns an error if the token is malformed, the HMAC-SHA256 signature does not
// match the provided secret, the audience or issuer claim is wrong, or the token is expired.
// Parameters: token — the raw dot-separated JWT; secret — HMAC-SHA256 signing key;
// wantAud — expected audience value; wantIss — expected issuer value.
func ValidateClaims(token, secret, wantAud, wantIss string) (*Claims, error) {
	parts := strings.Split(token, ".")
	if len(parts) != 3 {
		return nil, ErrMalformedToken
	}
 
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(parts[0] + "." + parts[1]))
	expectedSig := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
	if !hmac.Equal([]byte(expectedSig), []byte(parts[2])) {
		return nil, ErrInvalidSig
	}
 
	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return nil, ErrMalformedToken
	}
	var c Claims
	if err := json.Unmarshal(payload, &c); err != nil {
		return nil, ErrMalformedToken
	}
 
	if c.Aud != wantAud {
		return nil, ErrWrongAudience
	}
	if c.Iss != wantIss {
		return nil, ErrWrongIssuer
	}
 
	return &c, nil
}