Skip to content

Core Concepts

This page explains the core abstractions and design model behind CyberGo JWT, helping you build a mental model before diving into code. To start coding right away, jump to Quick Start.

Processor — The Central Type

Processor is the central type of the library, created via jwt.New(cfg). It encapsulates the full logic for token creation, validation, refresh, and revocation. All methods are goroutine-safe — a single instance can be shared across goroutines.

Call Close() when done to securely wipe the secret key and release pooled resources:

go
<!-- check-code: skip -->
cfg := jwt.DefaultConfig()
cfg.SecretKey = "your-32-byte-secret-key-here-minimum"

processor, err := jwt.New(cfg)
if err != nil {
    log.Fatal(err)
}
defer processor.Close()

Processor implements the TokenManager interface, enabling dependency injection and test substitution.

Token Lifecycle

A token moves through the following stages from issuance to invalidation:

text
Issue    Create(claims)           → Access token (short-lived)
         CreateRefresh(claims)     → Refresh token (long-lived)

Verify   Validate(token)          → Claims (checks signature, expiry, issuer, blacklist)

Refresh  Refresh(refreshToken)    → New access token

Revoke   Revoke(token)            → Added to blacklist
Check    IsRevoked(token)         → bool

Each stage returns sentinel errors (e.g. ErrTokenExpired, ErrTokenRevoked) that can be matched precisely with errors.Is(). See Error Handling.

Dual-Layer Token Model

CyberGo JWT uses an access token + refresh token design:

Access TokenRefresh Token
PurposeAPI authenticationObtain new access tokens
Default TTL15 minutes7 days
Issue methodCreateCreateRefresh
Refresh methodRefresh

Why two layers? Access tokens are short-lived, so the risk window is small even if leaked. Refresh tokens are long-lived but only used to obtain new access tokens, never for direct API authentication. This design balances security with user experience — users don't need to log in frequently, and access tokens can be refreshed silently after expiry.

Rotation semantics

Refresh does not automatically revoke the refresh token. The original refresh token remains valid until it expires or is explicitly Revoked. For one-time-use semantics (refresh token rotation), call Revoke on the old refresh token after a successful Refresh. See Token Refresh & Rotation.

Claims Structure

Claims carry the user identity data inside a token. CyberGo JWT provides a two-layer structure:

RegisteredClaims (RFC 7519 standard claims, auto-filled and validated):

FieldclaimDescription
IssuerissIssuer identifier
SubjectsubSubject identifier (also used as rate-limit key)
AudienceaudIntended audience
ExpiresAtexpExpiration time
NotBeforenbfNot-before time
IssuedAtiatIssuance time
IDjtiUnique ID (blacklist key)
TokenTypetoken_typeaccess or refresh

Claims (built-in business claims, embeds RegisteredClaims):

go
<!-- check-code: skip -->
type Claims struct {
    UserID      string         // User ID
    Username    string         // Username
    Role        string         // Role
    Permissions []string       // Permission list
    Scopes      []string       // OAuth scopes
    SessionID   string         // Session ID
    ClientID    string         // Client ID
    Extra       map[string]any // Extra fields
    RegisteredClaims           // Standard claims (embedded)
}

All fields undergo input validation: string length cap of 256, array cap of 100, injection pattern detection (XSS/SQLi signatures).

CustomClaims Interface

When the built-in Claims don't meet your needs, implement the CustomClaims interface to define your own claim structure:

go
<!-- check-code: skip -->
type AppClaims struct {
    UserID string   `json:"user_id"`
    TeamID string   `json:"team_id"`
    Roles  []string `json:"roles,omitempty"`
    jwt.RegisteredClaims
}

func (c *AppClaims) GetRegisteredClaims() *jwt.RegisteredClaims {
    return &c.RegisteredClaims
}

func (c *AppClaims) Validate() error {
    if c.UserID == "" {
        return errors.New("user_id is required")
    }
    return nil
}

Custom types are validated with ValidateInto and refreshed with RefreshInto — the Processor parses the token and populates your struct. See Custom Claims.

Config Overview

Config is the unified configuration entry point for Processor. Start with DefaultConfig() for sensible defaults, then set your signing key:

GroupFieldsDescription
SigningSecretKey / SigningKey / VerificationKey / SigningMethodHMAC uses SecretKey; RSA/ECDSA uses SigningKey
TokenAccessTokenTTL / RefreshTokenTTLAccess and refresh token lifetimes
ValidationIssuer / ExpectedAudience / RequireExpiration / ClockSkewIssuer, audience, mandatory expiry, clock tolerance
SecurityBlacklist / EnableRateLimitRevocation store and rate limiting
ExtensionClockClock injection (for testing)

For algorithm selection see Signing Algorithms; for full field documentation see Configuration.

Extension Interfaces

CyberGo JWT is extensible through interfaces:

InterfacePurpose
TokenManagerCore interface implemented by Processor. Define your own narrower subset (e.g. only Create + Validate) for dependency injection and decoupling
BlacklistStoreCustom blacklist backend (e.g. Redis). Implement Add / Contains / Close to connect external storage
RateLimitProviderCustom rate limiter. Implement Allow / Reset / Close to replace the built-in token bucket
ClockProviderClock injection. FixedClock returns a fixed time for deterministic control over expiry and refresh logic in tests

Next Steps