// Package session owns opaque server sessions: minting bearer tokens, resolving // them to accounts through a write-through in-memory cache, and revoking them. // Only the SHA-256 hash of a token is persisted; the plaintext is returned to // the caller once and never stored. Sessions are revoke-only (no TTL). package session import ( "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" ) // tokenBytes is the entropy of an opaque session token: 256 bits. const tokenBytes = 32 // GenerateToken returns a fresh random opaque token (URL-safe base64, 256-bit) // together with its hex-encoded SHA-256 hash for storage. The plaintext token // is handed to the caller once and is never persisted. func GenerateToken() (token, tokenHash string, err error) { buf := make([]byte, tokenBytes) if _, err := rand.Read(buf); err != nil { return "", "", fmt.Errorf("session: read random token: %w", err) } token = base64.RawURLEncoding.EncodeToString(buf) return token, HashToken(token), nil } // HashToken returns the hex-encoded SHA-256 of token. Lookups hash the presented // token and compare against the stored hash. func HashToken(token string) string { sum := sha256.Sum256([]byte(token)) return hex.EncodeToString(sum[:]) }