Merge branch 'release/16.4' into v16/dev

This commit is contained in:
Jacob Overgaard
2025-11-04 14:07:30 +01:00
2 changed files with 45 additions and 7 deletions

View File

@@ -77,6 +77,41 @@ export class RedirectRequestHandler extends AuthorizationRequestHandler {
}); });
} }
/**
* Cleanup all stale authorization requests and configurations from storage.
* This scans localStorage for any keys matching the appauth patterns and removes them,
* including the authorization request handle key.
*/
public cleanupStaleAuthorizationData(): Promise<void> {
// Check if we're in a browser environment with localStorage
if (typeof window === 'undefined' || !window.localStorage) {
return Promise.resolve();
}
const keysToRemove: string[] = [];
// Scan localStorage for all appauth-related keys
for (let i = 0; i < window.localStorage.length; i++) {
const key = window.localStorage.key(i);
if (
key &&
(key.includes('_appauth_authorization_request') ||
key.includes('_appauth_authorization_service_configuration') ||
key === AUTHORIZATION_REQUEST_HANDLE_KEY)
) {
keysToRemove.push(key);
}
}
// Remove all found stale keys
const removePromises = keysToRemove.map((key) => this.storageBackend.removeItem(key));
return Promise.all(removePromises).then(() => {
if (keysToRemove.length > 0) {
log(`Cleaned up ${keysToRemove.length} stale authorization data entries`);
}
});
}
/** /**
* Attempts to introspect the contents of storage backend and completes the * Attempts to introspect the contents of storage backend and completes the
* request. * request.
@@ -119,12 +154,8 @@ export class RedirectRequestHandler extends AuthorizationRequestHandler {
} else { } else {
authorizationResponse = new AuthorizationResponse({ code: code, state: state }); authorizationResponse = new AuthorizationResponse({ code: code, state: state });
} }
// cleanup state // cleanup all authorization data including current and stale entries
return Promise.all([ return this.cleanupStaleAuthorizationData().then(() => {
this.storageBackend.removeItem(AUTHORIZATION_REQUEST_HANDLE_KEY),
this.storageBackend.removeItem(authorizationRequestKey(handle)),
this.storageBackend.removeItem(authorizationServiceConfigurationKey(handle)),
]).then(() => {
log('Delivering authorization response'); log('Delivering authorization response');
return { return {
request: request, request: request,
@@ -134,7 +165,10 @@ export class RedirectRequestHandler extends AuthorizationRequestHandler {
}); });
} else { } else {
log('Mismatched request (state and request_uri) dont match.'); log('Mismatched request (state and request_uri) dont match.');
return Promise.resolve(null); // cleanup all authorization data even on mismatch to prevent stale PKCE data
return this.cleanupStaleAuthorizationData().then(() => {
return null;
});
} }
}) })
); );

View File

@@ -247,6 +247,10 @@ export class UmbAuthFlow {
// clear the internal state // clear the internal state
this.#tokenResponse.setValue(undefined); this.#tokenResponse.setValue(undefined);
// Also cleanup any OAuth/PKCE artifacts that may still be in localStorage
// This is a defense-in-depth measure during logout
await this.#authorizationHandler.cleanupStaleAuthorizationData();
} }
/** /**