auth

package
v0.9.705 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 20 Imported by: 0

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

View Source
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

func CheckPassword(hash, plain string) bool

CheckPassword is the constant-time bcrypt compare.

func HashPassword

func HashPassword(plain string) (string, error)

HashPassword wraps bcrypt with the default cost.

func IsAPIToken added in v0.8.444

func IsAPIToken(v string) bool

IsAPIToken — Bearer değerinin servis token'ı olup olmadığı.

func IsValidRole

func IsValidRole(s string) bool

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

func PKCEChallenge(verifier string) string

PKCEChallenge returns base64url(SHA256(verifier)) — the S256 method from RFC 7636.

func RandomURLToken

func RandomURLToken(nBytes int) string

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").

func SkipPath

func SkipPath(method, path string) bool

SkipPath reports whether a path should bypass authentication. OTLP ingest endpoints stay open so SDKs without auth headers continue to work; health is open for liveness probes; auth/login + OIDC routes are the entry points so they cannot themselves require auth.

Types

type AuthzLookup added in v0.9.352

type AuthzLookup interface {
	LiveAuthz(ctx context.Context, userID string) (role string, ok bool, err error)
}

AuthzLookup resolves the CURRENT authorization state of a user id.

ok=false means "this user must not be let in" — deleted, or disabled. The distinction doesn't matter to the caller and deliberately isn't exposed: both end in 401, and telling them apart would leak whether an account exists.

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

func FromContext(ctx context.Context) *Claims

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

type CustomRole struct {
	Name  string   `json:"name"`
	Pages []string `json:"pages"`
}

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

type LookupUser struct {
	ID    string
	Email string
	Role  string
}

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

func NewService(secret string, ttl time.Duration) *Service

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

func (s *Service) CustomRolePages(name string) []string

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

func (s *Service) DeleteCustomRole(ctx context.Context, store customRoleStore, name string) error

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) EnableAPITokens added in v0.8.444

func (s *Service) EnableAPITokens(ctx context.Context, src TokenSource)

EnableAPITokens — boot'ta bağlar ve tazeleme döngüsünü başlatır.

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) InvalidateAuthz added in v0.9.352

func (s *Service) InvalidateAuthz(userID string)

InvalidateAuthz forgets a user's cached role. Every user mutation calls it.

func (*Service) Issue

func (s *Service) Issue(userID, email, role string) (string, time.Time, error)

Issue signs a JWT for the given identity.

func (*Service) LoadPersistedCustomRoles added in v0.5.251

func (s *Service) LoadPersistedCustomRoles(ctx context.Context, store customRoleStore) error

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

func (s *Service) Middleware(next http.Handler) http.Handler

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) Parse

func (s *Service) Parse(token string) (*Claims, error)

Parse validates a JWT and returns its claims.

func (*Service) RefreshAPITokens added in v0.8.444

func (s *Service) RefreshAPITokens(ctx context.Context)

RefreshAPITokens — cache'i kaynaktan yeniler; create/revoke sonrası API handler'ı da çağırır (anında etki).

func (*Service) SetAuthzLookup added in v0.9.352

func (s *Service) SetAuthzLookup(l AuthzLookup)

SetAuthzLookup wires the live resolver. Called once from main(); when it is never called the middleware keeps the pre-v0.9.352 behaviour (trust the token), so tests and dev builds are unaffected.

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) TTL

func (s *Service) TTL() time.Duration

TTL returns the configured token lifetime — needed for cookie MaxAge.

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 TokenInfo added in v0.8.444

type TokenInfo struct {
	ID   string
	Name string
	Role string
}

TokenInfo — cache'teki tek kayıt (chstore.APIToken'ın auth dilimi; import döngüsü olmasın diye kendi tipi).

type TokenSource added in v0.8.444

type TokenSource interface {
	ActiveHashes(ctx context.Context) (map[string]TokenInfo, error)
}

TokenSource — chstore'un ihtiyaç duyulan dilimi.

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).

Jump to

Keyboard shortcuts

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