Abmelden beendet auch die Authentik-Sitzung und springt zurück ins Tool
- Kopfleiste meldet immer über das Portal ab, auch ohne Portal-Sitzung, und gibt die aktuelle Seite als returnTo mit. - Portal merkt sich den Rücksprung über den Umweg zu Authentik. - Setup-Skript setzt den Abmelde-Ablauf des Providers 'Portal' auf default-invalidation-flow (der Provider-Ablauf meldet nur bei der App ab). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c674fce196
commit
edc64f159a
5 files changed
+59
-8
No files matched your search
+15
-1
@@ -7,7 +7,7 @@ import helmet from 'helmet';
|
||||
import { assertConfig, config } from './config.js';
|
||||
import { initOidc } from './oidc.js';
|
||||
import { SqliteStore } from './sessionStore.js';
|
||||
import { authRouter } from './routes/auth.js';
|
||||
import { AFTER_LOGOUT_COOKIE, authRouter, safeAfterLogout } from './routes/auth.js';
|
||||
import { apiRouter } from './routes/api.js';
|
||||
|
||||
assertConfig();
|
||||
@@ -68,8 +68,22 @@ app.get('/topbar.js', (_req, res) => {
|
||||
});
|
||||
app.use(express.static(publicDir, { index: false, maxAge: '1h' }));
|
||||
|
||||
function readCookie(req: Request, name: string): string | undefined {
|
||||
for (const part of (req.headers.cookie ?? '').split(';')) {
|
||||
const [key, ...value] = part.trim().split('=');
|
||||
if (key === name) return decodeURIComponent(value.join('='));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Portal-Seite: nur für angemeldete Benutzer, sonst direkt zu Authentik.
|
||||
app.get('*', (req, res) => {
|
||||
// Authentik schickt nach dem Abmelden auf "/" zurück; von dort zurück ins Tool, in dem abgemeldet wurde.
|
||||
const afterLogout = safeAfterLogout(readCookie(req, AFTER_LOGOUT_COOKIE));
|
||||
if (afterLogout && req.path === '/' && !req.session.user) {
|
||||
res.clearCookie(AFTER_LOGOUT_COOKIE);
|
||||
return res.redirect(afterLogout);
|
||||
}
|
||||
if (!req.session.user) return res.redirect(`/auth/login?returnTo=${encodeURIComponent(req.originalUrl)}`);
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
res.sendFile(path.join(publicDir, 'index.html'));
|
||||
|
||||
@@ -53,11 +53,29 @@ authRouter.get('/callback', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/** Merkt sich über den Umweg zu Authentik, wohin es nach dem Abmelden zurückgeht (siehe index.ts). */
|
||||
export const AFTER_LOGOUT_COOKIE = 'hi.portal.nach_abmelden';
|
||||
|
||||
export function safeAfterLogout(raw: unknown): string | null {
|
||||
const target = safeReturnTo(raw);
|
||||
return target === '/' ? null : target;
|
||||
}
|
||||
|
||||
// Beendet Portal- und Authentik-Sitzung, auch wenn die Portal-Sitzung schon abgelaufen ist.
|
||||
authRouter.get('/logout', async (req, res, next) => {
|
||||
try {
|
||||
const url = await buildLogoutUrl(req.session.idToken);
|
||||
const returnTo = safeAfterLogout(req.query.returnTo);
|
||||
req.session.destroy(() => {
|
||||
res.clearCookie('hi.portal.sid');
|
||||
if (returnTo) {
|
||||
res.cookie(AFTER_LOGOUT_COOKIE, returnTo, {
|
||||
httpOnly: true,
|
||||
sameSite: 'lax',
|
||||
secure: config.isProduction,
|
||||
maxAge: 5 * 60 * 1000,
|
||||
});
|
||||
}
|
||||
res.redirect(url);
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
Reference in new issue
Block a user