From 125537fd53d80803052d3780dae686c71e28431e Mon Sep 17 00:00:00 2001 From: erdemcaygor Date: Fri, 24 Oct 2025 16:26:08 +0300 Subject: [PATCH] token storage worker added --- .../services/memory-token-storage.service.ts | 107 +++++++++++++++--- .../src/lib/workers/token-storage.worker.ts | 55 +++++++++ 2 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 npm/ng-packs/packages/oauth/src/lib/workers/token-storage.worker.ts diff --git a/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts index 849f118de9..1d35cabff3 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts @@ -1,3 +1,4 @@ +// shared-worker-token-storage.service.ts import { inject, Injectable } from '@angular/core'; import { OAuthStorage } from 'angular-oauth2-oidc'; import { AbpLocalStorageService } from '@abp/ng.core'; @@ -6,34 +7,108 @@ import { AbpLocalStorageService } from '@abp/ng.core'; providedIn: 'root', }) export class MemoryTokenStorageService implements OAuthStorage { - private keysShouldStoreInMemory = ['access_token', 'id_token', 'expires_at', 'id_token_claims_obj', 'id_token_expires_at', 'id_token_stored_at', 'access_token_stored_at', 'abpOAuthClientId', 'granted_scopes']; - private data = new Map(); + private keysShouldStoreInMemory = [ + 'access_token', 'id_token', 'expires_at', + 'id_token_claims_obj', 'id_token_expires_at', + 'id_token_stored_at', 'access_token_stored_at', + 'abpOAuthClientId', 'granted_scopes' + ]; + + private worker?: any; + private port?: MessagePort; + private cache = new Map(); private localStorageService = inject(AbpLocalStorageService); + private useSharedWorker = false; + + constructor() { + this.initializeStorage(); + } + + private initializeStorage(): void { + // @ts-ignore + if (typeof SharedWorker !== 'undefined') { + try { + // @ts-ignore + this.worker = new SharedWorker( + new URL('../workers/token-storage.worker.ts', import.meta.url), + { type: 'module', name: 'oauth-token-storage' } + ); + + this.port = this.worker.port; + this.port.start(); + this.useSharedWorker = true; - getItem(key: string): string { - if (this.keysShouldStoreInMemory.includes(key)) { - return this.data.get(key) || null; + this.port.onmessage = (event) => { + const { action, key, value } = event.data; + + switch (action) { + case 'set': + this.cache.set(key, value); + break; + case 'remove': + this.cache.delete(key); + break; + case 'clear': + this.cache.clear(); + break; + } + }; + + console.log('✅ Using SharedWorker for cross-tab token storage'); + } catch (error) { + console.warn('⚠️ SharedWorker failed:', error); + this.useSharedWorker = false; + } + } else { + console.warn('⚠️ SharedWorker not supported'); + this.useSharedWorker = false; } - return this.localStorageService.getItem(key); } - removeItem(key: string): void { - if (this.keysShouldStoreInMemory.includes(key)) { - this.data.delete(key); + getItem(key: string): string | null { + if (!this.keysShouldStoreInMemory.includes(key)) { + return this.localStorageService.getItem(key); + } + +/* if (this.useSharedWorker) { + return this.cache.get(key) || null; + }*/ + + return this.cache.get(key) || null; + } + + setItem(key: string, value: string): void { + if (!this.keysShouldStoreInMemory.includes(key)) { + this.localStorageService.setItem(key, value); return; } - this.localStorageService.removeItem(key); + + if (this.useSharedWorker && this.port) { + // this.cache.set(key, value); + this.port.postMessage({ action: 'set', key, value }); + } else { + this.cache.set(key, value); + } } - setItem(key: string, data: string): void { - if (this.keysShouldStoreInMemory.includes(key)) { - this.data.set(key, data); + removeItem(key: string): void { + if (!this.keysShouldStoreInMemory.includes(key)) { + this.localStorageService.removeItem(key); return; } - this.localStorageService.setItem(key, data); + + if (this.useSharedWorker && this.port) { + // this.cache.delete(key); + this.port.postMessage({ action: 'remove', key }); + } else { + this.cache.delete(key); + } } - clear() { - this.data.clear(); + clear(): void { + if (this.useSharedWorker && this.port) { + this.port.postMessage({ action: 'clear' }); + } + this.cache.clear(); } } diff --git a/npm/ng-packs/packages/oauth/src/lib/workers/token-storage.worker.ts b/npm/ng-packs/packages/oauth/src/lib/workers/token-storage.worker.ts new file mode 100644 index 0000000000..19b3537875 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/workers/token-storage.worker.ts @@ -0,0 +1,55 @@ +/// +/// +/// + +declare const self: SharedWorkerGlobalScope; + +interface TokenMessage { + action: 'set' | 'remove' | 'clear' | 'get'; + key?: string; + value?: string; +} + +const tokenStore = new Map(); +const ports = new Set(); + +self.onconnect = (event: MessageEvent) => { + const port = event.ports[0]; + ports.add(port); + + port.onmessage = (e: MessageEvent) => { + const { action, key, value } = e.data; + switch (action) { + case 'set': + if (key && value !== undefined) { + tokenStore.set(key, value); + ports.forEach(p => p.postMessage({ action: 'set', key, value })); + } + break; + case 'remove': + if (key) { + tokenStore.delete(key); + ports.forEach(p => p.postMessage({ action: 'remove', key })); + } + break; + case 'clear': + tokenStore.clear(); + ports.forEach(p => p.postMessage({ action: 'clear' })); + break; + case 'get': + if (key) { + const value = tokenStore.get(key) ?? null; + port.postMessage({ action: 'get', key, value }); + } + break; + } + }; + + port.start(); + + tokenStore.forEach((value, key) => { + port.postMessage({ action: 'set', key, value }); + }); +}; + +export {};