package auth import ( "strings" "time" ) // TokenAuth verifies Bearer credentials against the TokenStore and throttles // brute force. Unlike the login throttle (keyed on username+IP), a Bearer token // has no "login" step to rate-limit, so guesses are throttled by source IP // alone. The window is looser than login's because a legitimate dashboard may // fire many requests in a minute - only repeated *failures* count. type TokenAuth struct { store *TokenStore throttle *failLimiter } // NewTokenAuth wraps a store with an IP-keyed failure throttle. func NewTokenAuth(store *TokenStore) *TokenAuth { return &TokenAuth{store: store, throttle: newFailLimiter(20, time.Minute)} } // Verify resolves a presented Bearer token to its name. throttled is true when // the source IP is in cooldown after too many bad tokens; the caller should // answer 429 without consulting the store. func (a *TokenAuth) Verify(ip, raw string) (name string, ok, throttled bool) { if a.throttle.blocked(ip) { return "", false, true } name, found := a.store.Lookup(raw) if !found { a.throttle.fail(ip) return "", false, false } a.throttle.reset(ip) return name, true, false } // BearerToken extracts the credential from an Authorization header value, // reporting whether it was a Bearer scheme. Returns ("", false) for cookie-only // or unauthenticated requests. func BearerToken(authHeader string) (string, bool) { if len(authHeader) <= 7 || !strings.EqualFold(authHeader[:7], "Bearer ") { return "", false } return strings.TrimSpace(authHeader[7:]), true }