auth

package
v0.0.0-...-86b21bc Latest Latest
Warning

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

Go to latest
Published: May 8, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package auth implements authentication and authorization for the HTTP surface: token auth, TLS, and (post-v1) OIDC. Every code path here is security-sensitive; see gm-m2-auth formula.

Index

Constants

View Source
const DefaultSessionTTL = 24 * time.Hour

DefaultSessionTTL is how long an issued session cookie is valid.

View Source
const SessionCookieName = "gemba_session"

SessionCookieName is the cookie name the server issues after a successful bearer exchange at POST /api/auth/login.

Variables

This section is empty.

Functions

func BearerAuth

func BearerAuth(v Verifier) func(http.Handler) http.Handler

BearerAuth returns middleware that verifies the Authorization header using v. Retained for call sites (including tests) that only need bearer auth; BearerOrCookieAuth is the full gm-e5.2 surface.

func BearerOrCookieAuth

func BearerOrCookieAuth(v Verifier, cs *CookieSigner) func(http.Handler) http.Handler

BearerOrCookieAuth returns middleware that accepts either:

  • an Authorization: Bearer <token> header whose token v accepts, or
  • a signed session cookie (when cs is non-nil) whose signature matches.

Missing and invalid credentials return 401 with a stable JSON envelope. The middleware runs before route lookup so unauthenticated clients cannot distinguish existing routes from absent ones via 401-vs-404 oracles (gm-b3 / gm-99g).

func BootstrapLoginHandler

func BootstrapLoginHandler(cs *CookieSigner, bs *BootstrapStore) http.Handler

BootstrapLoginHandler exchanges a valid one-time bootstrap token for the same signed session cookie issued by LoginHandler. It intentionally does not accept the primary bearer token; that remains behind /api/auth/login.

func CertFingerprint

func CertFingerprint(der []byte) string

CertFingerprint returns the SHA-256 fingerprint of a DER-encoded certificate as a colon-separated uppercase hex string — the canonical "SHA256:AA:BB:..." form curl and browsers display when an operator inspects a self-signed cert.

func CheckCertKeyPermissions

func CheckCertKeyPermissions(path string) (os.FileMode, string, error)

CheckCertKeyPermissions warns if the operator-supplied key file is world- or group-readable. Returns the permission bits and a non-empty warning string when the file is more permissive than 0600; callers log the warning rather than abort so an operator running under a deliberately-relaxed umask can still boot. Missing files return an error so the caller fails fast.

func DefaultTokenPath

func DefaultTokenPath() (string, error)

DefaultTokenPath returns the on-disk location where the primary token hash lives: ~/.gemba/tokens/primary. Returns an error if the user's home directory cannot be resolved.

func GenerateSelfSignedCert

func GenerateSelfSignedCert(opts SelfSignedCertOptions) (tls.Certificate, string, error)

GenerateSelfSignedCert produces an ephemeral ECDSA P-256 self-signed TLS certificate suitable for `gemba serve --tls-self-signed`. The returned tls.Certificate can be plugged directly into a tls.Config via Certificates; the SHA-256 fingerprint is provided so the operator can pin it (printed at startup per the gm-e5.3 DoD).

ECDSA P-256 is chosen over RSA for speed: a self-signed cert is regenerated on every boot, so a 200ms RSA keygen would slow startup noticeably while contributing nothing to security (the cert is trusted via fingerprint pinning, not chain validation).

func HashToken

func HashToken(token string) (string, error)

HashToken computes an argon2id PHC-formatted hash of token. The returned string contains parameters + salt + hash so Verify does not need any configuration state.

func LoginHandler

func LoginHandler(cs *CookieSigner) http.Handler

LoginHandler returns an http.Handler that issues a signed session cookie. It expects the surrounding middleware to have already verified the request's bearer token; the handler itself just stamps a fresh cookie. Exchanging a valid cookie for a new cookie is harmless and acts as a session refresh.

func NewToken

func NewToken() (string, error)

NewToken returns a hex-encoded 256-bit random token suitable for use as a bearer credential. The return value is the plaintext token and must be shown to the operator exactly once; only its hash should be persisted.

func ReadHash

func ReadHash(path string) (string, error)

ReadHash loads the argon2id PHC string from path. Returns ("", nil) if the file does not exist, so callers can distinguish "no token yet" from genuine I/O failure.

func VerifyHash

func VerifyHash(token, encoded string) (bool, error)

VerifyHash checks token against a PHC-encoded argon2id hash using a constant-time comparison. It is not constant-time against argon2id's own parameters; the parameters themselves are not secret.

func WriteHash

func WriteHash(path, hash string) error

WriteHash writes the PHC-encoded hash to path, creating parent directories with 0700 and the file itself with 0600. Existing file is replaced atomically so a concurrent reader never sees a truncated hash.

Types

type BootstrapStore

type BootstrapStore struct {
	// contains filtered or unexported fields
}

BootstrapStore owns one short-lived, one-time launch token. The token is intended for URL fragments printed at startup; the server never sees the fragment until the SPA explicitly exchanges it.

func NewBootstrapStore

func NewBootstrapStore(token string, expiresAt time.Time) *BootstrapStore

func (*BootstrapStore) Consume

func (b *BootstrapStore) Consume(presented string, now time.Time) bool

type CookieSigner

type CookieSigner struct {
	// contains filtered or unexported fields
}

CookieSigner issues and verifies signed session cookies using HMAC-SHA256 over a randomly generated per-process key.

Cookie format:

base64url(exp_unix_seconds_decimal) + "." + base64url(HMAC-SHA256(key, exp_bytes))

The key is regenerated every process start so restarting the server invalidates every outstanding cookie. Clients recover by re-exchanging their bearer at POST /api/auth/login.

func NewCookieSigner

func NewCookieSigner() (*CookieSigner, error)

NewCookieSigner returns a CookieSigner with a random 256-bit key and the default session TTL. An error is returned only if the system RNG fails.

func (*CookieSigner) Issue

func (c *CookieSigner) Issue(now time.Time) string

Issue builds a signed cookie value whose expiry is now+TTL.

func (*CookieSigner) SetSessionCookie

func (c *CookieSigner) SetSessionCookie(w http.ResponseWriter, r *http.Request, value string)

SetSessionCookie writes the signed cookie onto w with secure defaults. Secure=true is set only when the request arrived over TLS so the cookie still works on the localhost-only default bind.

func (*CookieSigner) TTL

func (c *CookieSigner) TTL() time.Duration

TTL returns how long an issued cookie is valid.

func (*CookieSigner) Verify

func (c *CookieSigner) Verify(value string, now time.Time) bool

Verify returns true if value is a well-formed, signed, unexpired cookie.

type HashVerifier

type HashVerifier struct {
	// contains filtered or unexported fields
}

HashVerifier checks the presented token against an argon2id hash loaded from a file. It re-reads the file when its mtime changes so that `gemba auth token rotate` takes effect without restarting the server.

Rotate-while-running contract:

  • Verify() stats the file on every call; a changed mtime triggers a re-read of the hash before the comparison.
  • If the file is deleted, subsequent Verify() calls return false.

func NewHashVerifier

func NewHashVerifier(path string) *HashVerifier

NewHashVerifier returns a Verifier that reads a PHC argon2id hash from path on demand.

func (*HashVerifier) Verify

func (h *HashVerifier) Verify(presented string) bool

Verify returns true when the stored hash matches presented under argon2id. Returns false (never logging the presented token) if the hash file is missing, unreadable, malformed, or the hash does not match.

type PlainVerifier

type PlainVerifier struct {
	// contains filtered or unexported fields
}

PlainVerifier compares a presented token against a fixed plaintext reference using constant-time comparison. It exists so tests (and the legacy --auth=token flow that accepted a preset --auth-token value) can exercise the middleware without going through the hashed-file path.

func NewPlainVerifier

func NewPlainVerifier(token string) *PlainVerifier

NewPlainVerifier returns a Verifier backed by a fixed token.

func (*PlainVerifier) Verify

func (p *PlainVerifier) Verify(presented string) bool

Verify returns true if presented matches the configured token.

type SelfSignedCertOptions

type SelfSignedCertOptions struct {
	// BindHost, when non-empty, is added to the SAN list. If it parses
	// as an IP it lands in IPAddresses; otherwise it lands in DNSNames.
	// Empty values are dropped.
	BindHost string
	// ExtraIPs supplements the default 127.0.0.1 / ::1 SANs.
	ExtraIPs []net.IP
	// ExtraDNS supplements the default "localhost" SAN.
	ExtraDNS []string
	// ValidFor controls cert lifetime; zero defaults to 1 year per the
	// gm-e5.3 DoD ("regenerates on missing or expired cert").
	ValidFor time.Duration
}

SelfSignedCertOptions configures GenerateSelfSignedCert. The zero value is usable: it produces a cert valid for 1 year with a SAN list that covers "localhost" and 127.0.0.1 / ::1 — enough for a local-only gemba serve. Callers passing --listen with a remote bind should add the bind IP to ExtraIPs so curl / browsers don't reject the SAN.

type Verifier

type Verifier interface {
	Verify(presented string) bool
}

Verifier decides whether a presented bearer token is the expected credential. Implementations MUST use constant-time comparison and must not log the presented token.

Jump to

Keyboard shortcuts

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