Documentation
¶
Overview ¶
Package auth implements local username/password auth + JWT issuing for the Coremetry HTTP API. It deliberately avoids external session storage — JWTs are stateless and self-contained.
Index ¶
- Constants
- func CheckPassword(hash, plain string) bool
- func HashPassword(plain string) (string, error)
- func IsValidRole(s string) bool
- func PKCEChallenge(verifier string) string
- func RandomURLToken(nBytes int) string
- func RequireAnyRole(roles []string, h http.HandlerFunc) http.HandlerFunc
- func RequireRole(role string, h http.HandlerFunc) http.HandlerFunc
- func SkipPath(method, path string) bool
- type Claims
- type CustomRole
- type LookupUser
- type OIDCClaims
- type OIDCService
- func (o *OIDCService) AllowEmail(email string) bool
- func (o *OIDCService) AuthURL(state, nonce, codeChallenge string) string
- func (o *OIDCService) DefaultRole() string
- func (o *OIDCService) DisplayName() string
- func (o *OIDCService) Enabled() bool
- func (o *OIDCService) Exchange(ctx context.Context, code, codeVerifier, expectedNonce string) (*OIDCClaims, error)
- type Service
- func (s *Service) CustomRolePages(name string) []string
- func (s *Service) CustomRoles() []CustomRole
- func (s *Service) DeleteCustomRole(ctx context.Context, store customRoleStore, name string) error
- func (s *Service) EnableTrustedHeader(opts TrustedHeaderOptions, trustedProxies []string, store UserLookup) error
- func (s *Service) Issue(userID, email, role string) (string, time.Time, error)
- func (s *Service) LoadPersistedCustomRoles(ctx context.Context, store customRoleStore) error
- func (s *Service) Middleware(next http.Handler) http.Handler
- func (s *Service) Parse(token string) (*Claims, error)
- func (s *Service) StartCustomRoleRefresh(ctx context.Context, store customRoleStore, interval time.Duration)
- func (s *Service) TTL() time.Duration
- func (s *Service) UpsertCustomRole(ctx context.Context, store customRoleStore, role CustomRole) error
- type TrustedHeaderOptions
- type UserLookup
Constants ¶
const ( // RoleAdmin can do everything (user mgmt, settings, all CRUD). // RoleEditor can create/edit content (dashboards, monitors, // alert rules, incidents) but not user mgmt or system // settings. // RoleViewer is read-only. RoleAdmin = "admin" RoleEditor = "editor" RoleViewer = "viewer" // CookieName is set on login and cleared on logout. It must be the // same on the frontend (login fetch uses credentials: 'include'). CookieName = "coremetry_session" )
Variables ¶
This section is empty.
Functions ¶
func CheckPassword ¶
CheckPassword is the constant-time bcrypt compare.
func HashPassword ¶
HashPassword wraps bcrypt with the default cost.
func IsValidRole ¶
IsValidRole reports whether s is one of the canonical role strings. Used by handlers that accept a role from outside (admin upserting users, LDAP group→role mapping).
func PKCEChallenge ¶
PKCEChallenge returns base64url(SHA256(verifier)) — the S256 method from RFC 7636.
func RandomURLToken ¶
RandomURLToken returns a base64url-encoded random string of nBytes entropy — used for state, nonce, and PKCE code_verifier.
func RequireAnyRole ¶
func RequireAnyRole(roles []string, h http.HandlerFunc) http.HandlerFunc
RequireAnyRole accepts any of the listed roles. Used for routes that should be open to admin and editor (dashboard/monitor CRUD etc.) — admin-only routes still use RequireRole for clarity.
func RequireRole ¶
func RequireRole(role string, h http.HandlerFunc) http.HandlerFunc
RequireRole gates a handler on a specific role (typically "admin").
Types ¶
type Claims ¶
type Claims struct {
UserID string `json:"uid"`
Email string `json:"email"`
Role string `json:"role"`
jwt.RegisteredClaims
}
Claims is the payload embedded in every JWT.
func FromContext ¶
FromContext returns the authenticated claims set by Middleware. Handlers behind the middleware can rely on the value being non-nil.
type CustomRole ¶ added in v0.5.251
CustomRole is an admin-defined subset of viewer's page access. Each role names a set of sidebar paths the user is allowed to see; the frontend filters the sidebar + redirects direct-URL access to the first visible page. Custom roles only apply when the user's base role is viewer — admin/editor get no further restriction.
type LookupUser ¶ added in v0.4.90
LookupUser is the minimal shape exchanged through UserLookup. chstore.User has more fields (password hash, created_at, etc.) that the trusted-header path doesn't touch, so the interface only requires this slice.
type OIDCClaims ¶
type OIDCClaims struct {
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Subject string `json:"-"`
Nonce string `json:"-"`
}
OIDCClaims is the subset of id_token claims we care about.
type OIDCService ¶
type OIDCService struct {
// contains filtered or unexported fields
}
OIDCService is the optional SSO layer. nil when disabled — every call site checks Enabled() before invoking other methods.
func NewOIDCService ¶
func NewOIDCService(ctx context.Context, cfg config.OIDCConfig) (*OIDCService, error)
NewOIDCService runs OIDC discovery against the issuer. Returns (nil, nil) when disabled. Returns an error when enabled but the issuer is unreachable / misconfigured — the caller decides whether to abort startup or just log and continue with local-only auth.
func (*OIDCService) AllowEmail ¶
func (o *OIDCService) AllowEmail(email string) bool
AllowEmail enforces the optional domain whitelist. Empty list = allow all.
func (*OIDCService) AuthURL ¶
func (o *OIDCService) AuthURL(state, nonce, codeChallenge string) string
AuthURL builds the IdP redirect URL with PKCE + nonce + state.
func (*OIDCService) DefaultRole ¶
func (o *OIDCService) DefaultRole() string
func (*OIDCService) DisplayName ¶
func (o *OIDCService) DisplayName() string
func (*OIDCService) Enabled ¶
func (o *OIDCService) Enabled() bool
Enabled is true when the service is configured. Safe on a nil receiver.
func (*OIDCService) Exchange ¶
func (o *OIDCService) Exchange(ctx context.Context, code, codeVerifier, expectedNonce string) (*OIDCClaims, error)
Exchange completes the auth code flow: token exchange, id_token verify, nonce check, claim extraction, domain whitelist.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service issues, validates and exposes JWTs.
func NewService ¶
NewService is the constructor. If secret is empty a random one is generated — fine for first-run dev, but logged so operators can pin it.
func (*Service) CustomRolePages ¶ added in v0.5.251
CustomRolePages returns the page list for a named role, or nil if the role isn't found. nil is the signal for "this user has no page restriction" — callers MUST distinguish nil (unrestricted) from empty slice (restricted to zero pages, effectively no access).
func (*Service) CustomRoles ¶ added in v0.5.251
func (s *Service) CustomRoles() []CustomRole
CustomRoles returns a snapshot copy of the current role catalog ordered by name. The caller can freely mutate the returned slice.
func (*Service) DeleteCustomRole ¶ added in v0.5.251
DeleteCustomRole removes a role by name. Persists the updated catalog. Caller is responsible for clearing the custom_role field on any user assigned to it (the API handler does this — see deleteCustomRole in internal/api/api.go).
func (*Service) EnableTrustedHeader ¶ added in v0.4.90
func (s *Service) EnableTrustedHeader(opts TrustedHeaderOptions, trustedProxies []string, store UserLookup) error
EnableTrustedHeader turns on oauth2-proxy / IAP header trust. trustedProxies is a list of CIDR strings (e.g. "10.0.0.0/8", "172.16.0.0/12") — the headers are honoured only when the request originates from one of these blocks. An empty list is a config error: it'd let any caller spoof the email header. Returns an error in that case so main.go can fail fast at boot instead of silently leaving the door open.
func (*Service) LoadPersistedCustomRoles ¶ added in v0.5.251
LoadPersistedCustomRoles hydrates the in-memory custom-role catalog from system_settings. Missing blob = empty catalog. Called once at boot from main(); safe to call again on demand.
func (*Service) Middleware ¶
Middleware enforces a valid JWT (cookie or Bearer) for every protected endpoint. Failures return 401 with a JSON error so the SPA can redirect.
Trusted-header fallback: when EnableTrustedHeader was called at boot AND the JWT path fails AND the request originates from a configured trusted-proxy CIDR, the email header is honoured. The middleware looks up (or auto-provisions, if enabled) a user and mints a JWT cookie inline so the rest of the SPA's stateful flow keeps working.
func (*Service) StartCustomRoleRefresh ¶ added in v0.5.318
func (s *Service) StartCustomRoleRefresh(ctx context.Context, store customRoleStore, interval time.Duration)
StartCustomRoleRefresh — v0.5.318. Runs a background goroutine that re-reads the custom-role catalog from the shared chstore every `interval` (default 30s when ≤0). In a multi-pod cluster the previous load-once-at-boot pattern meant a role created on pod A wasn't visible to a session served by pod B until B's process restarted. Polling closes that gap to a bounded staleness window without requiring pub/sub infrastructure.
Returns when ctx is cancelled. Errors are logged but never fatal — a transient CH blip leaves the previous catalog in place rather than clearing it.
func (*Service) UpsertCustomRole ¶ added in v0.5.251
func (s *Service) UpsertCustomRole(ctx context.Context, store customRoleStore, role CustomRole) error
UpsertCustomRole writes a single role (create or replace by name). Persists the updated catalog atomically — partial writes don't occur because the whole catalog is one system_settings blob.
type TrustedHeaderOptions ¶ added in v0.4.90
type TrustedHeaderOptions struct {
Enabled bool
EmailHeader string
UserHeader string
GroupsHeader string
AutoProvision bool
DefaultRole string
}
TrustedHeaderOptions is the auth.Service-side mirror of the config.TrustedHeaderConfig. Kept separate so the auth package stays free of the internal/config import cycle.
type UserLookup ¶ added in v0.4.90
type UserLookup interface {
GetUserByEmail(ctx context.Context, email string) (*LookupUser, error)
UpsertUser(ctx context.Context, u LookupUser) error
}
UserLookup is the small store interface the trusted-header path needs — find an existing user by email, or upsert a new row when AutoProvision is on. Implemented by *chstore.Store (the API layer wires it in main.go).