Add optional Authentik (OIDC) sign-in with server-side preference sync
Signed-in users get their preferences (hidden feeds, weather location, weather toggle) stored server-side keyed by their OIDC subject and synced across devices. Anonymous visitors keep using browser localStorage, so the site stays public. First sign-in migrates existing localStorage prefs up. - config: [web.auth] section (issuer, client_id/secret, redirect, session_secret) - storage: user_preferences table + Get/PutUserPrefs - web/auth: OIDC code flow, HMAC-signed session cookie, CSRF state + nonce - web/prefs_api: GET/PUT /api/preferences (auth-gated, 64KB cap) - frontend: prefs.js sync layer seeds localStorage from server, pushes on write - header: sign-in / account control OIDC discovery is non-fatal at boot: if Authentik is down, Pete serves anonymously rather than refusing to start.
This commit is contained in:
@@ -55,6 +55,7 @@ type Server struct {
|
||||
sources []SourceInfo
|
||||
srv *http.Server
|
||||
tpls map[string]*template.Template // keyed by page name (e.g. "index", "channel")
|
||||
auth *Authenticator // nil when sign-in is disabled or unavailable
|
||||
}
|
||||
|
||||
// New builds the server. Templates are parsed once at startup. Each page
|
||||
@@ -82,6 +83,22 @@ func New(cfg config.WebConfig, sources []config.SourceConfig) (*Server, error) {
|
||||
infos = append(infos, SourceInfo{Name: sc.Name, Channel: sc.DirectRoute})
|
||||
}
|
||||
s := &Server{cfg: cfg, sources: infos, tpls: tpls}
|
||||
|
||||
// Optional OIDC sign-in (Authentik). Discovery is a network call; if the
|
||||
// provider is unreachable at boot we log and serve anonymously rather than
|
||||
// refusing to start the whole site.
|
||||
if cfg.Auth.Enabled {
|
||||
dctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
auth, err := newAuthenticator(dctx, cfg.Auth)
|
||||
cancel()
|
||||
if err != nil {
|
||||
slog.Error("web: OIDC auth init failed; sign-in disabled", "err", err)
|
||||
} else {
|
||||
s.auth = auth
|
||||
slog.Info("web: OIDC sign-in enabled", "issuer", cfg.Auth.Issuer)
|
||||
}
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
staticSub, err := fs.Sub(staticFS, "static")
|
||||
@@ -105,6 +122,14 @@ func New(cfg config.WebConfig, sources []config.SourceConfig) (*Server, error) {
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
})
|
||||
|
||||
if s.auth != nil {
|
||||
mux.HandleFunc("GET /auth/login", s.auth.handleLogin)
|
||||
mux.HandleFunc("GET /auth/callback", s.auth.handleCallback)
|
||||
mux.HandleFunc("GET /auth/logout", s.auth.handleLogout)
|
||||
mux.HandleFunc("GET /api/preferences", s.handlePrefs)
|
||||
mux.HandleFunc("PUT /api/preferences", s.handlePrefs)
|
||||
}
|
||||
|
||||
s.srv = &http.Server{
|
||||
Addr: cfg.ListenAddr,
|
||||
Handler: mux,
|
||||
|
||||
Reference in New Issue
Block a user