2026-06-22 16:06:57 +02:00
|
|
|
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) {
|
2026-06-22 16:51:18 +02:00
|
|
|
if len(authHeader) <= 7 || !strings.EqualFold(authHeader[:7], "Bearer ") {
|
2026-06-22 16:06:57 +02:00
|
|
|
return "", false
|
|
|
|
|
}
|
2026-06-22 16:51:18 +02:00
|
|
|
return strings.TrimSpace(authHeader[7:]), true
|
2026-06-22 16:06:57 +02:00
|
|
|
}
|