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.

18. August 2026 Christian Engel E-Mail schreiben In Teams chatten

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-js und 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
  • Eine eigene API-Route, falls du Angaben zum angemeldeten Nutzer brauchst
  • 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.

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, Land oder Berechtigungen aus dem JWT gelesen, müssen diese Daten künftig über das Backend bereitgestellt werden. Das Cookie ist HttpOnly und damit für JavaScript nicht zugänglich. Der Authorization-Header wird erst beim Auth-Gateway ergänzt.

Das Backend erhält den JWT weiterhin und kann die benötigten Claims über eine eigene Route bereitstellen:

// Backend
GET /api/me

{
  "name": "Erika Musterfrau",
  "country": "AT"
}

Das Frontend lädt diese Daten beim Start. Die dafür erforderliche Backend-Änderung muss bei der Planung berücksichtigt werden. Die Route sollte nur die tatsächlich benötigten Daten liefern und nicht den vollständigen JWT weiterreichen.

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.

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.

Checkliste

  • keycloak-js und 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 ersetzt durch eine eigene API-Route
  • 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