Merge pull request #298 from jubnl/dev
feat: Adds 2 environment variables to control initial admin user credentials, adds 1 environment variable to control OIDC scope
This commit is contained in:
@@ -154,8 +154,11 @@ services:
|
||||
# - OIDC_ONLY=false # Set to true to disable local password auth entirely (SSO only)
|
||||
# - OIDC_ADMIN_CLAIM=groups # OIDC claim used to identify admin users
|
||||
# - OIDC_ADMIN_VALUE=app-trek-admins # Value of the OIDC claim that grants admin role
|
||||
# - OIDC_SCOPE=openid email profile groups # Space-separated OIDC scopes to request (must include scopes for any claim used by OIDC_ADMIN_CLAIM)
|
||||
# - OIDC_DISCOVERY_URL= # Override the OIDC discovery endpoint for providers with non-standard paths (e.g. Authentik)
|
||||
# - DEMO_MODE=false # Enable demo mode (resets data hourly)
|
||||
# - ADMIN_EMAIL=admin@trek.local # Initial admin e-mail — only used on first boot when no users exist
|
||||
# - ADMIN_PASSWORD=changeme # Initial admin password — only used on first boot when no users exist
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./uploads:/app/uploads
|
||||
@@ -287,7 +290,13 @@ trek.yourdomain.com {
|
||||
| `OIDC_CLIENT_SECRET` | OIDC client secret | — |
|
||||
| `OIDC_DISPLAY_NAME` | Label shown on the SSO login button | `SSO` |
|
||||
| `OIDC_ONLY` | Disable local password auth entirely (first SSO login becomes admin) | `false` |
|
||||
| `OIDC_ADMIN_CLAIM` | OIDC claim used to identify admin users | — |
|
||||
| `OIDC_ADMIN_VALUE` | Value of the OIDC claim that grants admin role | — |
|
||||
| `OIDC_SCOPE` | Space-separated OIDC scopes to request. Must include scopes for any claim used by `OIDC_ADMIN_CLAIM` (e.g. add `groups` for group-based admin mapping) | `openid email profile groups` |
|
||||
| `OIDC_DISCOVERY_URL` | Override the auto-constructed OIDC discovery endpoint. Useful for providers that expose it at a non-standard path (e.g. Authentik: `https://auth.example.com/application/o/trek/.well-known/openid-configuration`) | — |
|
||||
| **Initial Setup** | | |
|
||||
| `ADMIN_EMAIL` | Email for the first admin account created on initial boot. Must be set together with `ADMIN_PASSWORD`. If either is omitted a random password is generated and printed to the server log. Has no effect once any user exists. | `admin@trek.local` |
|
||||
| `ADMIN_PASSWORD` | Password for the first admin account created on initial boot. Must be set together with `ADMIN_EMAIL`. | random |
|
||||
| **Other** | | |
|
||||
| `DEMO_MODE` | Enable demo mode (hourly data resets) | `false` |
|
||||
|
||||
|
||||
@@ -42,6 +42,18 @@ spec:
|
||||
name: {{ default (printf "%s-secret" (include "trek.fullname" .)) .Values.existingSecret }}
|
||||
key: {{ .Values.existingSecretKey | default "ENCRYPTION_KEY" }}
|
||||
optional: true
|
||||
- name: ADMIN_EMAIL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ default (printf "%s-secret" (include "trek.fullname" .)) .Values.existingSecret }}
|
||||
key: ADMIN_EMAIL
|
||||
optional: true
|
||||
- name: ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ default (printf "%s-secret" (include "trek.fullname" .)) .Values.existingSecret }}
|
||||
key: ADMIN_PASSWORD
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /app/data
|
||||
|
||||
@@ -8,6 +8,12 @@ metadata:
|
||||
type: Opaque
|
||||
data:
|
||||
{{ .Values.existingSecretKey | default "ENCRYPTION_KEY" }}: {{ .Values.secretEnv.ENCRYPTION_KEY | b64enc | quote }}
|
||||
{{- if .Values.secretEnv.ADMIN_EMAIL }}
|
||||
ADMIN_EMAIL: {{ .Values.secretEnv.ADMIN_EMAIL | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.secretEnv.ADMIN_PASSWORD }}
|
||||
ADMIN_PASSWORD: {{ .Values.secretEnv.ADMIN_PASSWORD | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if and (not .Values.existingSecret) (.Values.generateEncryptionKey) }}
|
||||
@@ -26,4 +32,10 @@ stringData:
|
||||
{{- else }}
|
||||
{{ .Values.existingSecretKey | default "ENCRYPTION_KEY" }}: {{ randAlphaNum 32 }}
|
||||
{{- end }}
|
||||
{{- if .Values.secretEnv.ADMIN_EMAIL }}
|
||||
ADMIN_EMAIL: {{ .Values.secretEnv.ADMIN_EMAIL }}
|
||||
{{- end }}
|
||||
{{- if .Values.secretEnv.ADMIN_PASSWORD }}
|
||||
ADMIN_PASSWORD: {{ .Values.secretEnv.ADMIN_PASSWORD }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -24,6 +24,8 @@ env:
|
||||
# Set to "false" to allow session cookies over plain HTTP (e.g. no ingress TLS). Not recommended for production.
|
||||
# OIDC_DISCOVERY_URL: ""
|
||||
# Override the OIDC discovery endpoint for providers with non-standard paths (e.g. Authentik).
|
||||
# OIDC_SCOPE: "openid email profile groups"
|
||||
# Space-separated OIDC scopes to request. Must include scopes for any claim used by OIDC_ADMIN_CLAIM.
|
||||
|
||||
|
||||
# Secret environment variables stored in a Kubernetes Secret.
|
||||
@@ -36,6 +38,11 @@ secretEnv:
|
||||
# 1. data/.jwt_secret (existing installs — encrypted data stays readable after upgrade)
|
||||
# 2. data/.encryption_key auto-generated on first start (fresh installs)
|
||||
ENCRYPTION_KEY: ""
|
||||
# Initial admin account — only used on first boot when no users exist yet.
|
||||
# If both values are non-empty the admin account is created with these credentials.
|
||||
# If either is empty a random password is generated and printed to the server log.
|
||||
ADMIN_EMAIL: ""
|
||||
ADMIN_PASSWORD: ""
|
||||
|
||||
# If true, a random ENCRYPTION_KEY is generated at install and preserved across upgrades
|
||||
generateEncryptionKey: false
|
||||
|
||||
@@ -79,7 +79,9 @@ export default function App() {
|
||||
const { loadSettings } = useSettingsStore()
|
||||
|
||||
useEffect(() => {
|
||||
loadUser()
|
||||
if (!location.pathname.startsWith('/shared/')) {
|
||||
loadUser()
|
||||
}
|
||||
authApi.getAppConfig().then(async (config: { demo_mode?: boolean; has_maps_key?: boolean; version?: string; timezone?: string; require_mfa?: boolean; trip_reminders_enabled?: boolean; permissions?: Record<string, PermissionLevel> }) => {
|
||||
if (config?.demo_mode) setDemoMode(true)
|
||||
if (config?.has_maps_key !== undefined) setHasMapsKey(config.has_maps_key)
|
||||
|
||||
@@ -26,7 +26,7 @@ apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401 && (error.response?.data as { code?: string } | undefined)?.code === 'AUTH_REQUIRED') {
|
||||
if (!window.location.pathname.includes('/login') && !window.location.pathname.includes('/register')) {
|
||||
if (!window.location.pathname.includes('/login') && !window.location.pathname.includes('/register') && !window.location.pathname.startsWith('/shared/')) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ export default function SettingsPage(): React.ReactElement {
|
||||
setSaving(s => ({ ...s, immich: true }))
|
||||
try {
|
||||
const saveRes = await apiClient.put('/integrations/immich/settings', { immich_url: immichUrl, immich_api_key: immichApiKey || undefined })
|
||||
if (saveRes.data.warning) toast.warn(saveRes.data.warning)
|
||||
if (saveRes.data.warning) toast.warning(saveRes.data.warning)
|
||||
toast.success(t('memories.saved'))
|
||||
const res = await apiClient.get('/integrations/immich/status')
|
||||
setImmichConnected(res.data.connected)
|
||||
|
||||
@@ -31,7 +31,12 @@ services:
|
||||
# - OIDC_CLIENT_SECRET=supersecret # OpenID Connect client secret
|
||||
# - OIDC_DISPLAY_NAME=SSO # Label shown on the SSO login button
|
||||
# - OIDC_ONLY=false # Set true to disable local password auth entirely (SSO only)
|
||||
# - OIDC_ADMIN_CLAIM=groups # OIDC claim used to identify admin users
|
||||
# - OIDC_ADMIN_VALUE=app-trek-admins # Value of the OIDC claim that grants admin role
|
||||
# - OIDC_SCOPE=openid email profile groups # Space-separated OIDC scopes to request (must include scopes for any claim used by OIDC_ADMIN_CLAIM)
|
||||
# - OIDC_DISCOVERY_URL= # Override the OIDC discovery endpoint for providers with non-standard paths (e.g. Authentik)
|
||||
# - ADMIN_EMAIL=admin@trek.local # Initial admin e-mail — only used on first boot when no users exist
|
||||
# - ADMIN_PASSWORD=changeme # Initial admin password — only used on first boot when no users exist
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./uploads:/app/uploads
|
||||
|
||||
@@ -24,5 +24,12 @@ OIDC_ONLY=true # Disable local password auth entirely (SSO only)
|
||||
OIDC_ADMIN_CLAIM=groups # OIDC claim used to identify admin users
|
||||
OIDC_ADMIN_VALUE=app-trek-admins # Value of the OIDC claim that grants admin role
|
||||
OIDC_DISCOVERY_URL= # Override the auto-constructed OIDC discovery endpoint. Useful for providers (e.g. Authentik) that expose it at a non-standard path. Example: https://auth.example.com/application/o/trek/.well-known/openid-configuration
|
||||
OIDC_SCOPE=openid email profile groups # Space-separated OIDC scopes to request (must include scopes for any claim used by OIDC_ADMIN_CLAIM)
|
||||
|
||||
DEMO_MODE=false # Demo mode - resets data hourly
|
||||
|
||||
# Initial admin account — only used on first boot when no users exist yet.
|
||||
# If both are set the admin account is created with these credentials.
|
||||
# If either is omitted a random password is generated and printed to the server log.
|
||||
# ADMIN_EMAIL=admin@trek.local
|
||||
# ADMIN_PASSWORD=changeme
|
||||
|
||||
@@ -22,9 +22,21 @@ function seedAdminAccount(db: Database.Database): void {
|
||||
}
|
||||
|
||||
const bcrypt = require('bcryptjs');
|
||||
const password = crypto.randomBytes(12).toString('base64url');
|
||||
|
||||
const env_admin_email = process.env.ADMIN_EMAIL;
|
||||
const env_admin_pw = process.env.ADMIN_PASSWORD;
|
||||
|
||||
let password;
|
||||
let email;
|
||||
if (env_admin_email && env_admin_pw) {
|
||||
password = env_admin_pw;
|
||||
email = env_admin_email;
|
||||
} else {
|
||||
password = crypto.randomBytes(12).toString('base64url');
|
||||
email = 'admin@trek.local';
|
||||
}
|
||||
|
||||
const hash = bcrypt.hashSync(password, 12);
|
||||
const email = 'admin@trek.local';
|
||||
const username = 'admin';
|
||||
|
||||
db.prepare('INSERT INTO users (username, email, password_hash, role, must_change_password) VALUES (?, ?, ?, ?, 1)').run(username, email, hash, 'admin');
|
||||
|
||||
@@ -66,6 +66,7 @@ app.use(helmet({
|
||||
"https://*.basemaps.cartocdn.com", "https://*.tile.openstreetmap.org",
|
||||
"https://unpkg.com", "https://open-meteo.com", "https://api.open-meteo.com",
|
||||
"https://geocoding-api.open-meteo.com", "https://api.exchangerate-api.com",
|
||||
"https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_50m_admin_0_countries.geojson"
|
||||
],
|
||||
fontSrc: ["'self'", "https://fonts.gstatic.com", "data:"],
|
||||
objectSrc: ["'none'"],
|
||||
|
||||
@@ -138,7 +138,7 @@ router.get('/login', async (req: Request, res: Response) => {
|
||||
response_type: 'code',
|
||||
client_id: config.clientId,
|
||||
redirect_uri: redirectUri,
|
||||
scope: 'openid email profile',
|
||||
scope: process.env.OIDC_SCOPE || 'openid email profile groups',
|
||||
state,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user