Add Authentik OIDC login as an additional sign-in option
Local auth stays the primary/always-available login (don't want to lock out the saved admin password) — OIDC is additive, shown as a second button when OIDC_ENABLED=true. Uses openid-client v6 with PKCE. Authentik-side provider was set up via an authentik blueprint (its own declarative automation, see docs/oidc-setup.md) rather than touching any existing admin credentials. Closes #12. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,6 @@ export interface AppConfig {
|
||||
dbPath: string;
|
||||
pollIntervalSeconds: number;
|
||||
snapshotRetentionHours: number;
|
||||
authMode: "local" | "oidc";
|
||||
sessionSecret: string;
|
||||
// Only set once a TLS-terminating reverse proxy sits in front (see issue #13) —
|
||||
// browsers silently drop `secure` cookies over plain HTTP.
|
||||
@@ -47,9 +46,19 @@ export interface AppConfig {
|
||||
sshHosts: SshHostConfig[];
|
||||
knownDevices: Map<string, string>;
|
||||
discoveryFilePath: string;
|
||||
oidc: OidcConfig | undefined;
|
||||
hosts: HostsConfig;
|
||||
}
|
||||
|
||||
export interface OidcConfig {
|
||||
issuerUrl: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
redirectUri: string;
|
||||
// Authentik's cert is self-signed on the LAN, same as Proxmox's.
|
||||
allowInsecureTls: boolean;
|
||||
}
|
||||
|
||||
function required(name: string): string {
|
||||
const v = process.env[name];
|
||||
if (!v) throw new Error(`Missing required env var: ${name}`);
|
||||
@@ -60,6 +69,20 @@ export function loadConfig(hostsConfigPath: string): AppConfig {
|
||||
const hosts = parse(readFileSync(hostsConfigPath, "utf8")) as HostsConfig;
|
||||
const sshPrivateKeyPath = process.env.SSH_PRIVATE_KEY_PATH ?? "./ssh/monitor_ed25519";
|
||||
|
||||
// Local auth (bcrypt + session) is always available. OIDC is an *additional*
|
||||
// sign-in option when configured, not a replacement — see issue #12 comments
|
||||
// for why (don't want to lock out the already-saved local admin password).
|
||||
const oidc: OidcConfig | undefined =
|
||||
process.env.OIDC_ENABLED === "true"
|
||||
? {
|
||||
issuerUrl: required("OIDC_ISSUER_URL"),
|
||||
clientId: required("OIDC_CLIENT_ID"),
|
||||
clientSecret: required("OIDC_CLIENT_SECRET"),
|
||||
redirectUri: required("OIDC_REDIRECT_URI"),
|
||||
allowInsecureTls: process.env.OIDC_ALLOW_INSECURE_TLS === "true",
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const sshHosts: SshHostConfig[] = (hosts.sshHosts ?? []).map((h) => ({
|
||||
id: h.id,
|
||||
displayName: h.displayName,
|
||||
@@ -76,7 +99,6 @@ export function loadConfig(hostsConfigPath: string): AppConfig {
|
||||
dbPath: process.env.DB_PATH ?? "./data/monitor.db",
|
||||
pollIntervalSeconds: Number(process.env.POLL_INTERVAL_SECONDS ?? 30),
|
||||
snapshotRetentionHours: Number(process.env.SNAPSHOT_RETENTION_HOURS ?? 24),
|
||||
authMode: (process.env.AUTH_MODE as "local" | "oidc") ?? "local",
|
||||
sessionSecret: required("SESSION_SECRET"),
|
||||
cookieSecure: process.env.COOKIE_SECURE === "true",
|
||||
adminUsername: process.env.ADMIN_USERNAME ?? "admin",
|
||||
@@ -90,6 +112,7 @@ export function loadConfig(hostsConfigPath: string): AppConfig {
|
||||
sshHosts,
|
||||
knownDevices: new Map((hosts.knownDevices ?? []).map((d) => [d.ip, d.name])),
|
||||
discoveryFilePath: process.env.DISCOVERY_FILE_PATH ?? "./data/devices-raw.json",
|
||||
oidc,
|
||||
hosts,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user