Authentifizierung im Frontend umstellen
Was du in deiner Angular- oder React-App ändern musst, wenn die Authentifizierung statt über keycloak-js jetzt über das Portal-Cookie läuft.
Dieser Guide beschreibt die technischen Änderungen im Frontend während Phase 3. Der übergeordnete Ablauf und die Voraussetzungen für das Deployment sind unter Portal-Integration in den Helm Values deklarieren beschrieben.
Zielzustand
Bisher hat das Frontend einen JWT selbst beschafft oder vom Host erhalten, an Backend-Requests angehängt und gegebenenfalls an eingebettete Microfrontends weitergegeben.
Im Zielzustand verarbeitet das Frontend keinen JWT mehr. Die Authentifizierung erfolgt über das Cookie der Portal-Domain. Bei Requests, die über das Auth-Gateway an ein Backend geleitet werden, ergänzt das Gateway den Authorization-Header.
Was konkret wegfällt:
keycloak-jsund dessen Initialisierung- Code, der ein Token vom Host empfängt
- Code, der ein Token explizit an Backend-Requests anhängt (Interceptoren, Fetch-Wrapper)
- Code, der Informationen aus dem JWT ausliest
- Hardcodierte oder env-basierte Backend-URLs
Was neu hinzukommt:
- Jeder Backend-Request muss das Portal-Cookie mitschicken
- Angaben zum angemeldeten Nutzer werden mit
getSessionInfo()im Frontend abgerufen - Backend-URLs werden zur Laufzeit aus der Portal-Domain abgeleitet
Token-Verarbeitung entfernen
keycloak-js
Falls dein Frontend keycloak-js selbst initialisiert:
import Keycloak from 'keycloak-js';
const keycloak = new Keycloak({ url: '...', realm: '...', clientId: '...' });
await keycloak.init({ onLoad: 'login-required' }); Die Initialisierung entfällt vollständig. Beim Laden des Microfrontends besteht bereits eine Sitzung im Portal.
Angular: Entferne KeycloakService und KeycloakAngularModule aus dem AppModule sowie die Verwendung von KeycloakService in Services und Guards.
React: Entferne den ReactKeycloakProvider und alle useKeycloak()-Aufrufe.
Token vom Host empfangen
Entferne die Übernahme eines JWT per postMessage, Attribut oder einer anderen Schnittstelle des Hosts. Im Zielzustand empfängt und verteilt das Microfrontend keinen JWT mehr.
Authorization-Header entfernen
Angular – HttpInterceptor:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<unknown>, next: HttpHandler) {
const cloned = req.clone({
headers: req.headers.set('Authorization', `Bearer ${this.token}`)
});
return next.handle(cloned);
}
} Entferne den Interceptor aus HTTP_INTERCEPTORS in deinem AppModule oder provideHttpClient()-Aufruf.
React – fetch / axios:
fetch(url, {
headers: { Authorization: `Bearer ${token}` }
}); Entferne den explizit gesetzten Authorization-Header. Das Auth-Gateway ergänzt ihn beim Weiterleiten des Requests an das Backend.
Cookie mitschicken
Das Frontend läuft im Portal beispielsweise unter mein-mfe.<portal-domain>, das Backend unter mein-backend.<portal-domain>. Für den Browser sind das unterschiedliche Origins. Damit das Cookie bei einem Cross-Origin-Request mitgesendet wird, muss der Request Credentials einschließen.
Fehlt diese Angabe, erreicht der Request das Gateway ohne Session-Cookie und das Backend antwortet mit Status 401.
Das gilt für jeden Request an ein Backend, nicht nur für den ersten:
// fetch
fetch(url, { credentials: 'include' });
// axios
axios.get(url, { withCredentials: true }); // Angular – einmalig global:
provideHttpClient(withInterceptors([(req, next) => next(req.clone({ withCredentials: true }))])); Konfiguriere withCredentials in Angular zentral, damit es für alle Backend-Requests gilt.
Angaben zum Nutzer
Hat das Frontend bisher Informationen wie Name oder Land aus dem JWT gelesen, rufe diese künftig mit getSessionInfo() aus @dvag/mfe-toolkit ab. Erstelle dafür keine eigene Backend-Route. Dem Backend liegen diese Informationen nicht vor.
import { getSessionInfo } from '@dvag/mfe-toolkit/url';
const { name, firstName, lastName, land } = await getSessionInfo(); Die Funktion ist ab Version 0.4.0 verfügbar und lädt die Session-Informationen über die Portal-Domain. Das zugrunde liegende Cookie bleibt HttpOnly; das Frontend verarbeitet weiterhin keinen JWT.
Backend-URLs zur Laufzeit ableiten
Das Backend läuft im Portal unter <name>.<portal-domain>. Da die Domain vom jeweiligen Portal abhängt, kann sie nicht zur Build-Zeit festgelegt werden.
Das Paket @dvag/mfe-toolkit aus dvag/frontend-boilerplate stellt dafür getPortalUrl bereit:
import { getPortalUrl } from '@dvag/mfe-toolkit/url';
const apiBase = getPortalUrl('mein-backend'); Der Parameter ist der name aus dem portalIntegration-Block deines Backends in den Helm Values:
portalIntegration:
name: mein-backend
type: backend Verwende diese Funktion im Portal anstelle buildzeitabhängiger API-URLs wie process.env.API_URL oder environment.apiUrl. Das Toolkit leitet die vollständige URL aus der aktuellen window.location ab.
Lokal entwickeln ohne Portal
Für die lokale Entwicklung übernimmt @dvag/dev-auth-proxy die Aufgabe des Auth-Gateways. Der Proxy führt den Keycloak-Login im Browser durch, aktualisiert den Access-Token und leitet Requests mit Authorization-Header an das Backend weiter. Das Frontend bleibt dabei tokenfrei.
Lege im Root-Verzeichnis deines Frontends eine dev-auth-proxy.json an:
{
"entwicklung": {
"keycloakClientId": "mein-keycloak-client",
"upstreamTarget": "https://mein-backend.entwicklung.dvag/"
}
} Starte zuerst den Proxy:
npx @dvag/dev-auth-proxy entwicklung Der Proxy verwendet Port 4200 für den Login-Callback und gibt ihn nach dem Login wieder frei. Starte den Frontend-Dev-Server deshalb erst nach Abschluss des Logins. In der lokalen Konfiguration gehen Backend-Requests an http://localhost:8080; im Portal wird weiterhin die mit getPortalUrl() abgeleitete URL verwendet.
Auch lokal müssen Requests credentials: 'include' beziehungsweise withCredentials: true verwenden. Fehlt das beim Login gesetzte HttpOnly-Cookie, antwortet der Proxy mit Status 400. Damit entspricht der relevante Teil des lokalen Request-Flows dem Verhalten im Portal.
getSessionInfo() verwendet auf localhost automatisch die /session-info-Route des Dev-Auth-Proxys auf Port 8079.
Für mehrere Backends kann upstreamTarget als Liste konfiguriert werden. Die Ziele sind in derselben Reihenfolge über die Ports 8080, 8081 und folgende erreichbar.
E2E-Tests mit Playwright
E2E-Tests dürfen den JWT künftig ebenfalls nicht selbst beschaffen oder als Authorization-Header an Requests anhängen:
formData.append('client_id', config.AuthentificationClientID);
formData.append('username', vbnummer);
formData.append('password', vbkennwort);
formData.append('grant_type', 'password');
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${access_token}`
}; Stattdessen startet createAuthProxy den Auth-Proxy im globalSetup. Playwright öffnet die von createAuthProxy bereitgestellte startUrl und führt den Keycloak-Login mit den vorhandenen Page Objects aus. Der dabei gespeicherte storageState enthält das Cookie für localhost und wird für Browser- und request-Kontexte konfiguriert:
// playwright.config.ts
export default defineConfig<TestOptions>({
globalSetup: './global-setup.ts',
use: {
storageState: STATE.VB_PWTESTUSER_AUTH
}
}); Der Proxy muss während des gesamten Testlaufs erreichbar bleiben und anschließend beendet werden. API-Requests verwenden die lokale Proxy-URL und setzen keinen Authorization-Header:
const url = `http://localhost:8080/kunden/${kundennummer}/ausgaben`;
const headers = { 'Content-Type': 'application/json' };
const response = await this.requestSender.sendRequest('POST', url, headers, body); Fehlt das Cookie, antwortet der Proxy mit Status 400.
Confidential Client in GitHub Actions
Für Tests gegen Umgebungen, in denen der Public Client localhost nicht als Redirect-URI verwenden darf, unterstützt @dvag/dev-auth-proxy ab Version 1.3.0 einen Confidential Client. Der Login läuft weiterhin als Authorization Code Flow mit PKCE über den Testnutzer. Der Proxy authentifiziert sich beim Code-Tausch und bei jedem Token-Refresh zusätzlich mit dem Client-Secret. Es handelt sich nicht um einen Client-Credentials-Flow.
Hinterlege das Client-Secret als GitHub Actions Secret und stelle es nur dem Playwright-Schritt als Umgebungsvariable bereit:
- name: Run Playwright tests
run: npx playwright test
env:
DEV_AUTH_PROXY_CLIENT_SECRET: ${{ secrets.DEV_AUTH_PROXY_CLIENT_SECRET }} Lies das Secret im globalSetup ein und übergib es an createAuthProxy:
import { createAuthProxy } from '@dvag/dev-auth-proxy';
const clientSecret = process.env.DEV_AUTH_PROXY_CLIENT_SECRET;
if (!clientSecret) {
throw new Error('DEV_AUTH_PROXY_CLIENT_SECRET is required');
}
const proxy = await createAuthProxy({
environment: 'abnahme',
clientId: 'mein-confidential-client',
clientSecret,
upstreamTarget: 'https://mein-backend.abnahme.dvag/'
}); Beim Confidential Client muss http://localhost:<callback-port> als gültige Redirect-URI hinterlegt sein. Proxy und Playwright-Browser müssen auf demselben GitHub Actions Runner beziehungsweise im selben Netzwerk-Namespace laufen.
Das Secret gehört nicht in dev-auth-proxy.json, den Quellcode, CLI-Argumente, Logs oder Testartefakte. Ohne clientSecret verwendet der Proxy weiterhin den bisherigen Public-Client-Flow, beispielsweise für Entwicklung und Integration.
Checkliste
-
keycloak-jsund dessen Initialisierung entfernt - Token-Empfang vom Host-Frame entfernt
-
Authorization-Header-Interceptor / -Wrapper entfernt - Alle Backend-Requests verwenden
credentials: 'include'/withCredentials: true - Zugriffe auf JWT-Claims im Frontend durch
getSessionInfo()ersetzt; keine eigene Backend-Route erstellt - Backend-URLs werden zur Laufzeit abgeleitet, nicht mehr hardcodiert
- Lokale Entwicklung verwendet
@dvag/dev-auth-proxy; das Frontend enthält auch lokal kein Token-Handling - E2E-Tests verwenden den Auth-Proxy und beschaffen keinen JWT selbst
- Falls E2E-Tests einen Confidential Client verwenden:
@dvag/dev-auth-proxyist mindestens Version1.3.0und das Client-Secret kommt aus der CI-Secret-Verwaltung