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
This commit is contained in:
@@ -36,4 +36,35 @@ router.put('/smtp', (req, res) => {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user