auth: split dev-mode auth constructor and wire up dev-login/logout UI
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 34s

Squeamish about New()'s empty-domain-string sentinel for "dev mode, skip
OIDC discovery" - split into New (always makes a real OIDC discovery
call, all params required) and NewDev (no ctx/domain/credentials at all,
since none are used). main.go now branches on cfg.DevAuthEnabled to pick
the right constructor instead of main.go/config.go coordinating on when
it's safe to pass empty strings.

Also finishes out the dev-auth flow this enables: config.Load reads a
DEV_AUTH_ENABLED-aware env file and only requires Auth0 vars when dev
auth is off; a PORT config var replaces the hardcoded :8082; and the nav
UI (layout/index templates, ui router) points login/logout links at
/api/auth/dev-login and a new /api/auth/dev-logout route when dev auth
is enabled, so the whole login/logout loop works locally without a real
Auth0 app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 00:24:04 -06:00
co-authored by Claude Sonnet 5
parent 6b6b631417
commit c41d5953c5
9 changed files with 275 additions and 64 deletions
+69 -17
View File
@@ -3,6 +3,7 @@
package config
import (
"flag"
"fmt"
"os"
"strconv"
@@ -25,13 +26,32 @@ type Config struct {
// login session for any user_id without going through Auth0. Must
// never be true outside local development.
DevAuthEnabled bool
Port int
}
var envFlagPtr = flag.String("env", "", "")
func init() {
flag.Parse()
}
// Load reads a .env file, if present, into the process environment, then
// reads the required configuration values from the environment.
func Load() (Config, error) {
if err := godotenv.Load(); err != nil && !os.IsNotExist(err) {
return Config{}, fmt.Errorf("failed to load .env file: %w", err)
dev, err := checkEnvFlag()
if err != nil {
return Config{}, err
}
if dev {
if err := godotenv.Load(".env.dev"); err != nil && !os.IsNotExist(err) {
return Config{}, fmt.Errorf("failed to load .env.dev file: %w", err)
}
} else {
if err := godotenv.Load(); err != nil && !os.IsNotExist(err) {
return Config{}, fmt.Errorf("failed to load .env file: %w", err)
}
}
var (
@@ -49,21 +69,8 @@ func Load() (Config, error) {
cfg.DatabaseURL = required("DATABASE_URL")
cfg.Auth0Domain = required("AUTH0_DOMAIN")
cfg.Auth0ClientID = required("AUTH0_CLIENT_ID")
cfg.Auth0ClientSecret = required("AUTH0_CLIENT_SECRET")
cfg.Auth0CallbackURL = required("AUTH0_CALLBACK_URL")
cfg.EtsyAPIKeystring = required("ETSY_API_KEYSTRING")
cfg.EtsyAPISharedSecret = required("ETSY_API_SHARED_SECRET")
if len(missing) > 0 {
return Config{}, fmt.Errorf(
"missing required environment variables: %v (see .env.example)",
missing,
)
}
// DEV_AUTH_ENABLED must be known before the Auth0 vars are read below,
// since it decides whether those are required at all.
if raw := os.Getenv("DEV_AUTH_ENABLED"); raw != "" {
devAuthEnabled, err := strconv.ParseBool(raw)
if err != nil {
@@ -72,5 +79,50 @@ func Load() (Config, error) {
cfg.DevAuthEnabled = devAuthEnabled
}
// With DEV_AUTH_ENABLED, real Auth0 login/logout never runs (dev-login
// mints sessions locally instead), so these are unused and optional.
if cfg.DevAuthEnabled {
cfg.Auth0Domain = os.Getenv("AUTH0_DOMAIN")
cfg.Auth0ClientID = os.Getenv("AUTH0_CLIENT_ID")
cfg.Auth0ClientSecret = os.Getenv("AUTH0_CLIENT_SECRET")
cfg.Auth0CallbackURL = os.Getenv("AUTH0_CALLBACK_URL")
} else {
cfg.Auth0Domain = required("AUTH0_DOMAIN")
cfg.Auth0ClientID = required("AUTH0_CLIENT_ID")
cfg.Auth0ClientSecret = required("AUTH0_CLIENT_SECRET")
cfg.Auth0CallbackURL = required("AUTH0_CALLBACK_URL")
}
cfg.EtsyAPIKeystring = required("ETSY_API_KEYSTRING")
cfg.EtsyAPISharedSecret = required("ETSY_API_SHARED_SECRET")
portStr := required("PORT")
if len(missing) > 0 {
return Config{}, fmt.Errorf(
"missing required environment variables: %v (see .env.example)",
missing,
)
}
if port, err := strconv.Atoi(portStr); err != nil {
return Config{}, fmt.Errorf("invalid port number: %w", err)
} else {
cfg.Port = port
}
return cfg, nil
}
func checkEnvFlag() (dev bool, err error) {
switch env := *envFlagPtr; env {
case "dev":
return true, nil
case "prd":
return false, nil
case "":
return false, fmt.Errorf("no env flag specified: must be one of dev, prd")
default:
return false, fmt.Errorf("invalid env flag specified: must be one of dev, prd: %q", env)
}
}