feat(serve): wire Dex OIDC into serve when configured, else StubAuth
CI / Lint / Test / Vet (push) Failing after 8s
CI / Build & Import (push) Has been skipped
CI / Mirror to GitHub (push) Has been skipped

serve now uses oidc.DexAuth (single-user allowlist authz, ADR-011) when
TAPIR_OIDC_ISSUER is set, falling back to allow-all StubAuth for local dev.
Adds the Dex config fields (TAPIR_OIDC_ISSUER/DEX_CLIENT_ID/SECRET/
OIDC_REDIRECT_URL/SESSION_SECRET/ALLOWED_SUBJECT) + Config.DexConfigured().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 00:05:25 +02:00
co-authored by Claude Opus 4.8
parent 7d525eaaa3
commit 3364cf7ac3
2 changed files with 43 additions and 4 deletions
+23 -4
View File
@@ -32,6 +32,7 @@ import (
"gitea.d-ma.be/mathias/tapir/internal/runner"
"gitea.d-ma.be/mathias/tapir/internal/usecase"
"gitea.d-ma.be/mathias/tapir/internal/web"
"gitea.d-ma.be/mathias/tapir/internal/web/oidc"
)
func main() {
@@ -162,10 +163,28 @@ func cmdServe(ctx context.Context, log *slog.Logger) error {
}
defer st.Close()
// Auth seam: StubAuth allows every request as the configured user. The
// Conductor replaces this with oidc.DexAuth (lane B) at merge — nothing else
// in this function or the handlers changes (handlers depend on web.Auth only).
var authn web.Auth = web.StubAuth{U: web.User{Subject: cfg.UserID}}
// Auth seam (handlers depend on web.Auth only). With Dex configured
// (TAPIR_OIDC_ISSUER set) serve uses real OIDC login with single-user
// allowlist authz (ADR-011); otherwise it falls back to the allow-all
// StubAuth for local dev — never expose StubAuth publicly.
var authn web.Auth
if cfg.DexConfigured() {
authn, err = oidc.New(ctx, oidc.Config{
Issuer: cfg.OIDCIssuer,
ClientID: cfg.DexClientID,
ClientSecret: cfg.DexClientSecret,
RedirectURL: cfg.OIDCRedirectURL,
SessionSecret: cfg.SessionSecret,
AllowedSubject: cfg.AllowedSubject,
})
if err != nil {
return fmt.Errorf("dex oidc: %w", err)
}
log.Info("web auth: dex oidc", "issuer", cfg.OIDCIssuer)
} else {
authn = web.StubAuth{U: web.User{Subject: cfg.UserID}}
log.Warn("web auth: STUB allow-all (no TAPIR_OIDC_ISSUER) — local dev only, do not expose")
}
app := &web.App{Store: st, Auth: authn, UserID: cfg.UserID, Log: log}
srv := &http.Server{
+20
View File
@@ -56,8 +56,22 @@ type Config struct {
// HTTPAddr is the listen address for `tapir serve` (the Stage-0 web UI).
HTTPAddr string
// Dex OIDC (web login, ADR-011). When OIDCIssuer is empty, `serve` falls back
// to the allow-all StubAuth (local dev). When set, serve uses Dex with
// single-user allowlist authz.
OIDCIssuer string
DexClientID string
DexClientSecret string
OIDCRedirectURL string
SessionSecret string
AllowedSubject string
}
// DexConfigured reports whether Dex OIDC login is wired (issuer present). When
// false, `serve` uses StubAuth (dev only).
func (c Config) DexConfigured() bool { return strings.TrimSpace(c.OIDCIssuer) != "" }
// Defaults (see docs/homelab-integration.md). All overridable via env.
const (
defaultGatewayURL = "http://koala:30401/v1"
@@ -85,6 +99,12 @@ func Load() (Config, error) {
SecretsFile: envOr("TAPIR_SECRETS_FILE", defaultSecretsFile()),
OAuthRedirectAddr: envOr("TAPIR_OAUTH_REDIRECT_ADDR", defaultOAuthRedirectAddr),
HTTPAddr: envOr("TAPIR_HTTP_ADDR", defaultHTTPAddr),
OIDCIssuer: os.Getenv("TAPIR_OIDC_ISSUER"),
DexClientID: os.Getenv("TAPIR_DEX_CLIENT_ID"),
DexClientSecret: os.Getenv("TAPIR_DEX_CLIENT_SECRET"),
OIDCRedirectURL: os.Getenv("TAPIR_OIDC_REDIRECT_URL"),
SessionSecret: os.Getenv("TAPIR_SESSION_SECRET"),
AllowedSubject: os.Getenv("TAPIR_ALLOWED_SUBJECT"),
}
timeout, err := durationOr("TAPIR_SUMMARIZER_TIMEOUT", defaultSummarizerTimeout)