feat(serve): wire Dex OIDC into serve when configured, else StubAuth
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:
+23
-4
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user