Authentifizierung in einer Electron-App implementieren
Wer nach einer Möglichkeit sucht, Authentifizierung in eine Electron-App zu integrieren, stößt wahrscheinlich auf Tutorials, die ein BrowserWindow öffnen, um ein Login-Formular anzuzeigen, den OAuth-Callback innerhalb von Electron abfangen und Token im Klartext speichern. Dieser Ansatz ist veraltet, unsicher und widerspricht der Art und Weise, wie OAuth 2.0 für native Apps heute tatsächlich funktioniert.
Dieser Artikel erklärt die korrekte Architektur: Authorization Code Flow mit PKCE, Login über den Systembrowser und eine Deep-Link- oder Loopback-Weiterleitung, um Token an Ihre App zurückzugeben.
Wichtige Erkenntnisse
- Eingebettete
BrowserWindow-Login-Flows verstoßen gegen RFC 8252 und setzen Benutzer dem Risiko des Credential-Abfangens aus. Verwenden Sie stattdessen den Systembrowser. - PKCE (Proof Key for Code Exchange) ersetzt das Client-Secret, das Electron-Apps nicht sicher speichern können.
- Deep Linking mit einem benutzerdefinierten URI-Schema oder eine Loopback-Weiterleitung gibt den Autorisierungscode an den Hauptprozess zurück.
- Speichern Sie Refresh-Token mit
safeStorage, das OS-seitiges Schlüsselmanagement nutzt (Keychain, DPAPI, libsecret). - Isolieren Sie den Renderer-Prozess mit
contextIsolation: trueund stellen Sie übercontextBridgenur eng begrenzte IPC-Methoden bereit.
Warum sich Electron-Authentifizierung von Web-Authentifizierung unterscheidet
Electron-Apps laufen auf einer vom Benutzer kontrollierten Maschine. Es gibt kein Backend, in dem ein Client-Secret verborgen werden könnte, und es gibt keine Browser-Sandbox, die Ihre App vom Betriebssystem isoliert. Dies verändert das Bedrohungsmodell erheblich.
RFC 8252, der OAuth-2.0-Standard, der speziell für native und Desktop-Apps verfasst wurde, ist eindeutig: Verwenden Sie keine eingebetteten Web-Views für OAuth-Login-Flows. Ein eingebettetes BrowserWindow kann Anmeldedaten stillschweigend abfangen, und der Benutzer hat keine Möglichkeit zu überprüfen, ob er mit einem legitimen Identity-Provider kommuniziert. Verwenden Sie stattdessen den Systembrowser.
Die korrekte Architektur: OAuth 2.0 PKCE mit Deep Linking
Der empfohlene Ablauf sieht folgendermaßen aus:
- Der Benutzer klickt in Ihrer Electron-App auf „Anmelden”.
- Ihre App öffnet den Systembrowser mit einer Autorisierungs-URL.
- Der Benutzer authentifiziert sich bei Ihrem Identity-Provider (Auth0, Clerk, Firebase Authentication, AWS Cognito oder einem anderen OpenID-Connect-kompatiblen Anbieter).
- Der Provider leitet zu einem benutzerdefinierten URI-Schema wie
myapp://auth/callbackoder einer Loopback-Adresse wiehttp://127.0.0.1:PORT/callbackweiter. - Ihr Electron-Hauptprozess fängt diese Weiterleitung ab und tauscht den Autorisierungscode gegen Token aus.
- Token werden sicher mit
safeStoragegespeichert.
Da Electron-Apps ein Client-Secret nicht sicher aufbewahren können, ersetzt PKCE (Proof Key for Code Exchange) das Secret vollständig. Vor Beginn des Flows generiert Ihre App einen zufälligen code_verifier, hasht ihn zu einem code_challenge und sendet die Challenge mit der Autorisierungsanfrage. Beim Austausch des Codes gegen Token senden Sie den ursprünglichen code_verifier. Der Autorisierungsserver überprüft, ob sie übereinstimmen. Es verlässt niemals ein Secret Ihre App.
Deep Linking für den OAuth-Callback einrichten
Registrieren Sie einen benutzerdefinierten Protokoll-Handler in Ihrem Hauptprozess:
// main.js
const { app } = require('electron');
const path = require('path');
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('myapp', process.execPath, [
path.resolve(process.argv[1]),
]);
}
} else {
app.setAsDefaultProtocolClient('myapp');
}
// macOS: handle the deep link callback
app.on('open-url', (event, callbackUrl) => {
event.preventDefault();
handleAuthCallback(callbackUrl); // parse code, exchange for tokens
});
// Windows and Linux: deep links arrive via second-instance
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', (event, argv) => {
const callbackUrl = argv.find((arg) => arg.startsWith('myapp://'));
if (callbackUrl) handleAuthCallback(callbackUrl);
});
}
Unter Windows und Linux liefert das second-instance-Event den Deep Link als Kommandozeilenargument. Unter macOS ist open-url das korrekte Event. Für die Paketierung konfigurieren Sie Ihr Protokoll in electron-builder:
{
"protocols": {
"name": "myapp",
"schemes": ["myapp"]
}
}
Hinweis: Unter macOS und Linux funktioniert Deep Linking im Allgemeinen nur zuverlässig in paketierten Apps, da der Protokoll-Handler beim Betriebssystem registriert sein muss. Testen Sie Deep Linking stets gegen einen paketierten Build und nicht nur mit
electron ..
Discover how at OpenReplay.com.
Token sicher mit Electron safeStorage speichern
Speichern Sie Token niemals in localStorage, in Klartextdateien oder in electron-store ohne Verschlüsselung. Verwenden Sie safeStorage, das Daten mithilfe von OS-seitigem Schlüsselmanagement verschlüsselt (Keychain unter macOS, DPAPI unter Windows, libsecret unter Linux):
const { app, safeStorage } = require('electron');
const fs = require('fs');
const path = require('path');
const TOKEN_PATH = path.join(app.getPath('userData'), 'refresh.enc');
async function saveRefreshToken(token) {
if (!safeStorage.isEncryptionAvailable()) {
throw new Error('Encryption is not available on this system');
}
const encrypted = await safeStorage.encryptStringAsync(token);
fs.writeFileSync(TOKEN_PATH, encrypted);
}
async function loadRefreshToken() {
if (!fs.existsSync(TOKEN_PATH)) return null;
const encrypted = fs.readFileSync(TOKEN_PATH);
return await safeStorage.decryptStringAsync(encrypted);
}
Überprüfen Sie stets safeStorage.isEncryptionAvailable() vor der Verschlüsselung, insbesondere unter Linux, wo der OS-Keyring möglicherweise nicht konfiguriert ist. Auf einigen Linux-Systemen kann Electron auf ein basic_text-Backend zurückfallen, wenn kein sicherer Keyring verfügbar ist. Sie können das aktive Backend mit safeStorage.getSelectedStorageBackend() abfragen und Benutzer entsprechend informieren.
Den Renderer-Prozess isoliert halten
Ihr Renderer-Prozess sollte Token niemals direkt verarbeiten. Verwenden Sie contextIsolation, nodeIntegration: false und stellen Sie dem Renderer über eine eng begrenzte contextBridge nur das Nötigste zur Verfügung:
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('auth', {
login: () => ipcRenderer.invoke('auth:login'),
logout: () => ipcRenderer.send('auth:logout'),
getProfile: () => ipcRenderer.invoke('auth:get-profile'),
});
Der Hauptprozess übernimmt alle Token-Operationen. Der Renderer ruft ausschließlich benannte IPC-Methoden auf. Dies begrenzt den Schadensradius, falls ein Renderer jemals durch schädliche Inhalte kompromittiert werden sollte.
Einen Identity-Provider auswählen
Die oben beschriebene Architektur funktioniert mit jedem OpenID-Connect-kompatiblen Provider. Beliebte Optionen für Electron-Apps sind Auth0, Clerk, Firebase Authentication und AWS Cognito. Registrieren Sie Ihre App beim gewählten Provider als Anwendungstyp Native oder Desktop und konfigurieren Sie Ihr benutzerdefiniertes URI-Schema als zulässige Redirect-URI.
Das zu vermeidende Muster
Viele Tutorials zeigen nach wie vor dieses Muster:
// ❌ Do not do this
const authWindow = new BrowserWindow({ /* ... */ });
authWindow.loadURL(authorizationUrl);
authWindow.webContents.session.webRequest.onBeforeRequest(
{ urls: ['http://localhost/callback*'] },
({ url }) => { /* intercept tokens */ }
);
Dieses Muster bettet den Login-Flow in Electron ein, verstößt gegen RFC 8252 und nimmt dem Benutzer die Möglichkeit, den Identity-Provider zu verifizieren. Vermeiden Sie es, unabhängig davon, wie viele Tutorials es noch empfehlen.
Fazit
Der richtige Weg, Authentifizierung in eine Electron-App zu integrieren, ist unkompliziert: Öffnen Sie den Systembrowser, verwenden Sie OAuth 2.0 mit PKCE, verarbeiten Sie den Callback über Deep Linking oder eine Loopback-Weiterleitung und speichern Sie Token mit safeStorage. Halten Sie den Renderer-Prozess von jeglicher Token-Logik isoliert. Diese Architektur entspricht den Empfehlungen von RFC 8252 und ist das, was moderne Provider wie Auth0 und Clerk erwarten, wenn Sie eine native Anwendung registrieren.
Häufig gestellte Fragen
Ja. Eine Loopback-Weiterleitung nutzt einen lokalen HTTP-Server auf 127.0.0.1 mit einem zufälligen Port, um den OAuth-Callback zu empfangen. Sie vermeidet die OS-seitige Protokollregistrierung und eignet sich gut für die Entwicklung. Der Nachteil ist, dass Sie den Server für jeden Login-Vorgang starten und stoppen müssen, und einige Provider erfordern eine explizite Konfiguration von Loopback-URIs in ihrem Dashboard.
Ja. Auch wenn Ihr Provider ein Client-Secret ausstellt, sollten Sie es nicht in eine Electron-Binary einbetten, da Benutzer es extrahieren können. PKCE schützt den Autorisierungscode-Austausch unabhängig davon, ob ein Secret vorhanden ist. Registrieren Sie Ihre Anwendung als nativen oder öffentlichen Client, damit PKCE erforderlich ist und kein Secret erwartet wird.
Speichern Sie ausschließlich den Refresh-Token mit safeStorage. Wenn der Access-Token abläuft, sendet der Hauptprozess den Refresh-Token an den Token-Endpunkt des Providers und erhält einen neuen Access-Token. Halten Sie Access-Token im Arbeitsspeicher des Hauptprozesses vor, niemals im Renderer. Falls der Refresh-Token widerrufen oder abgelaufen ist, fordern Sie den Benutzer auf, sich erneut über den Systembrowser anzumelden.
Benutzerdefinierte Protokoll-Handler müssen beim Betriebssystem registriert werden, und diese Registrierung erfolgt in der Regel bei der Installation oder Paketierung der App. Das Ausführen von electron . startet die Electron-Binary direkt, sodass das Betriebssystem nicht weiß, dass Ihre App das Schema myapp:// verarbeitet. Testen Sie Deep Linking stets gegen einen paketierten Build, der mit electron-builder oder einem ähnlichen Tool erstellt wurde.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.