- Dienste (Gitea, Kanban, Cloud …) werden im System-Tab gepflegt und erscheinen als Kacheln auf der Startseite; GET /api/services ist öffentlich, Pflege braucht den settings-Scope - /brand.css liefert die Design-Tokens (Farben live aus dem Brand-Tab, ändern sich damit überall mit) plus Basis-Klassen d4rk-card, d4rk-btn, d4rk-title, d4rk-tag - /brand-nav.js baut die D4RKST3R-Leiste in jede fremde App ein, mit Links zum Hub und zu allen gepflegten Diensten; die Links stecken fertig im Skript, dadurch kein zweiter Request und kein CORS nötig - Beide Dateien mit offenem CORS-Header und 5 Minuten Cache - Doku für Anbindung + Schriften in docs/sso.md ergänzt Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.8 KiB
Single Sign-On mit d4rkbot
Der Bot ist Identity-Provider für die anderen D4RKST3R-Dienste (Kanban, Platform …). Statt in jeder App einen eigenen Discord-OAuth2-Flow zu bauen, fragst du beim Bot nach — und bekommst dabei gleich die Discord-Rollen des Users mitgeliefert.
1. App registrieren
Setup → API → Single Sign-On:
| Feld | Beispiel | Bedeutung |
|---|---|---|
| Kürzel | kanban |
interner Slug, taucht in der URL auf |
| Name | Kanban-Board |
Anzeigename |
| Rücksprung-URL | https://kanban.d4rkst3r.de/ |
erlaubte Ziele (mehrere kommagetrennt) |
Beim Anlegen wird ein Secret angezeigt — einmalig, danach nie wieder.
In der App als Umgebungsvariable hinterlegen (SSO_SECRET), niemals ins Repo.
Der Bot leitet ausschließlich auf Adressen zurück, die mit einer der hinterlegten Rücksprung-URLs beginnen. Das verhindert, dass jemand den Login-Flow auf eine fremde Seite umbiegt und den Token abgreift.
2. Ablauf
Nutzer klickt "Login" → https://bot.d4rkst3r.de/sso/authorize
?app=kanban
&redirect=https://kanban.d4rkst3r.de/auth/callback
↓ (Bot prüft Session, ggf. erst Discord-Login)
Zurück zu https://kanban.d4rkst3r.de/auth/callback?sso_token=<token>
↓ (App tauscht den Token serverseitig ein)
POST https://bot.d4rkst3r.de/sso/verify
Authorization: Bearer <SSO_SECRET>
{ "token": "<token>" }
Der Token ist 60 Sekunden gültig und nur einmal einlösbar. Er selbst enthält keine Nutzerdaten — die gibt es erst beim Verify-Aufruf, der serverseitig passieren muss.
3. Antwort von /sso/verify
{
"user": {
"id": "123456789012345678",
"username": "d4rkst3r",
"displayName": "Alexander",
"avatar": "https://cdn.discordapp.com/avatars/…",
"joinedAt": "2026-02-01T12:00:00.000Z"
},
"roles": [
{ "id": "111…", "name": "Entwickler" },
{ "id": "222…", "name": "Playtester" }
],
"owner": false,
"scopes": ["content", "server"],
"app": "kanban"
}
owner— ist der Server-Owner (ADMIN_DISCORD_ID)scopes— Team-Bereiche aus dem Bot (['*']beim Owner, sonst z. B.['content'])roles— alle Discord-Rollen; damit kannst du in der App Rechte vergeben, ohne eine eigene Nutzerverwaltung zu pflegen
4. Beispiel (Node / Express)
const BOT = 'https://bot.d4rkst3r.de';
app.get('/login', (req, res) => {
const redirect = `${process.env.PUBLIC_URL}/auth/callback`;
res.redirect(`${BOT}/sso/authorize?app=kanban&redirect=${encodeURIComponent(redirect)}`);
});
app.get('/auth/callback', async (req, res) => {
const result = await fetch(`${BOT}/sso/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.SSO_SECRET}`,
},
body: JSON.stringify({ token: req.query.sso_token }),
});
if (!result.ok) return res.status(401).send('Login fehlgeschlagen');
const { user, roles, owner } = await result.json();
req.session.user = {
id: user.id,
name: user.displayName ?? user.username,
avatar: user.avatar,
// Rechte direkt aus den Discord-Rollen ableiten
canEdit: owner || roles.some((r) => r.name === 'Entwickler'),
};
res.redirect('/');
});
5. Aussehen mitnehmen (Design-System)
Damit die angebundene App nicht wie ein Fremdkörper wirkt, liefert der Bot die Brand-Tokens und eine fertige Navigation aus:
<link rel="stylesheet" href="https://bot.d4rkst3r.de/brand.css">
<script defer src="https://bot.d4rkst3r.de/brand-nav.js"></script>
/brand.css— CSS-Variablen (--neon,--bg,--display…) plus ein paar Basis-Klassen:.d4rk-card,.d4rk-btn,.d4rk-title,.d4rk-tag. Die Farben kommen live aus dem Brand-Tab — änderst du sie dort, ändern sie sich überall mit (Cache: 5 Minuten)./brand-nav.js— fügt oben die D4RKST3R-Leiste ein, mit Links zurück zum Hub und zu allen im Setup gepflegten Diensten. Die Links stecken fertig im Skript, es sind also keine weiteren Anfragen und kein CORS nötig.
Die Schriften installierst du in der App selbst, damit sie lokal ausgeliefert werden (kein Google-Fonts-Aufruf):
npm i @fontsource/bebas-neue @fontsource/barlow-condensed @fontsource/share-tech-mono
6. Hinweise
- Der Verify-Aufruf muss serverseitig erfolgen — das Secret darf nie im Browser landen.
- Nur Mitglieder des konfigurierten Discord-Servers können sich anmelden; alle anderen landen auf der Beitritts-Seite des Bots.
- Rollen werden bei jedem Login frisch von Discord geholt. Wer eine Rolle verliert, verliert die Rechte beim nächsten Login — für sofortige Wirkung die Sitzung in der App kurz halten oder periodisch neu prüfen.
- Wird eine App im Setup gelöscht, funktioniert ihr Login sofort nicht mehr.