Baue eine Multi-Tenant-SaaS-Anwendung: Ein vollständiger Leitfaden von Design bis Implementierung
Wie werden Apps wie Notion, Slack oder Figma gebaut? Diese Multi-Tenant-SaaS-Anwendungen wirken einfach zu bedienen, aber selbst eine zu bauen? Das ist eine andere Geschichte.
Als ich zum ersten Mal daran dachte, so ein komplexes Biest zu bauen, explodierte mein Kopf:
- Benutzer benötigen mehrere Anmeldeoptionen (E-Mail, Google, GitHub)
- Jeder Benutzer kann mehrere Organisationen erstellen und ihnen angehören
- Unterschiedliche Berechtigungsstufen innerhalb jeder Organisation
- Enterprise-Organisationen, die Auto-Join für bestimmte E-Mail-Domains erfordern
- MFA-Anforderungen für sensible Operationen
- Und mehr...
„Chef, lass uns in zwei Wochen über das Produktdesign sprechen. Ich stecke gerade fest.“
Aber als ich tatsächlich damit anfing, stellte ich fest, dass es gar nicht so entmutigend ist, wie es scheint.
Ich habe gerade ein System mit all diesen Funktionen mit überraschend wenig Aufwand gebaut!



Ich zeige dir genau, wie du ein solches System von Grund auf entwirfst und implementierst – und du wirst erstaunt sein, wie einfach es im Jahr 2025 mit modernen Tools und dem richtigen Architekturansatz wirklich ist.
Der vollständige Quellcode ist in diesem Github Repo verfügbar. Lass uns eintauchen!
Wir beginnen mit einem KI-Dokumentations-SaaS-Produkt namens DocuMind.
DocuMind ist ein KI-Dokumentations-SaaS-Produkt, das mit einem Multi-Tenant-Modell entwickelt wurde, um Einzelpersonen, kleine Unternehmen und Konzerne zu unterstützen.
Die Plattform bietet leistungsstarke KI-Funktionen für das Dokumentenmanagement, einschließlich automatischer Zusammenfassungen, Extraktion von Schlüsselpunkten und intelligente Inhaltsvorschläge innerhalb von Organisationen.
Welche Funktionen sind für SaaS Authentifizierung und Autorisierung erforderlich?
Schauen wir uns zunächst die notwendigen Anforderungen an. Welche Funktionen benötigst du?
Multi-Tenant-Architektur
Um eine Multi-Tenant-Architektur zu ermöglichen, benötigst du eine Entitätsschicht namens Organisation. Stell dir vor, du hast einen einzigen Pool von Benutzern, die auf mehrere Arbeitsbereiche zugreifen können. Jede Organisation repräsentiert einen Arbeitsbereich, und Benutzer behalten eine einzige Identität, während sie auf verschiedene Arbeitsbereiche (Organisationen) basierend auf ihren zugewiesenen Rollen zugreifen.
Dies ist eine weit verbreitete Funktion bei Authentifizierungsanbietern. Eine Organisation in einem Identitätsmanagementsystem entspricht dem Workspace, Projekt oder Tenant deiner SaaS-App.

Mitgliedschaft
Ein Mitglied ist ein temporäres Konzept, das den Mitgliedsstatus einer Identität innerhalb einer Organisation anzeigt.
Beispiel: Sarah registriert sich mit ihrer E-Mail sarah@gmail.com in deiner App. Sie kann verschiedenen Arbeitsbereichen angehören. Wenn Sarah Teil von Workspace A ist, aber nicht von Workspace B, gilt sie als Mitglied von Workspace A, aber nicht von Workspace B.
Rollen- und Berechtigungsdesign
In einer Multi-Tenant-Architektur benötigen Benutzer Rollen mit bestimmten Berechtigungen, um auf ihre Tenant-Ressourcen zuzugreifen.
Berechtigungen sind detaillierte Zugangskontrollen, die bestimmte Aktionen definieren, wie read: order oder write: order. Sie bestimmen, welche Aktionen auf bestimmten Ressourcen ausgeführt werden dürfen.
Rollen sind eine Sammlung von Berechtigungen, die Mitgliedern in einer Multi-Tenant-Umgebung zugewiesen werden.
Du musst diese Rollen und Berechtigungen definieren, dann Rollen an Benutzer zuweisen, und manchmal kann dies automatisierte Prozesse beinhalten. Zum Beispiel:
- Benutzer, die einer Organisation beitreten, erhalten automatisch die Mitglied-Rolle.
- Der erste Benutzer, der einen Arbeitsbereich erstellt, erhält automatisch die Admin-Rolle.
Registrierungs- und Anmeldefluss
Stelle einen benutzerfreundlichen und sicheren Registrierungs- und Authentifizierungsprozess sicher, einschließlich grundlegender Anmelde- und Registrierungsoptionen:
- E-Mail- und Passwort-Anmeldung: Traditionelle Anmeldemethode mit E-Mail und Passwort.
- Passwortlose Anmeldung: Verwende E-Mail-Bestätigungscodes für einfachen und sicheren Zugang.
- Kontoverwaltung: Ein Account Center, in dem Benutzer ihre E-Mail, ihr Passwort und andere Details aktualisieren können.
- Soziale Anmeldung: Optionen wie Google und GitHub für schnelles Login.
- Multi-Faktor-Authentifizierung (MFA): Erhöhe die Sicherheit, indem du die Anmeldung über Authenticator-Apps wie Duo ermöglichst.
Tenant-Erstellung und Einladung
In einer Multi-Tenant-SaaS-App ist ein wichtiger Unterschied im Benutzerfluss die Notwendigkeit, die Tenant-Erstellung und Mitglieder-Einladungen zu unterstützen. Dieser Prozess erfordert sorgfältige Planung und Ausführung, da er eine Schlüsselrolle bei der Produktaktivierung und dem Wachstum spielt.
Hier sind einige typische Nutzungsflüsse, die du berücksichtigen solltest:
| Benutzertyp | Einstiegspunkt |
|---|---|
| Neues Konto | Einstieg über Anmelde- und Registrierungsseite zur Erstellung eines neuen Tenants |
| Bestehendes Konto | Erstelle einen weiteren Tenant innerhalb des Produkts |
| Bestehendes Konto erhält neue Tenant-Einladung | Einstieg über Anmelde- und Registrierungsseite |
| Bestehendes Konto erhält neue Tenant-Einladung | Einstieg über die Einladungsemail |
| Neues Konto erhält neue Tenant-Einladung | Einstieg über Anmelde- und Registrierungsseite |
| Neues Konto erhält neue Tenant-Einladung | Einstieg über die Einladungsemail |
Dies sind einige gängige Szenarien, die in fast jeder SaaS-App zu finden sind. Nutze sie als Referenz, um dein Produkt- und Designteam zu inspirieren, und erstelle bei Bedarf eigene Flows.






Technische Architektur und Systemdesign
Sobald wir alle Produktanforderungen verstanden haben, gehen wir zur Implementierung über.
Authentifizierungsstrategie definieren
Authentifizierung wirkt einschüchternd. Benutzer benötigen:
- E-Mail & Passwort Registrierung/Anmeldung
- Ein-Klick-Anmeldung mit Google/Github
- Passwort zurücksetzen, wenn sie es vergessen
- Teamweite Anmeldung für Unternehmenskunden
- ...
Allein die Implementierung dieser Grundfunktionen könnte Wochen Entwicklungszeit kosten.
Aber jetzt müssen wir das alles NICHT mehr selbst bauen!
Moderne Auth-Anbieter (ich wähle diesmal Logto) haben all diese Funktionen für uns gebündelt. Der Authentifizierungsfluss ist unkompliziert:
Von Wochen Entwicklungszeit zu 15 Minuten Einrichtung – Logto übernimmt alle komplexen Flows für uns! Die Integrationsschritte behandeln wir später im Implementierungsteil. Jetzt können wir uns auf die Kernfunktionen von DocuMind konzentrieren!
Multi-Tenant-Architektur etablieren
Das Organisationssystem ermöglicht es Benutzern, mehrere Organisationen zu erstellen und ihnen beizutreten. Schauen wir uns die Kernbeziehungen an:
In diesem System kann jeder Benutzer mehreren Organisationen angehören und jede Organisation kann mehrere Mitglieder haben.
Zugangskontrolle in Multi-Tenant-App aktivieren
Rollenbasierte Zugangskontrolle (RBAC) ist wichtig für Sicherheit und Skalierbarkeit in Multi-Tenant-SaaS-Anwendungen.
In einer Multi-Tenant-App ist das Design von Berechtigungen und Rollen meist konsistent, da es aus dem Produktdesign hervorgeht. Zum Beispiel gibt es in mehreren Arbeitsbereichen typischerweise eine Admin-Rolle und eine Mitglieder-Rolle. Logto als Auth-Anbieter hat folgendes rollenbasiertes Zugangskontroll-Design auf Organisationsebene:
- Einheitliche Berechtigungsdefinitionen: Berechtigungen werden auf Systemebene definiert und gelten konsistent für alle Organisationen, was eine wartbare und konsistente Berechtigungsverwaltung ermöglicht.
- Organisationstemplates: Vorgefertigte Kombinationen aus Rollen und Berechtigungen durch Organisationstemplates, die die Initialisierung von Organisationen vereinfachen.
Die Berechtigungsbeziehung sieht so aus:
Da jeder Benutzer innerhalb jeder Organisation eigene Rolle(n) benötigt, muss die Beziehung zwischen Rollen und Organisationen die zugewiesenen Rollen jedes Benutzers widerspiegeln:
Wir haben das Organisationssystem und das Zugangskontrollsystem entworfen und können nun mit dem Bau unseres Produkts beginnen!
Tech-Stack
Ich habe einen einsteigerfreundlichen, portablen Stack gewählt:
- Frontend: React (leicht auf Vue/Angular/Svelte übertragbar)
- Backend: Express (einfaches, intuitives API)
Warum Frontend und Backend trennen? Weil es eine klare Architektur hat, leicht zu erlernen und einfach zu wechseln ist. Und als Auth-Anbieter verwende ich Logto als Beispiel.
Und für die folgenden Anleitungen gilt: Die Muster funktionieren mit jedem Frontend, jedem Backend und jedem Auth-System.
Füge deiner App einen grundlegenden Authentifizierungsfluss hinzu
Das ist der einfachste Schritt. Wir müssen nur Logto in unser Projekt integrieren. Dann können wir im Logto Console die Methoden für Benutzeranmeldung/-registrierung nach unseren Bedürfnissen konfigurieren.
Logto in deiner App installieren
Melde dich zuerst bei Logto Cloud an. Du kannst ein kostenloses Konto erstellen, falls du noch keines hast. Erstelle einen Development Tenant zum Testen.
Im Tenant Console klicke auf die Schaltfläche "Application" links. Wähle dann React, um mit dem Bau unserer Anwendung zu beginnen.
Folge der Anleitung auf der Seite. Die Logto-Integration ist in etwa 5 Minuten abgeschlossen!
Hier ist mein Integrationscode:
const config: LogtoConfig = {
endpoint: "<YOUR_LOGTO_ENDPOINT>",
appId: "<YOUR_LOGTO_APP_ID>",
};
function App() {
return (
<LogtoProvider config={config}>
<div className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100">
<Routes>
{/* Dieser Callback verarbeitet die Benutzer-Login-Weiterleitung von Logto */}
<Route path="/callback" element={<Callback />} />
<Route path="/*" element={<AppContent />} />
</Routes>
</div>
</LogtoProvider>
);
}
function AppContent() {
const { isAuthenticated } = useLogto();
if (!isAuthenticated) {
// Zeige Landingpage für nicht authentifizierte Benutzer
return <Landing />;
}
// Zeige Haupt-App für authentifizierte Benutzer
return (
<Routes>
{/* Dashboard zeigt alle verfügbaren Organisationen */}
<Route path="/" element={<Dashboard />} />
{/* Organisationsseite nach Klick auf eine Organisation im Dashboard */}
<Route path="/:orgId" element={<Organization />} />
</Routes>
);
}

Ein nützlicher Trick: Unsere Login-Seite hat sowohl Anmelden- als auch Registrieren-Buttons. Der Registrieren-Button führt direkt zur Registrierungsseite von Logto. Das funktioniert über Logtos First Screen-Funktion. Sie bestimmt, welchen Schritt des Auth-Flows Benutzer zuerst sehen.
Du kannst die Registrierungsseite als Standard setzen, wenn dein Produkt viele neue Benutzer erwartet.
function LandingPage() {
const { signIn } = useLogto();
return (
<div className="landing-container">
<div className="auth-buttons">
<button
className="sign-in-button"
onClick={() => {
signIn({
redirectUri: '<YOUR_APP_CALLBACK_URL>',
});
}}
>
Anmelden
</button>
<button
className="register-button"
onClick={() => {
signIn({
redirectUri: '<YOUR_APP_CALLBACK_URL>',
firstScreen: 'register',
});
}}
>
Registrieren
</button>
</div>
</div>
);
}
Nach dem Klick auf Anmelden gelangst du zur Logto-Anmeldeseite. Nach erfolgreicher Anmeldung (oder Registrierung) – Glückwunsch! Deine App hat ihren ersten Benutzer (dich)!
Und rufe die signOut-Funktion aus dem useLogto-Hook auf, um den Benutzer abzumelden, wann immer du möchtest.
function SignOutButton() {
const { signOut } = useLogto();
return <button onClick={() => signOut('<YOUR_POST_LOGOUT_REDIRECT_URL>')}>Abmelden</button>;
}
Anmelde- und Registrierungsarten anpassen
Im Logto Console klicke im linken Menü auf "Sign-in & account". Dann auf den Tab "Sign-up and sign-in". Auf dieser Seite folge den Anweisungen, um die Login-/Registrierungsmethoden von Logto zu konfigurieren.

Und der Anmeldefluss sieht dann so aus:
Multi-Faktor-Authentifizierung aktivieren
Mit Logto ist das Aktivieren von MFA einfach. Klicke einfach auf die Schaltfläche "Multi-factor auth" im Logto Console. Dann aktiviere es auf der Multi-Faktor-Authentifizierungsseite.

Und der MFA-Fluss sieht dann so aus:


Alles ist so einfach! Wir haben in wenigen Minuten ein komplexes Authentifizierungssystem eingerichtet!
Multi-Tenant-Organisationserfahrung hinzufügen
Jetzt haben wir unseren ersten Benutzer! Allerdings gehört dieser Benutzer noch keiner Organisation an, und wir haben auch noch keine Organisationen erstellt.
Logto bietet integrierte Unterstützung für Multi-Tenancy. Du kannst beliebig viele Organisationen in Logto erstellen. Jede Organisation kann mehrere Mitglieder haben.
Jeder Benutzer kann seine Organisationsinformationen von Logto abrufen. Das ermöglicht Multi-Tenancy-Unterstützung.
Organisationsinformationen eines Benutzers abrufen
Um die Organisationsinformationen eines Benutzers von Logto zu erhalten, befolge diese zwei Schritte:
Deklariere den Zugriff auf Organisationsinformationen im Logto Config. Dies geschieht durch Setzen der entsprechenden scopes und resources.
import { UserScope, ReservedResource } from "@logto/react";
const config: LogtoConfig = {
endpoint: "<YOUR_LOGTO_ENDPOINT>",
appId: "<YOUR_LOGTO_APP_ID>",
scopes: [UserScope.Organizations], // Wert: "urn:logto:scope:organizations"
resources: [ReservedResource.Organization], // Wert: "urn:logto:resource:organizations"
};
Nutze Logtos fetchUserInfo-Methode, um Benutzerinformationen einschließlich Organisationsdaten zu erhalten.
function Dashboard() {
// Benutzerinfo abrufen
const { fetchUserInfo } = useLogto();
const [organizations, setOrganizations] = useState<OrganizationData[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const loadOrganizations = async () => {
try {
setLoading(true);
// Benutzerinfo abrufen
const userInfo = await fetchUserInfo();
// Organisationsinfo des Benutzers abrufen
const organizationData = userInfo?.organization_data || [];
setOrganizations(organizationData);
} catch (error) {
console.error('Fehler beim Abrufen der Organisationen:', error);
} finally {
setLoading(false);
}
};
loadOrganizations();
}, [fetchUserInfo]);
if (loading) {
return <div>Lädt...</div>;
}
if (organizations.length === 0) {
return <div>Du bist noch kein Mitglied einer Organisation</div>;
}
return <div>Organisationen: {organizations.map(org => org.name).join(', ')}</div>;
}
Nachdem du diese Schritte abgeschlossen hast, musst du dich ab- und wieder anmelden. Das ist notwendig, weil wir den angeforderten Scope und die Resource geändert haben.
Im Moment hast du noch keine Organisationen erstellt. Der Benutzer ist auch noch keiner Organisation beigetreten. Das Dashboard zeigt "Du hast noch keine Organisation".

Als Nächstes erstellen wir eine Organisation für unsere Benutzer und fügen sie hinzu.
Dank Logto müssen wir keine komplexen Organisationsbeziehungen bauen. Wir müssen nur eine Organisation in Logto erstellen und Benutzer hinzufügen. Logto übernimmt die gesamte Komplexität für uns. Es gibt zwei Möglichkeiten, Organisationen zu erstellen:
- Manuelles Erstellen von Organisationen über das Logto Console
- Verwendung der Logto Management API zum Erstellen von Organisationen, insbesondere wenn du einen SaaS-Flow entwirfst, der es Benutzern ermöglicht, eigene Organisationen (Workspaces) zu erstellen.
Organisation im Logto Console erstellen
Klicke im Logto Console auf die Schaltfläche "Organizations" im linken Menü. Erstelle eine Organisation.
Jetzt hast du deine erste Organisation.

Als Nächstes fügen wir den Benutzer zu dieser Organisation hinzu.
Gehe zur Organisationsdetailseite. Wechsle zum Tab Mitglieder. Klicke auf die Schaltfläche "+ Mitglied hinzufügen". Wähle deinen Login-Benutzer aus der linken Liste. Klicke auf die Schaltfläche "Mitglieder hinzufügen" unten rechts. Jetzt hast du den Benutzer erfolgreich zur Organisation hinzugefügt.

Aktualisiere deine APP-Seite. Du siehst, dass der Benutzer jetzt einer Organisation angehört!

Self-Service-Organisationserstellung implementieren
Eine Organisation im Console zu erstellen reicht nicht aus. Deine SaaS-App benötigt einen Flow, der Endbenutzern ermöglicht, einfach eigene Workspaces zu erstellen und zu verwalten. Um diese Funktion zu implementieren, verwende die Logto Management API.
Für Anleitungen siehe die Interaktion mit Management API-Dokumentation, um die API-Kommunikation mit Logto einzurichten.
Auth-Interaktionsfluss für Organisationen verstehen
Nehmen wir den Organisations-Erstellungsfluss als Beispiel. So funktioniert der Prozess:
Dieser Flow hat zwei wichtige Authentifizierungsanforderungen:
- Backend-Service-API schützen:
- Frontend-Zugriff auf unsere Backend-Service-API erfordert Authentifizierung
- API-Endpunkte sind durch Validierung des Logto Zugangstokens des Benutzers geschützt
- Stellt sicher, dass nur authentifizierte Benutzer auf unsere Dienste zugreifen können
- Zugriff auf Logto Management API:
- Backend-Service muss sicher die Logto Management API aufrufen
- Folge der Anleitung Interaktion mit Management API für die Einrichtung
- Verwende Maschine-zu-Maschine-Authentifizierung, um Zugangsdaten zu erhalten
Schütze deine Backend-API
Erstelle zunächst einen API-Endpunkt in unserem Backend-Service zur Organisationserstellung.
app.post('/organizations', async (req, res) => {
// Implementierung mit Logto Management API
// ...
});
Unsere Backend-Service-API erlaubt nur authentifizierten Benutzern den Zugriff. Wir müssen Logto verwenden, um unsere API zu schützen. Außerdem benötigen wir die aktuellen Benutzerinformationen (wie Benutzer-ID).
Im Logto-Konzept (und OAuth 2.0) agiert unser Backend-Service als Ressourcensserver. Benutzer greifen mit einem Zugangstoken vom Frontend auf den DocuMind-Ressourcenserver zu. Der Ressourcenserver prüft dieses Token. Ist es gültig, gibt er die angeforderten Ressourcen zurück.
Erstelle eine API-Ressource, um unseren Backend-Service darzustellen.
Gehe zum Logto Console.
- Klicke auf die Schaltfläche "API resources" rechts.
- Klicke auf "Create API resource". Wähle Express im Popup.
- Gib "DocuMind API" als API-Namen ein. Verwende "https://api.documind.com" als API-Identifier.
- Klicke auf Erstellen.
Mach dir keine Sorgen um diese API-Identifier-URL. Sie ist nur ein eindeutiger Bezeichner für deine API in Logto. Sie ist nicht mit deiner tatsächlichen Backend-Service-URL verbunden.
Du siehst ein Tutorial zur Verwendung der API-Ressource. Du kannst diesem Tutorial oder unseren Schritten unten folgen.
Erstelle ein requireAuth-Middleware, um unseren POST /organizations-Endpunkt zu schützen.
const { createRemoteJWKSet, jwtVerify } = require('jose');
const getTokenFromHeader = (headers) => {
const { authorization } = headers;
const bearerTokenIdentifier = 'Bearer';
if (!authorization) {
throw new Error('Authorization header missing');
}
if (!authorization.startsWith(bearerTokenIdentifier)) {
throw new Error('Authorization token type not supported');
}
return authorization.slice(bearerTokenIdentifier.length + 1);
};
const requireAuth = (resource) => {
if (!resource) {
throw new Error('Resource parameter is required for authentication');
}
return async (req, res, next) => {
try {
// Token extrahieren
const token = getTokenFromHeader(req.headers);
const { payload } = await jwtVerify(
token,
createRemoteJWKSet(new URL(process.env.LOGTO_JWKS_URL)),
{
issuer: process.env.LOGTO_ISSUER,
audience: resource,
}
);
// Benutzerinfo zur Anfrage hinzufügen
req.user = {
id: payload.sub,
};
next();
} catch (error) {
console.error('Auth-Fehler:', error);
res.status(401).json({ error: 'Nicht autorisiert' });
}
};
};
module.exports = {
requireAuth,
};
Um dieses Middleware zu verwenden, benötigen wir diese Umgebungsvariablen:
- LOGTO_JWKS_URL
- LOGTO_ISSUER
Hole diese Variablen aus dem OpenID-Konfigurationsendpunkt deines Logto-Tenants. Besuche https://<your-tenant-id>.logto.app/oidc/.well-known/openid-configuration. Du findest die benötigten Informationen im zurückgegebenen JSON:
{
"jwks_uri": "<https://tenant-id.logto.app/oidc/jwks>",
"issuer": "<https://tenant-id.logto.app/oidc>"
}
Jetzt verwende das requireAuth-Middleware in unserem POST /organizations-Endpunkt.
app.post('/organizations', requireAuth('<https://api.documind.com>'), async (req, res) => {
// Logik zur Organisationserstellung
// ...
});
Damit ist unser POST /organizations-Endpunkt geschützt. Nur Benutzer mit gültigen Logto Zugangstokens können darauf zugreifen.
Wir können das Token jetzt im Frontend von Logto abrufen. Benutzer können mit diesem Token Organisationen über unseren Backend-Service erstellen. Das Middleware liefert uns auch die Benutzer-ID. Das hilft beim Hinzufügen von Benutzern zu Organisationen.
Im Frontend-Code deklariere diese API-Ressource im Logto-Config. Füge ihren Identifier zum resources-Array hinzu.
const config: LogtoConfig = {
endpoint: "<YOUR_LOGTO_ENDPOINT>",
appId: "<YOUR_LOGTO_APP_ID>",
scopes: [UserScope.Organizations],
resources: [ReservedResource.Organization, "<https://api.documind.com>"], // Neu erstellter API-Ressourcen-Identifier
};
Wie zuvor müssen sich Benutzer nach der Aktualisierung des Logto-Configs erneut anmelden.
Im Dashboard hole das Logto Zugangstoken, wenn eine Organisation erstellt wird. Verwende dieses Token, um auf unsere Backend-Service-API zuzugreifen.
// Zugangstoken für "DocuMind API" holen
const token = await getAccessToken('<https://api.documind.com>');
// Zugriff auf unsere Backend-Service-API mit dem Token
const response = await fetch('<http://localhost:3000/organizations>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
name: 'Organisation A',
description: 'Beschreibung Organisation A',
}),
});
Jetzt können wir korrekt auf die DocuMind Backend-Service-API zugreifen.
Logto Management API aufrufen
Implementieren wir die Organisationserstellung mit der Logto Management API.
Wie Frontend-Anfragen an den Backend-Service benötigen Backend-Service-Anfragen an Logto Zugangstokens.
In Logto verwenden wir Maschine-zu-Maschine-Authentifizierung für Zugangstokens. Siehe Interaktion mit Management API.
Gehe zur Anwendungsseite im Logto Console. Erstelle eine Maschine-zu-Maschine-Anwendung. Weise die Rolle "Logto Management API access" zu. Kopiere den Token-Endpunkt, App-ID und App-Secret. Wir verwenden diese für Zugangstokens.

Jetzt können wir Logto Management API Zugangstokens über diese M2M-Anwendung erhalten.
async function fetchLogtoManagementApiAccessToken() {
const response = await fetch(process.env.LOGTO_MANAGEMENT_API_TOKEN_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(
`${process.env.LOGTO_MANAGEMENT_API_APPLICATION_ID}:${process.env.LOGTO_MANAGEMENT_API_APPLICATION_SECRET}`
).toString('base64')}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
resource: process.env.LOGTO_MANAGEMENT_API_RESOURCE,
scope: 'all',
}).toString(),
});
const data = await response.json();
return data.access_token;
}
Verwende dieses Zugangstoken, um die Logto Management API aufzurufen.
Wir verwenden diese Management-APIs:
POST /api/organizations: Organisation erstellen (siehe: Create organization API reference)POST /api/organizations/{id}/users: Benutzer zur Organisation hinzufügen (siehe: Add users to organization API reference)
app.post('/organizations', requireAuth('<https://api.documind.com>'), async (req, res) => {
const accessToken = await fetchLogtoManagementApiAccessToken();
// Organisation in Logto erstellen und Benutzer hinzufügen
const response = await fetch(`${process.env.LOGTO_ENDPOINT}/api/organizations`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
name: req.body.name,
description: req.body.description,
}),
});
const createdOrganization = await response.json();
await fetch(`${process.env.LOGTO_ENDPOINT}/api/organizations/${createdOrganization.id}/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
userIds: [req.user.id],
}),
});
res.json({ data: createdOrganization });
});
Wir haben jetzt die Organisationserstellung über die Logto Management API implementiert. Wir können auch Benutzer zu Organisationen hinzufügen.
Teste diese Funktion im Dashboard.

und klicke auf „Organisation erstellen“

Erstellung erfolgreich!
Der nächste Schritt wäre, Benutzer zu einer Organisation einzuladen. Diese Funktion implementieren wir in unserem Tutorial noch nicht. Du weißt bereits, wie du die Management API verwendest. Du kannst dich an diesem Tenant-Erstellung und Einladung als Produktdesign-Referenz orientieren und diese Funktion leicht umsetzen, indem du diesem Blogpost folgst: Wie wir Benutzerzusammenarbeit in einer Multi-Tenant-App implementieren.
Zugangskontrolle für deine Multi-Tenant-App implementieren
Kommen wir nun zur Zugangskontrolle für Organisationen.
Wir wollen erreichen:
- Benutzer können nur auf Ressourcen zugreifen, die zu ihren eigenen Organisationen gehören: Das kann über Logtos
Organisationstokenerfolgen - Benutzer haben spezifische Rollen innerhalb von Organisationen (mit unterschiedlichen Berechtigungen), um autorisierte Aktionen auszuführen: Das kann über Logtos Organisationstemplate-Funktion umgesetzt werden
Schauen wir uns an, wie diese Funktionen implementiert werden.
Verwendung des Logto Organisationstokens
Ähnlich wie beim zuvor erwähnten Logto Zugangstoken stellt Logto ein Zugangstoken aus, das einer bestimmten Ressource entspricht, und Benutzer verwenden dieses Token, um auf geschützte Ressourcen im Backend-Service zuzugreifen. Entsprechend stellt Logto ein Organisationstoken aus, das einer bestimmten Organisation entspricht, und Benutzer verwenden dieses Token, um auf geschützte Organisationsressourcen im Backend-Service zuzugreifen.
In der Frontend-Anwendung können wir mit Logtos getOrganizationToken-Methode ein Token für den Zugriff auf eine bestimmte Organisation erhalten.
const { getOrganizationToken } = useLogto();
const organizationToken = await getOrganizationToken(organizationId);
Hier ist organizationId die ID der Organisation, der der Benutzer angehört.
Bevor du getOrganization oder andere Organisationsfunktionen verwendest, stelle sicher, dass der Scope urn:logto:scope:organizations und die Resource urn:logto:resource:organization im Logto-Config enthalten sind. Da wir dies bereits deklariert haben, wiederholen wir es nicht.
Auf unserer Organisationsseite verwenden wir das Organisationstoken, um Dokumente innerhalb der Organisation abzurufen.
function OrganizationPage() {
const { organizationId } = useParams();
const navigate = useNavigate();
const { signOut, getOrganizationToken } = useLogto();
const [error, setError] = useState<Error | null>(null);
const [documents, setDocuments] = useState([]);
const fetchDocuments = useCallback(async () => {
if (!organizationId) return;
try {
const organizationToken = await getOrganizationToken(organizationId);
const response = await fetch(`http://localhost:3000/documents`, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${organizationToken}`,
},
});
const documents = await response.json();
setDocuments(documents);
} catch (error: unknown) {
if (error instanceof Error) {
setError(error);
} else {
setError(new Error(String(error)));
}
}
},[getOrganizationToken, organizationId]);
useEffect(() => {
void fetchDocuments();
}, [fetchDocuments]);
if (error) {
return <div>Fehler: {error.message}</div>;
}
return <div>
<h1>Organisationsdokumente</h1>
<ul>
{documents.map((document) => (
<li key={document.id}>{document.name}</li>
))}
</ul>
</div>
}
Es gibt zwei wichtige Punkte in dieser Implementierung:
- Wenn die an
getOrganizationTokenübergebeneorganizationIdkeine Organisation ist, der der aktuelle Benutzer angehört, kann diese Methode kein Token erhalten. So wird sichergestellt, dass Benutzer nur auf ihre eigenen Organisationen zugreifen können. - Beim Anfordern von Organisationsressourcen verwenden wir das Organisationstoken anstelle des Zugangstokens, da wir für Ressourcen, die zu einer Organisation gehören, die Organisationsberechtigungssteuerung und nicht die Benutzerberechtigungssteuerung verwenden möchten (du wirst das besser verstehen, wenn wir später die API
GET /documentsimplementieren).
Als Nächstes erstellen wir eine API GET /documents in unserem Backend-Service. Ähnlich wie wir die API-Ressource zum Schutz der API POST /organizations verwenden, schützen wir die API GET /documents mit organisationsspezifischen Ressourcenindikatoren.
Erstelle zunächst ein requireOrganizationAccess-Middleware, um Organisationsressourcen zu schützen.
const getTokenFromHeader = (headers) => {
const { authorization } = headers;
const bearerTokenIdentifier = 'Bearer';
if (!authorization) {
throw new Error('Authorization header missing');
}
if (!authorization.startsWith(bearerTokenIdentifier)) {
throw new Error('Authorization token type not supported');
}
return authorization.slice(bearerTokenIdentifier.length + 1);
};
const extractOrganizationId = (aud) => {
if (!aud || typeof aud !== 'string' || !aud.startsWith('urn:logto:organization:')) {
throw new Error('Invalid organization token');
}
return aud.replace('urn:logto:organization:', '');
};
const decodeJwtPayload = (token) => {
try {
const [, payloadBase64] = token.split('.');
if (!payloadBase64) {
throw new Error('Invalid token format');
}
const payloadJson = Buffer.from(payloadBase64, 'base64').toString('utf-8');
return JSON.parse(payloadJson);
} catch (error) {
throw new Error('Failed to decode token payload');
}
};
const requireOrganizationAccess = () => {
return async (req, res, next) => {
try {
// Token extrahieren
const token = getTokenFromHeader(req.headers);
// Audience dynamisch aus dem Token holen
const { aud } = decodeJwtPayload(token);
if (!aud) {
throw new Error('Missing audience in token');
}
// Token mit Audience verifizieren
const { payload } = await jwtVerify(
token,
createRemoteJWKSet(new URL(process.env.LOGTO_JWKS_URL)),
{
issuer: process.env.LOGTO_ISSUER,
audience: aud,
}
);
// Organisations-ID aus dem Audience-Claim extrahieren
const organizationId = extractOrganizationId(payload.aud);
// Organisationsinfo zur Anfrage hinzufügen
req.user = {
id: payload.sub,
organizationId,
};
next();
} catch (error) {
console.error('Organisation Auth-Fehler:', error);
res.status(401).json({ error: 'Nicht autorisiert - Ungültiger Organisationszugriff' });
}
};
};
Dann verwende das requireOrganizationAccess-Middleware, um die API GET /documents zu schützen.
app.get('/documents', requireOrganizationAccess(), async (req, res) => {
// Du kannst die aktuelle Benutzer-ID und OrganisationId über req.user erhalten
console.log('userId', req.user.id);
console.log('organizationId', req.user.organizationId);
// Dokumente aus der Datenbank nach OrganisationId abrufen
// ....
const documents = await getDocumentsByOrganizationId(req.user.organizationId);
res.json(documents);
});
So haben wir die Nutzung von Organisationstokens zum Zugriff auf Organisationsressourcen implementiert. Im Backend-Service kannst du die entsprechenden Ressourcen aus der Datenbank anhand der Organisations-ID abrufen.
Manche Software erfordert eine Datenisolation zwischen Organisationen. Für weitere Diskussionen und Implementierungen siehe den Blogpost: Multi-Tenancy-Implementierung mit PostgreSQL: Lerne anhand eines einfachen Praxisbeispiels.
Organisationsebene rollenbasierte Zugangskontrolle implementieren
Wir haben die Nutzung von Organisationstokens zum Zugriff auf Organisationsressourcen implementiert. Als Nächstes implementieren wir die Benutzerberechtigungssteuerung innerhalb von Organisationen mit RBAC.
Nehmen wir an, DocuMind hat zwei Rollen: Admin und Mitarbeiter.
Admins können Dokumente erstellen und darauf zugreifen, während Mitarbeiter nur auf Dokumente zugreifen können.
Unsere Organisation benötigt also diese beiden Rollen: Admin und Mitarbeiter.
Admin hat sowohl die Berechtigungen read:documents als auch create:documents, während Mitarbeiter nur die Berechtigung read:documents haben.
- Admin
read:documentscreate:documents
- Mitarbeiter
read:documents
Hier kommt die Organisationstemplate-Funktion von Logto ins Spiel.
Ein Organisationstemplate ist eine Blaupause des Zugangskontrollmodells für jede Organisation: Es definiert die Rollen und Berechtigungen, die für alle Organisationen gelten.
Warum Organisationstemplate?
Weil Skalierbarkeit eine der wichtigsten Anforderungen für SaaS-Produkte ist. Mit anderen Worten: Was für einen Kunden funktioniert, sollte für alle Kunden funktionieren.
Gehe zu Logto Console > Organization Templates > Organization permissions und erstelle zwei Berechtigungen: read:documents und create:documents.

Gehe dann zum Tab Organisationsrollen, um zwei Benutzerrollen zu erstellen: Admin und Mitarbeiter, und weise diesen Rollen die entsprechenden Berechtigungen zu.

So haben wir ein RBAC-Berechtigungsmodell für jede Organisation erstellt.
Als Nächstes gehen wir auf unsere Organisationsdetailseite, um unseren Mitgliedern die passenden Rollen zuzuweisen.

Jetzt haben unsere Organisationsbenutzer Rollen! Du kannst diese Schritte auch über die Logto Management API durchführen:
// Weisen Sie dem Organisationsersteller die Rolle 'Admin' zu
app.post('/organizations', requireAuth('https://api.documind.com'), async (req, res) => {
const accessToken = await fetchLogtoManagementApiAccessToken();
// Organisation in Logto erstellen
// bestehender Code...
// Benutzer zur Organisation in Logto hinzufügen
await fetch(`${process.env.LOGTO_ENDPOINT}/api/organizations/${createdOrganization.id}/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
userIds: [req.user.id],
}),
});
// Weisen Sie dem ersten Benutzer die Rolle `Admin` zu.
const rolesResponse = await fetch(`${process.env.LOGTO_ENDPOINT}/api/organization-roles`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
});
const roles = await rolesResponse.json();
// Finde die Rolle `Admin`
const adminRole = roles.find((role) => role.name === 'Admin');
// Weisen Sie dem ersten Benutzer die Rolle `Admin` zu.
await fetch(
`${process.env.LOGTO_ENDPOINT}/api/organizations/${createdOrganization.id}/users/${req.user.id}/roles`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
organizationRoleIds: [adminRole.id],
}),
}
);
// bestehender Code...
});
Jetzt können wir die Benutzerberechtigungssteuerung durch Überprüfung ihrer Berechtigungen implementieren.
Im Code müssen wir dafür sorgen, dass das Organisationstoken des Benutzers Berechtigungsinformationen trägt, und diese Berechtigungen dann im Backend überprüfen.
Im Logto-Config des Frontends müssen wir die Berechtigungen deklarieren, die Benutzer innerhalb der Organisation anfordern sollen. Fügen wir read:documents und create:documents zu den scopes hinzu.
const config: LogtoConfig = {
endpoint: "<YOUR_LOGTO_ENDPOINT>",
appId: "<YOUR_LOGTO_APP_ID>",
scopes: [UserScope.Organizations, "read:documents", "create:documents"],
resources: [ReservedResource.Organization, "<https://api.documind.com>"], // Neu erstellter API-Ressourcen-Identifier
};
Wie üblich, melde dich erneut mit deinem Benutzer an, damit diese Konfigurationen wirksam werden.
Dann erweitern wir das requireOrganizationAccess-Middleware im Backend um die Überprüfung der Benutzerberechtigungen.
const hasRequiredScopes = (tokenScopes, requiredScopes) => {
if (!requiredScopes || requiredScopes.length === 0) {
return true;
}
const scopeSet = new Set(tokenScopes);
return requiredScopes.every((scope) => scopeSet.has(scope));
};
const requireOrganizationAccess = ({ requiredScopes = [] } = {}) => {
return async (req, res, next) => {
try {
//...
// Token mit Audience verifizieren
const { payload } = await jwtVerify(
token,
createRemoteJWKSet(new URL(process.env.LOGTO_JWKS_URL)),
{
issuer: process.env.LOGTO_ISSUER,
audience: aud,
}
);
//...
// Scopes aus dem Token holen
const scopes = payload.scope?.split(' ') || [];
// Erforderliche Scopes prüfen
if (!hasRequiredScopes(scopes, requiredScopes)) {
throw new Error('Unzureichende Berechtigungen');
}
//...
next();
} catch (error) {
//...
}
};
};
Erstelle dann eine API POST /documents und verwende das requireOrganizationAccess-Middleware mit requiredScopes-Konfiguration, um diese API und die vorherige API GET /documents zu schützen.
// API zum Erstellen von Dokumenten
app.post(
'/documents',
requireOrganizationAccess({ requiredScopes: ['create:documents'] }),
async (req, res) => {
//...
}
);
// API zum Abrufen von Dokumenten
app.get(
'/documents',
requireOrganizationAccess({ requiredScopes: ['read:documents'] }),
async (req, res) => {
//...
}
);
So haben wir die Benutzerberechtigungssteuerung durch Überprüfung der Benutzerberechtigungen implementiert.
Im Frontend kannst du die Berechtigungsinformationen des Benutzers durch Dekodieren des Organisationstokens oder durch Aufruf von Logtos getOrganizationTokenClaims-Methode erhalten.
const [scopes, setScopes] = useState([]);
const { getOrganizationTokenClaims } = useLogto();
const loadScopes = async () => {
const claims = await getOrganizationTokenClaims(organizationId);
setScopes(claims.scope.split(' '));
};
// ...
Steuere Seitenelemente basierend auf den Berechtigungen, indem du die Scopes in den Claims prüfst.
Weitere Multi-Tenant-App-Funktionen hinzufügen
Bisher haben wir die grundlegenden Benutzer- und Organisationsfunktionen in einem Multi-Tenant-SaaS-System implementiert! Es gibt jedoch noch einige Funktionen, die wir nicht behandelt haben, wie z. B. individuelles Branding der Anmeldeseite für jede Organisation, automatisches Hinzufügen von Benutzern mit bestimmten Domain-E-Mails zu bestimmten Organisationen und Integration von Enterprise-SSO-Funktionalität.
Dies sind alles Out-of-the-Box-Funktionen, und du findest weitere Informationen dazu in der Logto-Dokumentation:
- Enterprise SSO Integration
- Just-in-Time (JIT) Provisioning
- Organisationsebene Branding
- Organisationsebene Multi-Faktor-Authentifizierung (MFA)
- Organisationsebene Verwaltung
Zusammenfassung
Erinnerst du dich, wie überwältigend es am Anfang wirkte? Benutzer, Organisationen, Berechtigungen, Enterprise-Funktionen ... es schien wie ein endloser Berg.
Aber schau, was wir erreicht haben:
- Ein vollständiges Authentifizierungssystem mit mehreren Anmeldeoptionen und MFA-Unterstützung
- Ein flexibles Organisationssystem, das mehrere Mitgliedschaften unterstützt
- Rollenbasierte Zugangskontrolle innerhalb von Organisationen
Und das Beste? Wir mussten das Rad nicht neu erfinden. Durch die Nutzung moderner Tools wie Logto haben wir aus Monaten Entwicklungszeit Minuten gemacht.
Der vollständige Quellcode für dieses Tutorial ist verfügbar unter: Multi-Tenant-SaaS-Beispiel.
Das ist die Kraft moderner Entwicklung im Jahr 2025 – wir können uns auf den Bau einzigartiger Produktfunktionen konzentrieren, statt uns mit Infrastruktur herumzuschlagen. Jetzt bist du dran, etwas Großartiges zu bauen!
Entdecke alle Funktionen von Logto, von Logto Cloud bis Logto OSS, auf der Logto Website oder registriere dich noch heute bei Logto Cloud.