diff --git a/README.md b/README.md index 0a847aa..8ca32fa 100644 --- a/README.md +++ b/README.md @@ -316,35 +316,36 @@ If you are not running a reverse proxy change `host` in the `config.toml` to `0. The following environment variables can be used: -| Variable | Description | Default | -|-------------------------------------|--------------------------------------|------------------------------------------| -| `AUTOBRR__HOST` | Listen address | `127.0.0.1` | -| `AUTOBRR__PORT` | Listen port | `7474` | -| `AUTOBRR__BASE_URL` | Base URL for reverse proxy | `/` | -| `AUTOBRR__LOG_LEVEL` | Log level (DEBUG, INFO, WARN, ERROR) | `INFO` | -| `AUTOBRR__LOG_PATH` | Log file location | `/config/logs` | -| `AUTOBRR__LOG_MAX_SIZE` | Max size in MB before rotation | `10` | -| `AUTOBRR__LOG_MAX_BACKUPS` | Number of rotated logs to keep | `5` | -| `AUTOBRR__SESSION_SECRET` | Random string for session encryption | - | -| `AUTOBRR__CUSTOM_DEFINITIONS` | Path to custom indexer definitions | - | -| `AUTOBRR__CHECK_FOR_UPDATES` | Enable update checks | `true` | -| `AUTOBRR__DATABASE_TYPE` | Database type (sqlite/postgres) | `sqlite` | -| `AUTOBRR__POSTGRES_HOST` | PostgreSQL host | - | -| `AUTOBRR__POSTGRES_PORT` | PostgreSQL port | `5432` | -| `AUTOBRR__POSTGRES_DATABASE` | PostgreSQL database name | - | -| `AUTOBRR__POSTGRES_USER` | PostgreSQL username | - | -| `AUTOBRR__POSTGRES_PASS` | PostgreSQL password | - | -| `AUTOBRR__POSTGRES_SSLMODE` | PostgreSQL SSL mode | `disable` | -| `AUTOBRR__POSTGRES_EXTRA_PARAMS` | Additional PostgreSQL parameters | - | -| `AUTOBRR__OIDC_ENABLED` | Enable OpenID Connect authentication | `false` | -| `AUTOBRR__OIDC_ISSUER` | OIDC issuer URL | - | -| `AUTOBRR__OIDC_CLIENT_ID` | OIDC client ID | - | -| `AUTOBRR__OIDC_CLIENT_SECRET` | OIDC client secret | - | -| `AUTOBRR__OIDC_REDIRECT_URL` | OIDC callback URL | `https://baseurl/api/auth/oidc/callback` | -| `AUTOBRR__METRICS_ENABLED` | Enable Metrics server | `false` | -| `AUTOBRR__METRICS_HOST` | Metrics listen address | `127.0.0.1` | -| `AUTOBRR__METRICS_PORT` | Metrics listen port | `9074` | -| `AUTOBRR__METRICS_BASIC_AUTH_USERS` | Metrics basic auth users | - | +| Variable | Description | Default | +| -------------------------------------- | -------------------------------------------------------- | ---------------------------------------- | +| `AUTOBRR__HOST` | Listen address | `127.0.0.1` | +| `AUTOBRR__PORT` | Listen port | `7474` | +| `AUTOBRR__BASE_URL` | Base URL for reverse proxy | `/` | +| `AUTOBRR__LOG_LEVEL` | Log level (DEBUG, INFO, WARN, ERROR) | `INFO` | +| `AUTOBRR__LOG_PATH` | Log file location | `/config/logs` | +| `AUTOBRR__LOG_MAX_SIZE` | Max size in MB before rotation | `10` | +| `AUTOBRR__LOG_MAX_BACKUPS` | Number of rotated logs to keep | `5` | +| `AUTOBRR__SESSION_SECRET` | Random string for session encryption | - | +| `AUTOBRR__CUSTOM_DEFINITIONS` | Path to custom indexer definitions | - | +| `AUTOBRR__CHECK_FOR_UPDATES` | Enable update checks | `true` | +| `AUTOBRR__DATABASE_TYPE` | Database type (sqlite/postgres) | `sqlite` | +| `AUTOBRR__POSTGRES_HOST` | PostgreSQL host | - | +| `AUTOBRR__POSTGRES_PORT` | PostgreSQL port | `5432` | +| `AUTOBRR__POSTGRES_DATABASE` | PostgreSQL database name | - | +| `AUTOBRR__POSTGRES_USER` | PostgreSQL username | - | +| `AUTOBRR__POSTGRES_PASS` | PostgreSQL password | - | +| `AUTOBRR__POSTGRES_SSLMODE` | PostgreSQL SSL mode | `disable` | +| `AUTOBRR__POSTGRES_EXTRA_PARAMS` | Additional PostgreSQL parameters | - | +| `AUTOBRR__OIDC_ENABLED` | Enable OpenID Connect authentication | `false` | +| `AUTOBRR__OIDC_ISSUER` | OIDC issuer URL | - | +| `AUTOBRR__OIDC_CLIENT_ID` | OIDC client ID | - | +| `AUTOBRR__OIDC_CLIENT_SECRET` | OIDC client secret | - | +| `AUTOBRR__OIDC_REDIRECT_URL` | OIDC callback URL | `https://baseurl/api/auth/oidc/callback` | +| `AUTOBRR__OIDC_DISABLE_BUILT_IN_LOGIN` | Disable login form (only works when using external auth) | `false` | +| `AUTOBRR__METRICS_ENABLED` | Enable Metrics server | `false` | +| `AUTOBRR__METRICS_HOST` | Metrics listen address | `127.0.0.1` | +| `AUTOBRR__METRICS_PORT` | Metrics listen port | `9074` | +| `AUTOBRR__METRICS_BASIC_AUTH_USERS` | Metrics basic auth users | - | ## Community diff --git a/config.toml b/config.toml index 5536c59..83691f4 100644 --- a/config.toml +++ b/config.toml @@ -72,19 +72,22 @@ sessionSecret = "secret-session-key" # OpenID Connect Configuration # # Enable OIDC authentication -#oidc_enabled = false - +#oidcEnabled = false +# # OIDC Issuer URL (e.g. https://auth.example.com) -#oidc_issuer = "" - +#oidcIssuer = "" +# # OIDC Client ID -#oidc_client_id = "" - +#oidcClientId = "" +# # OIDC Client Secret -#oidc_client_secret = "" - +#oidcClientSecret = "" +# # OIDC Redirect URL (e.g. http://localhost:7474/api/auth/oidc/callback) -#oidc_redirect_url = "" +#oidcRedirectUrl = "" +# +# Disable Built In Login Form (only works when using external auth) +#oidcDisableBuiltInLogin = false # Metrics # @@ -93,11 +96,11 @@ sessionSecret = "secret-session-key" # Metrics server host # -# metricsHost = "127.0.0.1" +#metricsHost = "127.0.0.1" # Metrics server port # -# metricsPort = "9074" +#metricsPort = "9074" # Metrics basic auth # diff --git a/internal/auth/oidc.go b/internal/auth/oidc.go index a2646b3..e71babf 100644 --- a/internal/auth/oidc.go +++ b/internal/auth/oidc.go @@ -21,12 +21,13 @@ import ( ) type OIDCConfig struct { - Enabled bool - Issuer string - ClientID string - ClientSecret string - RedirectURL string - Scopes []string + Enabled bool + Issuer string + ClientID string + ClientSecret string + RedirectURL string + DisableBuiltInLogin bool + Scopes []string } type OIDCHandler struct { @@ -124,12 +125,13 @@ func NewOIDCHandler(cfg *domain.Config, log zerolog.Logger) (*OIDCHandler, error handler := &OIDCHandler{ log: log, config: &OIDCConfig{ - Enabled: cfg.OIDCEnabled, - Issuer: cfg.OIDCIssuer, - ClientID: cfg.OIDCClientID, - ClientSecret: cfg.OIDCClientSecret, - RedirectURL: cfg.OIDCRedirectURL, - Scopes: scopes, + Enabled: cfg.OIDCEnabled, + Issuer: cfg.OIDCIssuer, + ClientID: cfg.OIDCClientID, + ClientSecret: cfg.OIDCClientSecret, + RedirectURL: cfg.OIDCRedirectURL, + DisableBuiltInLogin: cfg.OIDCDisableBuiltInLogin, + Scopes: scopes, }, provider: provider, verifier: provider.Verifier(oidcConfig), @@ -282,27 +284,30 @@ func (h *OIDCHandler) GetAuthorizationURL() string { } type GetConfigResponse struct { - Enabled bool `json:"enabled"` - AuthorizationURL string `json:"authorizationUrl"` - State string `json:"state"` + Enabled bool `json:"enabled"` + AuthorizationURL string `json:"authorizationUrl"` + State string `json:"state"` + DisableBuiltInLogin bool `json:"disableBuiltInLogin"` } func (h *OIDCHandler) GetConfigResponse() GetConfigResponse { if h == nil { return GetConfigResponse{ - Enabled: false, + Enabled: false, + DisableBuiltInLogin: false, } } state := generateRandomState() authURL := h.oauthConfig.AuthCodeURL(state) - h.log.Debug().Bool("enabled", h.config.Enabled).Str("authorization_url", authURL).Str("state", state).Msg("returning OIDC config response") + h.log.Debug().Bool("enabled", h.config.Enabled).Str("authorization_url", authURL).Str("state", state).Bool("disable_built_in_login", h.config.DisableBuiltInLogin).Msg("returning OIDC config response") return GetConfigResponse{ - Enabled: h.config.Enabled, - AuthorizationURL: authURL, - State: state, + Enabled: h.config.Enabled, + AuthorizationURL: authURL, + State: state, + DisableBuiltInLogin: h.config.DisableBuiltInLogin, } } diff --git a/internal/config/config.go b/internal/config/config.go index 035dd4a..c262f6e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -112,19 +112,22 @@ sessionSecret = "{{ .sessionSecret }}" # OpenID Connect Configuration # # Enable OIDC authentication -#oidc_enabled = false +#oidcEnabled = false # # OIDC Issuer URL (e.g. https://auth.example.com) -#oidc_issuer = "" +#oidcIssuer = "" # # OIDC Client ID -#oidc_client_id = "" +#oidcClientId = "" # # OIDC Client Secret -#oidc_client_secret = "" +#oidcClientSecret = "" # # OIDC Redirect URL (e.g. http://localhost:7474/api/auth/oidc/callback) -#oidc_redirect_url = "" +#oidcRedirectUrl = "" +# +# Disable Built In Login Form (only works when using external auth) +#oidcDisableBuiltInLogin = false # Metrics # @@ -432,6 +435,10 @@ func (c *AppConfig) loadFromEnv() { c.Config.OIDCRedirectURL = v } + if v := os.Getenv(prefix + "OIDC_DISABLE_BUILT_IN_LOGIN"); v != "" { + c.Config.OIDCDisableBuiltInLogin = strings.EqualFold(strings.ToLower(v), "true") + } + if v := os.Getenv(prefix + "METRICS_ENABLED"); v != "" { c.Config.MetricsEnabled = strings.EqualFold(strings.ToLower(v), "true") } diff --git a/internal/domain/config.go b/internal/domain/config.go index 69f1076..9e40d2d 100644 --- a/internal/domain/config.go +++ b/internal/domain/config.go @@ -4,41 +4,42 @@ package domain type Config struct { - Version string - ConfigPath string - Host string `toml:"host"` - Port int `toml:"port"` - LogLevel string `toml:"logLevel"` - LogPath string `toml:"logPath"` - LogMaxSize int `toml:"logMaxSize"` - LogMaxBackups int `toml:"logMaxBackups"` - BaseURL string `toml:"baseUrl"` - BaseURLModeLegacy bool `toml:"baseUrlModeLegacy"` - SessionSecret string `toml:"sessionSecret"` - CustomDefinitions string `toml:"customDefinitions"` - CheckForUpdates bool `toml:"checkForUpdates"` - DatabaseType string `toml:"databaseType"` - DatabaseMaxBackups int `toml:"databaseMaxBackups"` - PostgresHost string `toml:"postgresHost"` - PostgresPort int `toml:"postgresPort"` - PostgresDatabase string `toml:"postgresDatabase"` - PostgresUser string `toml:"postgresUser"` - PostgresPass string `toml:"postgresPass"` - PostgresSSLMode string `toml:"postgresSSLMode"` - PostgresExtraParams string `toml:"postgresExtraParams"` - ProfilingEnabled bool `toml:"profilingEnabled"` - ProfilingHost string `toml:"profilingHost"` - ProfilingPort int `toml:"profilingPort"` - OIDCEnabled bool `mapstructure:"oidc_enabled"` - OIDCIssuer string `mapstructure:"oidc_issuer"` - OIDCClientID string `mapstructure:"oidc_client_id"` - OIDCClientSecret string `mapstructure:"oidc_client_secret"` - OIDCRedirectURL string `mapstructure:"oidc_redirect_url"` - OIDCScopes string `mapstructure:"oidc_scopes"` - MetricsEnabled bool `toml:"metricsEnabled"` - MetricsHost string `toml:"metricsHost"` - MetricsPort int `toml:"metricsPort"` - MetricsBasicAuthUsers string `toml:"metricsBasicAuthUsers"` + Version string + ConfigPath string + Host string `toml:"host"` + Port int `toml:"port"` + LogLevel string `toml:"logLevel"` + LogPath string `toml:"logPath"` + LogMaxSize int `toml:"logMaxSize"` + LogMaxBackups int `toml:"logMaxBackups"` + BaseURL string `toml:"baseUrl"` + BaseURLModeLegacy bool `toml:"baseUrlModeLegacy"` + SessionSecret string `toml:"sessionSecret"` + CustomDefinitions string `toml:"customDefinitions"` + CheckForUpdates bool `toml:"checkForUpdates"` + DatabaseType string `toml:"databaseType"` + DatabaseMaxBackups int `toml:"databaseMaxBackups"` + PostgresHost string `toml:"postgresHost"` + PostgresPort int `toml:"postgresPort"` + PostgresDatabase string `toml:"postgresDatabase"` + PostgresUser string `toml:"postgresUser"` + PostgresPass string `toml:"postgresPass"` + PostgresSSLMode string `toml:"postgresSSLMode"` + PostgresExtraParams string `toml:"postgresExtraParams"` + ProfilingEnabled bool `toml:"profilingEnabled"` + ProfilingHost string `toml:"profilingHost"` + ProfilingPort int `toml:"profilingPort"` + OIDCEnabled bool `toml:"oidcEnabled" mapstructure:"oidc_enabled"` + OIDCIssuer string `toml:"oidcIssuer" mapstructure:"oidc_issuer"` + OIDCClientID string `toml:"oidcClientId" mapstructure:"oidc_client_id"` + OIDCClientSecret string `toml:"oidcClientSecret" mapstructure:"oidc_client_secret"` + OIDCRedirectURL string `toml:"oidcRedirectUrl" mapstructure:"oidc_redirect_url"` + OIDCScopes string `toml:"oidcScopes" mapstructure:"oidc_scopes"` + OIDCDisableBuiltInLogin bool `toml:"oidcDisableBuiltInLogin" mapstructure:"disable_built_in_login"` + MetricsEnabled bool `toml:"metricsEnabled"` + MetricsHost string `toml:"metricsHost"` + MetricsPort int `toml:"metricsPort"` + MetricsBasicAuthUsers string `toml:"metricsBasicAuthUsers"` } type ConfigUpdate struct { diff --git a/web/src/api/APIClient.ts b/web/src/api/APIClient.ts index eda1315..1ccd181 100644 --- a/web/src/api/APIClient.ts +++ b/web/src/api/APIClient.ts @@ -262,10 +262,10 @@ export const APIClient = { { body: req }), getOIDCConfig: async () => { try { - return await appClient.Get<{ enabled: boolean; authorizationUrl: string; state: string }>("api/auth/oidc/config"); + return await appClient.Get<{ enabled: boolean; authorizationUrl: string; state: string; disableBuiltInLogin: boolean }>("api/auth/oidc/config"); } catch (error: unknown) { if (error instanceof Error && error.message?.includes('404')) { - return { enabled: false, authorizationUrl: '', state: '' }; + return { enabled: false, authorizationUrl: '', state: '', disableBuiltInLogin: false }; } throw error; } diff --git a/web/src/screens/auth/Login.tsx b/web/src/screens/auth/Login.tsx index a6b0e5e..803f0e5 100644 --- a/web/src/screens/auth/Login.tsx +++ b/web/src/screens/auth/Login.tsx @@ -140,83 +140,86 @@ export const Login = () => { -
-
- {/* Only show regular login form if onboarding is not available */} - {!canOnboard && ( - <> -
- - name="username" - id="username" - label="Username" - type="text" - register={register} - rules={{ required: "Username is required" }} - errors={formState.errors} - autoComplete="username" - /> - - name="password" - id="password" - label="Password" - register={register} - rules={{ required: "Password is required" }} - errors={formState.errors} - autoComplete="current-password" - /> + {/* Wait for OIDC config to load before rendering any login forms */} + {typeof oidcConfig !== 'undefined' && ( +
+
+ {/* Built-in login form */} + {!canOnboard && (!oidcConfig?.enabled || !oidcConfig?.disableBuiltInLogin) && ( + <> + + + name="username" + id="username" + label="Username" + type="text" + register={register} + rules={{ required: "Username is required" }} + errors={formState.errors} + autoComplete="username" + /> + + name="password" + id="password" + label="Password" + register={register} + rules={{ required: "Password is required" }} + errors={formState.errors} + autoComplete="current-password" + /> -
-
- - Forgot password? -
- } - > -

Reset via terminal: autobrrctl --config /home/username/.config/autobrr change-password $USERNAME

- +
+
+ + Forgot password? +
+ } + > +

Reset via terminal: autobrrctl --config /home/username/.config/autobrr change-password $USERNAME

+ +
-
+ + + + {oidcConfig?.enabled && ( +
+ + )} + + )} + + {/* OIDC button */} + {oidcConfig?.enabled && ( +
- - - {oidcConfig?.enabled && ( -
- - )} - - )} - - {/* OIDC login button */} - {oidcConfig?.enabled && ( -
- -
- )} +
+ )} +
-
+ )}
); };