·2 min read·← All posts
Go JWT Security Stdlib

Why not a library

The case for stdlib over a JWT library:

What the implementation looks like

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
)

type Issuer struct {
    Secret   []byte
    Issuer   string
    Audience []string
    TTL      time.Duration
}

func (i *Issuer) Issue(userID, email string, roles []Role) (string, Claims, error) {
    now := time.Now().UTC()
    claims := Claims{
        Subject: userID, Email: email, Roles: roles,
        IssuedAt: now.Unix(), ExpiresAt: now.Add(i.TTL).Unix(),
        Issuer: i.Issuer, Audience: i.Audience,
    }
    return encode(jwtHeader{Alg: "HS256", Typ: "JWT"}, claims, i.Secret), claims, nil
}

func (i *Issuer) Verify(token string) (Claims, error) {
    parts := strings.Split(token, ".")
    if len(parts) != 3 { return Claims{}, ErrInvalidToken }
    // ... decode header, recompute signature with hmac.Equal,
    //     decode claims, check exp + iss + aud
}

hmac.Equal is the constant-time comparator — using bytes.Equal would be a timing side-channel.

Genie in production

Genie’s pkg/auth/jwt.go is ~170 lines including all the doc comments and the IssueWithActor variant for RFC 8693 token exchange. Three years in production. Zero CVEs. Zero “I wish we had used a library” moments. The audit footprint is exactly the code in front of you.

If you need RS256, the same story extends — about 50 more lines for crypto/rsa. If you need JWKS rotation, that’s a separate ~80 lines. Still well inside a single well-commented file.

When a library makes sense

For the common case in a Go service that issues its own tokens and verifies its own tokens: stdlib wins.

← Back to all posts