Documentation
¶
Index ¶
- Constants
- Variables
- func ClearSession(w http.ResponseWriter, r *http.Request)
- func ClearTransientCookies(w http.ResponseWriter, r *http.Request, names ...string)
- func ContextWithClaims(ctx context.Context, claims *Claims) context.Context
- func IsLocked(s LockoutState, now time.Time) (bool, time.Time)
- func JTILogValue(jti string) string
- func ReadTransientCookies(r *http.Request, names ...string) map[string]string
- func SetSession(w http.ResponseWriter, r *http.Request, token string, ttl time.Duration)
- func SetTransientCookie(w http.ResponseWriter, r *http.Request, name, value string)
- type Claims
- type ErrAccountLockedT
- type Limiter
- type LockoutPolicy
- type LockoutState
- type Manager
- type OIDCManager
- type OIDCProvider
- type SessionMeta
- type SessionPurger
- type UnlockMode
- type UserFilter
- type UserPatch
Constants ¶
const SessionCookie = "streamline_session"
SessionCookie is the name of the httpOnly cookie that carries the webui session JWT. API clients (Authorization: Bearer, X-API-Key) must NOT use it.
Variables ¶
var ( // ErrPasswordInvalid is returned when the supplied current password does // not match the stored hash. Handlers map to 401. ErrPasswordInvalid = errors.New("current password invalid") // ErrPasswordWeak is returned when a new password fails the strength // policy. Handlers map to 422. ErrPasswordWeak = errors.New("new password fails policy") // ErrAPIKeyNotFound is returned when no key matches the (userID, keyID) // tuple — either the key does not exist or it belongs to another user. // Handlers must not leak ownership information. ErrAPIKeyNotFound = errors.New("api key not found") )
Account-related sentinel errors. Handlers translate these to HTTP codes.
var ( // ErrSelfDeleteForbidden is returned when an admin attempts to delete // their own account. Handlers map to 409 self_delete_forbidden. ErrSelfDeleteForbidden = errors.New("cannot delete yourself") // ErrLastAdmin is returned when demoting or deleting a user would leave // the system without any admin. Handlers map to 409 last_admin. ErrLastAdmin = errors.New("cannot remove the last admin") // ErrUserEmailExists is returned when direct user creation collides with // an existing email. Handlers map to 409 email_exists. ErrUserEmailExists = errors.New("email already registered") // ErrUserNotFound is returned when the target user does not exist. // Handlers map to 404. ErrUserNotFound = errors.New("user not found") // ErrAccountLocked is the sentinel callers compare against using errors.As // (errors.Is also works because Unwrap is not defined). The runtime value // returned by Login carries the auto-expiry time in LockedUntil. ErrAccountLocked = ErrAccountLockedT{} )
Admin-only sentinel errors. Handlers translate these to HTTP codes.
var ( ErrOIDCEmailUnverified = errors.New("oidc_email_unverified") ErrOIDCRegDisabled = errors.New("oidc_registration_disabled") ErrOIDCNoInvite = errors.New("oidc_no_invite") )
OIDC sentinel errors translated to user-facing messages by the webui.
var ( // ErrSessionRevoked is returned when a session row has a non-nil revoked_at. ErrSessionRevoked = errors.New("session revoked") // ErrSessionExpired is returned when a session row's expires_at is in the past. // Middleware usually catches this via JWT expiration first; this covers the // edge case of a clock-skewed token or a session revoked-by-expiry. ErrSessionExpired = errors.New("session expired") // ErrSessionNotFound is returned when no row matches the jti. ErrSessionNotFound = errors.New("session not found") )
Session-related sentinel errors. Middleware translates these to 401/302.
var ErrInviteInvalid = errors.New("invite invalid or expired")
Functions ¶
func ClearSession ¶
func ClearSession(w http.ResponseWriter, r *http.Request)
ClearSession expires the session cookie.
func ClearTransientCookies ¶
func ClearTransientCookies(w http.ResponseWriter, r *http.Request, names ...string)
ClearTransientCookies expires the named cookies.
func ContextWithClaims ¶
ContextWithClaims returns a copy of ctx with claims attached. Used by the HTTP auth middleware after authentication succeeds.
func IsLocked ¶
IsLocked reports whether the account is locked at now and, if so, when the lockout expires.
func JTILogValue ¶
JTILogValue returns a session.id_hash value that identifies a session in logs without leaking the raw jti (which, combined with the signing secret, is the session bearer credential). The 16-char prefix of the SHA-256 hex digest is enough to correlate log lines while being computationally infeasible to reverse.
func ReadTransientCookies ¶
ReadTransientCookies returns a map of name→value for each requested cookie. Missing cookies map to empty string.
func SetSession ¶
SetSession writes the session cookie. Secure flag tracks the current request's transport so local http development still works.
func SetTransientCookie ¶
func SetTransientCookie(w http.ResponseWriter, r *http.Request, name, value string)
SetTransientCookie writes a short-lived httpOnly cookie scoped to /auth/oidc/. Used to carry state + nonce + PKCE verifier across the provider redirect round-trip.
Types ¶
type Claims ¶
type Claims struct {
UserID uint32 `json:"user_id"`
Email string `json:"email"`
DisplayName string `json:"display_name,omitempty"`
Role string `json:"role"`
JTI string `json:"jti,omitempty"`
jwt.RegisteredClaims
}
Claims embedded in JWT tokens.
JTI (JWT ID) is the per-session handle. It maps 1:1 to a row in the sessions table so the server can revoke the session without rotating the signing secret. Generated by issueToken; validated by middleware on every request against the sessions table.
func ClaimsFromContext ¶
ClaimsFromContext extracts auth claims from ctx. Returns nil when no middleware has run upstream (e.g. /health probes that bypass auth).
type ErrAccountLockedT ¶
ErrAccountLockedT is returned by Login when the account has been locked after too many failed attempts. LockedUntil reports when the lockout auto-expires; handlers surface this to users via Retry-After / banner.
func (ErrAccountLockedT) Error ¶
func (e ErrAccountLockedT) Error() string
type Limiter ¶
Limiter is the consumer-facing surface used by login and OIDC handlers to throttle credential attempts per IP.
func NewLimiter ¶
NewLimiter returns a Limiter allowing `limit` hits per key within `window`.
type LockoutPolicy ¶
LockoutPolicy is the tunable knobs read fresh from config on each login.
type LockoutState ¶
LockoutState mirrors the three lockout columns on the user row. Pointers distinguish "no failure yet" / "no active lockout" from zero values.
func OnFailedAttempt ¶
func OnFailedAttempt(s LockoutState, now time.Time, p LockoutPolicy) LockoutState
OnFailedAttempt advances state for one mismatched-password attempt. Resets the counter when the previous failure has fallen out of the sliding window. Stamps LockedUntil when the new counter meets the threshold.
type Manager ¶
type Manager interface {
// Service: core auth + JWT + API key
Login(
ctx context.Context,
email, password string,
meta SessionMeta,
) (string, error)
ValidateToken(token string) (*Claims, error)
ValidateAPIKey(ctx context.Context, key string) (*ent.User, error)
GetUserByID(ctx context.Context, id uint32) (*ent.User, error)
CreateAPIKey(
ctx context.Context,
userID uint32,
name string,
) (string, *ent.ApiKey, error)
RotateJWTSecret(ctx context.Context, callerID uint32) (string, error)
// Account: profile + own credentials/keys
UpdateProfile(
ctx context.Context,
userID uint32,
displayName string,
) (*ent.User, error)
ChangePassword(
ctx context.Context,
userID uint32,
current, newPassword, keepJTI string,
) error
ListAPIKeys(ctx context.Context, userID uint32) ([]*ent.ApiKey, error)
RevokeAPIKeyByID(ctx context.Context, userID, keyID uint32) error
// OIDC
LoginOIDC(
ctx context.Context,
provider, subject, email, displayName string,
emailVerified bool,
claims map[string]any,
meta SessionMeta,
) (*ent.User, string, error)
// Admin: user CRUD + admin actions
ListUsers(ctx context.Context, f UserFilter) ([]*ent.User, int, error)
CreateUserDirect(
ctx context.Context,
email, password, role, displayName string,
) (*ent.User, error)
GetUserDetail(
ctx context.Context,
id uint32,
) (*ent.User, []*ent.ApiKey, []*ent.Session, error)
UpdateUser(ctx context.Context, id uint32, p UserPatch) error
DeleteUser(ctx context.Context, id, requesterID uint32) error
AdminResetPassword(ctx context.Context, id uint32, newPassword string) error
AdminRevokeAPIKey(ctx context.Context, userID, keyID uint32) error
AdminRevokeSession(ctx context.Context, userID, sessionID uint32) error
Unlock(ctx context.Context, email string, mode UnlockMode) error
AdminUnlock(ctx context.Context, id uint32) error
// Invites
CreateInvite(
ctx context.Context,
createdByID uint32,
email, role string,
ttl time.Duration,
) (string, *ent.Invite, error)
LookupInviteForPrefill(ctx context.Context, rawToken string) (*ent.Invite, error)
ListInvites(ctx context.Context) ([]*ent.Invite, error)
RevokeInvite(ctx context.Context, id uint32) error
RegisterWithInvite(
ctx context.Context,
rawToken, email, password, displayName string,
meta SessionMeta,
) (*ent.User, string, error)
// Bootstrap + open registration
BootstrapSeedAdmin(ctx context.Context) error
RegisterOpen(
ctx context.Context,
email, password, displayName, defaultRole string,
meta SessionMeta,
) (*ent.User, string, error)
// Sessions
ValidateSession(ctx context.Context, jti string) error
TouchSessionAsync(jti string)
Shutdown(ctx context.Context) error
RevokeSession(ctx context.Context, jti string) error
RevokeSessionByID(ctx context.Context, userID, sessionID uint32) error
ListUserSessions(ctx context.Context, userID uint32) ([]*ent.Session, error)
PurgeExpiredSessions(ctx context.Context, before time.Time) (int, error)
}
Manager is the consumer-facing surface used by HTTP handlers, the auth middleware, the scheduler, and the composition root.
type OIDCManager ¶
type OIDCManager interface {
Init(ctx context.Context, redirectBase string)
Get(name string) (*OIDCProvider, bool)
}
OIDCManager is the consumer-facing surface for resolving configured OIDC providers at HTTP request time and warming the provider cache at startup.
func NewOIDCManager ¶
func NewOIDCManager() OIDCManager
type OIDCProvider ¶
type OIDCProvider struct {
Name string
Verifier *oidc.IDTokenVerifier
OAuth2 *oauth2.Config
}
OIDCProvider bundles a verifier + OAuth2 config for one named provider.
type SessionMeta ¶
type SessionMeta struct {
// IP is the originating client IP. Handlers should use the proxy-aware
// client IP (resolved by the chi ClientIPFrom* middleware), not RemoteAddr.
IP string
// UserAgent is the raw User-Agent header, truncated to 512 chars at the
// storage layer (schema MaxLen). Self-reported, so trustworthy only as
// a UX signal — never for authorization.
UserAgent string
}
SessionMeta carries per-request metadata recorded on session creation. Callers (HTTP handlers) populate this from the incoming request; service methods store the values on the Session row for the user-facing session list and for audit.
type SessionPurger ¶
type SessionPurger interface {
PurgeExpiredSessions(ctx context.Context, before time.Time) (int, error)
}
SessionPurger is the consumer-facing surface for the periodic expired-session sweep. jobs.PurgeSessions accepts it so it can be driven by a fake in tests without standing up the full Service.
type UnlockMode ¶
type UnlockMode string
UnlockMode tags the operator surface that triggered an unlock event. Surfaces in the auth.unlocked slog event so audit logs can distinguish admin-modal clicks from CLI invocations.
const ( UnlockModeAdmin UnlockMode = "admin" UnlockModeCLI UnlockMode = "cli" )