Files
check-printing/src/routes/settings.js
T
steve 3b1a35b7f2 feat: add OIDC login with account linking
Add OpenID Connect as an alternative login method. Users can sign in
via an external identity provider (e.g., Authentik, Keycloak, Google).

- OIDC settings configured in admin UI (discovery URL, client ID/secret,
  redirect URI, button label, enable/disable toggle)
- PKCE-based authorization code flow with state and nonce validation
- Admin can manually link any user's OIDC identity (sub/issuer fields)
- Self-service linking: logged-in users can link/unlink their own account
- SSO button conditionally shown on login page when OIDC is enabled
- Username in header now clickable to open profile for all users
- Callback errors/success communicated via URL hash fragments
2026-04-09 14:48:50 -06:00

71 lines
2.6 KiB
JavaScript

'use strict';
const express = require('express');
const router = express.Router();
const db = require('../db/database');
const { requireAdmin } = require('../middleware/auth');
router.use(requireAdmin);
// GET /api/settings/smtp
router.get('/smtp', (req, res) => {
const rows = db.prepare("SELECT key, value FROM settings WHERE key LIKE 'smtp_%'").all();
const s = Object.fromEntries(rows.map(r => [r.key.replace('smtp_', ''), r.value || '']));
res.json({
host: s.host || '',
port: s.port || '587',
secure: s.secure === '1',
user: s.user || '',
from: s.from || '',
has_password: !!(rows.find(r => r.key === 'smtp_pass') || {}).value,
});
});
// PUT /api/settings/smtp
router.put('/smtp', (req, res) => {
const { host, port, secure, user, pass, from } = req.body;
const upsert = db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)');
db.transaction(() => {
upsert.run('smtp_host', host || '');
upsert.run('smtp_port', String(parseInt(port, 10) || 587));
upsert.run('smtp_secure', secure ? '1' : '0');
upsert.run('smtp_user', user || '');
if (pass !== undefined && pass !== '') upsert.run('smtp_pass', pass);
upsert.run('smtp_from', from || '');
})();
res.json({ ok: true });
});
// GET /api/settings/oidc
router.get('/oidc', (req, res) => {
const rows = db.prepare("SELECT key, value FROM settings WHERE key LIKE 'oidc_%'").all();
const s = Object.fromEntries(rows.map(r => [r.key.replace('oidc_', ''), r.value || '']));
res.json({
enabled: s.enabled === '1',
discovery_url: s.discovery_url || '',
client_id: s.client_id || '',
redirect_uri: s.redirect_uri || '',
button_label: s.button_label || 'Sign in with SSO',
has_secret: !!(rows.find(r => r.key === 'oidc_client_secret') || {}).value,
});
});
// PUT /api/settings/oidc
router.put('/oidc', (req, res) => {
const { enabled, discovery_url, client_id, client_secret, redirect_uri, button_label } = req.body;
const upsert = db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)');
db.transaction(() => {
upsert.run('oidc_enabled', enabled ? '1' : '0');
upsert.run('oidc_discovery_url', discovery_url || '');
upsert.run('oidc_client_id', client_id || '');
upsert.run('oidc_redirect_uri', redirect_uri || '');
upsert.run('oidc_button_label', button_label || 'Sign in with SSO');
if (client_secret !== undefined && client_secret !== '') {
upsert.run('oidc_client_secret', client_secret);
}
})();
res.json({ ok: true });
});
module.exports = router;