Wenn der OpenId-Endpunkt nicht erreichbar ist – Progressive Web App (PWA) mit Blazor WebAssembly (WASM)

In diesem PAR-Statement geht es um eine Anwendung, die für die Authentisierung von Anwendern das Backend for Frontend – Pattern (BFF) nutzt. In der Entwicklungsumgebung funktioniert die Anmeldung. Sobald die Anwendung auf die Testumgebung deployed wurde, wurden die Endpunkte zur Anmeldung an den Identity Provider nicht gefunden.

Problem

Die Authentisierung funktioniert nicht, sobald die Anwendung im Release-Modus veröffentlicht wird. Statt ein Anmeldedialog kommt die Blazor-Standard-Fehlermeldung.

Blazor-Standard-Fehlermeldung

Aktion

Die Analyse grenzte das Problem auf die Datei service-worker.js der Anwendung ein. Im Debug-Modus hat der Serviceworker keinen weiteren Inhalt. Es sieht wie folgt aus:

self.addEventListener('fetch', () => { });

Dadurch hat die App im Entwicklungsmodus keinen Offline-Support. Sobald diese im Release-Modus veröffentlicht wird, greift die Logik der Datei service-worker.published.js. Diese unterstützt Caching, der bei den Endpunkten für die BFF-Endpunkte unerwünschte Auswirkungen hat. Da diese Endpunkte nicht im Cache sind, meldet die PWA das keine Seite gefunden wurde. Die Endpunkte befinden sich jedoch auf dem Server und sollen nicht aus dem Cache der PWA bedient werden. 

Damit die Anmeldung korrekt funktioniert muss die Datei service-worker.published.js angepasst werden.

In der Datei steht eine Methode namens onFetch zur Verfügung:

async function onFetch(event) {
    let cachedResponse = null;
    if (event.request.method === 'GET') {
        // For all navigation requests, try to serve index.html from cache
        // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
        const shouldServeIndexHtml = event.request.mode === 'navigate';

        const request = shouldServeIndexHtml ? 'index.html' : event.request;
        const cache = await caches.open(cacheName);
        cachedResponse = await cache.match(request);
    }

    return cachedResponse || fetch(event.request);
}	

In dieser muss shouldServeIndexHtml erweitert werden, sodass der Service-Worker für die Endpunkte den Server kontaktiert.

async function onFetch(event) {
    let cachedResponse = null;
    if (event.request.method === 'GET') {
        // For all navigation requests, try to serve index.html from cache
        // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
        const shouldServeIndexHtml = event.request.mode === 'navigate'
            && !event.request.url.includes('/signin-oidc')
            && !event.request.url.includes('/signout-callback-oidc')
            && !event.request.url.includes('/bff/');

        const request = shouldServeIndexHtml ? 'index.html' : event.request;
        const cache = await caches.open(cacheName);
        cachedResponse = await cache.match(request);
    }

    return cachedResponse || fetch(event.request);
}

Resultat

Nach dem erneuten Publizieren der Anwendung und bereinigen des Cache leitet der Service-Worker die Anfrage an den Server weiter und der Benutzer kann sich beim Identity Provider authentisieren.

Auf dem Bild sieht man die Login-Seite, die erfolgreich geladen wurde.

Einen guten Einstieg in das Backend for Frontend (BFF) – Pattern mit historischen Hintergrund-Informationen zeigt Dominick Baier im NDC-Talk.