diff --git a/projects/auth-js/oidc/oidc-auth-manager.ts b/projects/auth-js/oidc/oidc-auth-manager.ts index 98eaf1b..6d198db 100644 --- a/projects/auth-js/oidc/oidc-auth-manager.ts +++ b/projects/auth-js/oidc/oidc-auth-manager.ts @@ -38,41 +38,41 @@ const DEFAULT_SETTINGS: Optional }; export class OIDCAuthManager extends AuthManager { - private idTokenSubs = new AuthSubscriptions<[string | undefined]>(); - private accessTokenSubs = new AuthSubscriptions<[string | undefined]>(); - private userProfileSubs = new AuthSubscriptions<[UserProfile | undefined]>(); - private userSessionSubs = new AuthSubscriptions<[UserSession | undefined]>(); - private authenticatedSubs = new AuthSubscriptions<[boolean]>(); - private renewingSubs = new AuthSubscriptions<[boolean]>(); - private redirectSubs = new AuthSubscriptions<[URL]>(); - private userManagerSubs: (() => void)[] = []; - - private _idToken?: string; - private _accessToken?: string; - private _userProfile?: UserProfile; - private _userSession?: UserSession; - private _isAuthenticated = false; - private _isRenewing = false; - - private userManager?: OidcUserManager; - private settings = DEFAULT_SETTINGS as OIDCAuthSettings; - - private _user?: User | null; + #idTokenSubs = new AuthSubscriptions<[string | undefined]>(); + #accessTokenSubs = new AuthSubscriptions<[string | undefined]>(); + #userProfileSubs = new AuthSubscriptions<[UserProfile | undefined]>(); + #userSessionSubs = new AuthSubscriptions<[UserSession | undefined]>(); + #authenticatedSubs = new AuthSubscriptions<[boolean]>(); + #renewingSubs = new AuthSubscriptions<[boolean]>(); + #redirectSubs = new AuthSubscriptions<[URL]>(); + #userManagerSubs: (() => void)[] = []; + + #idToken?: string; + #accessToken?: string; + #userProfile?: UserProfile; + #userSession?: UserSession; + #isAuthenticated = false; + #isRenewing = false; + + #userManager?: OidcUserManager; + #settings = DEFAULT_SETTINGS as OIDCAuthSettings; + + #user?: User | null; private set user(value: User | null | undefined) { - if (this._user !== value) { - this._user = value; - - this._idToken = (value) ? value.id_token : undefined; - this._accessToken = (value) ? value.access_token : undefined; - this._userProfile = (value?.profile) ? value.profile : undefined; - this._userSession = (value) ? UserSession.deserialize(value) : undefined; - this._isAuthenticated = !!(value && !value.expired); - - this.idTokenSubs.notify(this._idToken); - this.accessTokenSubs.notify(this._accessToken); - this.userProfileSubs.notify(this._userProfile); - this.userSessionSubs.notify(this._userSession); - this.authenticatedSubs.notify(this._isAuthenticated); + if (this.#user !== value) { + this.#user = value; + + this.#idToken = (value) ? value.id_token : undefined; + this.#accessToken = (value) ? value.access_token : undefined; + this.#userProfile = (value?.profile) ? value.profile : undefined; + this.#userSession = (value) ? UserSession.deserialize(value) : undefined; + this.#isAuthenticated = !!(value && !value.expired); + + this.#idTokenSubs.notify(this.#idToken); + this.#accessTokenSubs.notify(this.#accessToken); + this.#userProfileSubs.notify(this.#userProfile); + this.#userSessionSubs.notify(this.#userSession); + this.#authenticatedSubs.notify(this.#isAuthenticated); } } @@ -90,7 +90,7 @@ export class OIDCAuthManager extends AuthManager { const baseUrl = (isNativeMobile) ? `${userSettings.mobileScheme}://localhost/` : AuthUtils.getBaseUrl(); // Initialize settings - this.settings = merge({}, DEFAULT_SETTINGS, { + this.#settings = merge({}, DEFAULT_SETTINGS, { internal: { userStore: new WebStorageStateStore({ store: (isNativeMobile) ? new MobileStorage() : new InMemoryWebStorage() @@ -104,57 +104,57 @@ export class OIDCAuthManager extends AuthManager { }, userSettings); // Configure the user manager - this.userManager = new OidcUserManager(this.settings); + this.#userManager = new OidcUserManager(this.#settings); // Listen for events - this.userManagerSubs.push( - this.userManager.events.addUserLoaded(user => { + this.#userManagerSubs.push( + this.#userManager.events.addUserLoaded(user => { this.user = user; }), - this.userManager.events.addUserUnloaded(() => { - if (this.user) { + this.#userManager.events.addUserUnloaded(() => { + if (this.#user) { this.user = null; // If user is kicked out for any reason -> reload the app if login is required - if (this.settings.loginRequired) { + if (this.#settings.loginRequired) { location.reload(); } } }), - this.userManager.events.addSilentRenewError(async () => { - await this.removeUser(); + this.#userManager.events.addSilentRenewError(async () => { + await this.#removeUser(); }) ); // Make sure we are not trapped in the inception loop - this.assertNotInInceptionLoop(); + this.#assertNotInInceptionLoop(); // Decide what to do.. - if (AuthUtils.isUrlMatching(location.href, this.settings.internal?.redirect_uri)) { + if (AuthUtils.isUrlMatching(location.href, this.#settings.internal?.redirect_uri)) { // Back from signin redirect - await this.runSyncOrAsync(async () => { + await this.#runSyncOrAsync(async () => { const redirectUrl = sessionStorage.getItem(REDIRECT_URL_KEY); - await this.callSignin(() => this.userManager!.signinRedirectCallback(location.href), redirectUrl); + await this.#callSignin(() => this.#userManager!.signinRedirectCallback(location.href), redirectUrl); sessionStorage.removeItem(REDIRECT_URL_KEY); }); - } else if (AuthUtils.isUrlMatching(location.href, this.settings.internal?.post_logout_redirect_uri)) { + } else if (AuthUtils.isUrlMatching(location.href, this.#settings.internal?.post_logout_redirect_uri)) { // Back from signout redirect - await this.runSyncOrAsync(async () => { + await this.#runSyncOrAsync(async () => { const redirectUrl = sessionStorage.getItem(REDIRECT_URL_KEY); - await this.callSignout(() => this.userManager!.signoutRedirectCallback(location.href), redirectUrl); + await this.#callSignout(() => this.#userManager!.signoutRedirectCallback(location.href), redirectUrl); sessionStorage.removeItem(REDIRECT_URL_KEY); }); - } else if (this.settings.retrieveUserSession || this.settings.loginRequired) { + } else if (this.#settings.retrieveUserSession || this.#settings.loginRequired) { const signinSilent = async (): Promise => { - await this.runSyncOrAsync(() => this.signinSilent() + await this.#runSyncOrAsync(() => this.#signinSilent() .catch(async (signinSilentError: ErrorResponse) => { const { error, message } = signinSilentError; // Ex: login_required, consent_required, interaction_required, account_selection_required - if (this.settings.loginRequired && (error?.includes('_required') || message?.includes('_required'))) { + if (this.#settings.loginRequired && (error?.includes('_required') || message?.includes('_required'))) { await this.login(); } else { console.error('[OIDCAuthManager] User\'s session cannot be retrieved:', signinSilentError.message); - this.authenticatedSubs.notify(false); - if (this.settings.loginRequired) { + this.#authenticatedSubs.notify(false); + if (this.#settings.loginRequired) { throw signinSilentError; } } @@ -162,15 +162,15 @@ export class OIDCAuthManager extends AuthManager { }; // Try to load user from storage - const user = await this.userManager?.getUser(); + const user = await this.#userManager?.getUser(); if (!user || user.expired) { // on desktop -> try a silent renew with iframe - if (!isNativeMobile && this.settings.retrieveUserSession) { + if (!isNativeMobile && this.#settings.retrieveUserSession) { await signinSilent(); - // else -> force login if required - } else if (this.settings.loginRequired) { + // else -> force login if required + } else if (this.#settings.loginRequired) { await this.login(); - // else -> gracefully notify that we are not authenticated + // else -> gracefully notify that we are not authenticated } else { this.user = null; } @@ -185,16 +185,16 @@ export class OIDCAuthManager extends AuthManager { public async logout(args?: LogoutArgs): Promise { const redirectUrl = args?.redirectUrl ?? location.href; if (AuthUtils.isNativeMobile()) { - await this.callSignout(() => this.userManager!.signoutMobile(args), redirectUrl); + await this.#callSignout(() => this.#userManager!.signoutMobile(args), redirectUrl); } else { - switch (args?.desktopNavigationType ?? this.settings.desktopNavigationType) { + switch (args?.desktopNavigationType ?? this.#settings.desktopNavigationType) { case DesktopNavigation.POPUP: - await this.callSignout(() => this.userManager!.signoutPopup(args), redirectUrl); + await this.#callSignout(() => this.#userManager!.signoutPopup(args), redirectUrl); break; case DesktopNavigation.REDIRECT: default: sessionStorage.setItem(REDIRECT_URL_KEY, redirectUrl); - await this.userManager?.signoutRedirect(args); + await this.#userManager?.signoutRedirect(args); break; } } @@ -203,110 +203,110 @@ export class OIDCAuthManager extends AuthManager { public async login(args?: LoginArgs): Promise { const redirectUrl = args?.redirectUrl ?? location.href; if (AuthUtils.isNativeMobile()) { - await this.callSignin(() => this.userManager!.signinMobile(args), redirectUrl); + await this.#callSignin(() => this.#userManager!.signinMobile(args), redirectUrl); } else { - switch (args?.desktopNavigationType ?? this.settings.desktopNavigationType) { + switch (args?.desktopNavigationType ?? this.#settings.desktopNavigationType) { case DesktopNavigation.POPUP: - await this.callSignin(() => this.userManager!.signinPopup(args), redirectUrl); + await this.#callSignin(() => this.#userManager!.signinPopup(args), redirectUrl); break; case DesktopNavigation.REDIRECT: default: sessionStorage.setItem(REDIRECT_URL_KEY, redirectUrl); - await this.userManager?.signinRedirect(args); + await this.#userManager?.signinRedirect(args); break; } } - return (this._isAuthenticated); + return (this.#isAuthenticated); } public async renew(args?: RenewArgs): Promise { - return this.signinSilent(args).catch(error => console.error(error)); + return this.#signinSilent(args).catch(error => console.error(error)); } public getSettings(): OIDCAuthSettings { - return this.settings; + return this.#settings; } public isRenewing(): boolean { - return this._isRenewing; + return this.#isRenewing; } public async isAuthenticated(): Promise { - await this.waitForRenew('isAuthenticated()'); - return this._isAuthenticated; + await this.#waitForRenew('isAuthenticated()'); + return this.#isAuthenticated; } public async getUserProfile(): Promise { - await this.waitForRenew('getUserProfile()'); - return this._userProfile; + await this.#waitForRenew('getUserProfile()'); + return this.#userProfile; } public async getUserSession(): Promise { - await this.waitForRenew('getUserSession()'); - return this._userSession; + await this.#waitForRenew('getUserSession()'); + return this.#userSession; } public async getIdToken(): Promise { - await this.waitForRenew('getIdToken()'); - return this._idToken; + await this.#waitForRenew('getIdToken()'); + return this.#idToken; } public async getIdTokenDecoded(): Promise { - await this.waitForRenew('getIdTokenDecoded()'); - return AuthUtils.decodeJwt(this._idToken); + await this.#waitForRenew('getIdTokenDecoded()'); + return AuthUtils.decodeJwt(this.#idToken); } public async getAccessToken(): Promise { - await this.waitForRenew('getAccessToken()'); - return this._accessToken; + await this.#waitForRenew('getAccessToken()'); + return this.#accessToken; } public async getAccessTokenDecoded(): Promise { - await this.waitForRenew('getAccessTokenDecoded()'); - return AuthUtils.decodeJwt(this._accessToken); + await this.#waitForRenew('getAccessTokenDecoded()'); + return AuthUtils.decodeJwt(this.#accessToken); } // --- DESTROY --- public destroy(): void { - this.idTokenSubs.unsubscribe(); - this.accessTokenSubs.unsubscribe(); - this.userProfileSubs.unsubscribe(); - this.userSessionSubs.unsubscribe(); - this.authenticatedSubs.unsubscribe(); - this.renewingSubs.unsubscribe(); - this.redirectSubs.unsubscribe(); - this.userManagerSubs.forEach(unsub => unsub()); + this.#idTokenSubs.unsubscribe(); + this.#accessTokenSubs.unsubscribe(); + this.#userProfileSubs.unsubscribe(); + this.#userSessionSubs.unsubscribe(); + this.#authenticatedSubs.unsubscribe(); + this.#renewingSubs.unsubscribe(); + this.#redirectSubs.unsubscribe(); + this.#userManagerSubs.forEach(unsub => unsub()); } // --- HANDLER(s) --- public onIdTokenChanged(handler: AuthSubscriber<[string | undefined]>): AuthSubscription { - return this.idTokenSubs.add(handler); + return this.#idTokenSubs.add(handler); } public onAccessTokenChanged(handler: AuthSubscriber<[string | undefined]>): AuthSubscription { - return this.accessTokenSubs.add(handler); + return this.#accessTokenSubs.add(handler); } public onUserProfileChanged(handler: AuthSubscriber<[UserProfile | undefined]>): AuthSubscription { - return this.userProfileSubs.add(handler); + return this.#userProfileSubs.add(handler); } public onUserSessionChanged(handler: AuthSubscriber<[UserSession | undefined]>): AuthSubscription { - return this.userSessionSubs.add(handler); + return this.#userSessionSubs.add(handler); } public onAuthenticatedChanged(handler: AuthSubscriber<[boolean]>): AuthSubscription { - return this.authenticatedSubs.add(handler); + return this.#authenticatedSubs.add(handler); } public onRenewingChanged(handler: AuthSubscriber<[boolean]>): AuthSubscription { - return this.renewingSubs.add(handler); + return this.#renewingSubs.add(handler); } public onRedirect(handler: AuthSubscriber<[URL]>): AuthSubscription { - return this.redirectSubs.add(handler); + return this.#redirectSubs.add(handler); } // --- HELPER(s) --- @@ -322,8 +322,8 @@ export class OIDCAuthManager extends AuthManager { * 5) the web app (instead of the proper redirect_uri) is loaded in the iframe or popup * 6) an inception loop occurs -> app in iframe in iframe in iframe or popup in popup in popup.. */ - private assertNotInInceptionLoop(): void { - [this.settings.internal?.silent_redirect_uri, this.settings.internal?.popup_redirect_uri] + #assertNotInInceptionLoop(): void { + [this.#settings.internal?.silent_redirect_uri, this.#settings.internal?.popup_redirect_uri] .forEach(uri => { const htmlFileName = (new RegExp(/^.*\/(.*).html$/gm).exec(uri ?? ''))?.[1]; const error = new Error(`[OIDCAuthManager] ${uri ?? 'redirect uri'} was not found.`); @@ -351,10 +351,10 @@ export class OIDCAuthManager extends AuthManager { * 6) in parallel user navigates somewhere and triggers isAuthenticated * 7) isAuthenticated should wait signinSilent to finish before returning */ - private async waitForRenew(caller: string): Promise { + async #waitForRenew(caller: string): Promise { const startTime = Date.now(); // eslint-disable-next-line no-loops/no-loops - while (this._isRenewing) { + while (this.#isRenewing) { if (Date.now() > (startTime + 5000)) { console.warn('[@badisi/auth-js]', `\`${caller}\``, 'timed out waiting for renew to finish.'); break; @@ -372,63 +372,63 @@ export class OIDCAuthManager extends AuthManager { * 3) url did not changed, so no navigation occured and no guards either * 4) at this point, user is logged-out but still inside the app and able to see it */ - private postLogoutVerification(redirectUrlAskedAfterLogout: string | null): void { + #postLogoutVerification(redirectUrlAskedAfterLogout: string | null): void { const postLogoutUrl = AuthUtils.stringToURL(redirectUrlAskedAfterLogout ?? '/'); - if (this.settings.loginRequired && (location.origin === postLogoutUrl.origin)) { + if (this.#settings.loginRequired && (location.origin === postLogoutUrl.origin)) { location.reload(); } } - private notifyRenew(value: boolean): void { - this._isRenewing = value; - this.renewingSubs.notify(value); + #notifyRenew(value: boolean): void { + this.#isRenewing = value; + this.#renewingSubs.notify(value); } - private async runSyncOrAsync(job: () => Promise): Promise { + async #runSyncOrAsync(job: () => Promise): Promise { // eslint-disable-next-line @typescript-eslint/brace-style, max-statements-per-line - if (this.settings.loginRequired) { await job(); } else { void job(); } + if (this.#settings.loginRequired) { await job(); } else { void job(); } } - private async redirect(url: string | null, error?: unknown): Promise { + async #redirect(url: string | null, error?: unknown): Promise { if (error) { console.error(error); - await this.removeUser(); + await this.#removeUser(); } const redirectUrl = AuthUtils.stringToURL(url ?? '/'); // History cannot be rewritten when origin is different if (location.origin === redirectUrl.origin) { history.replaceState(history.state, '', redirectUrl.href); - this.redirectSubs.notify(redirectUrl); + this.#redirectSubs.notify(redirectUrl); } else { location.href = redirectUrl.href; } } - private async removeUser(): Promise { + async #removeUser(): Promise { this.user = null; await Promise.all([ - this.userManager?.clearStaleState(), - this.userManager?.removeUser() + this.#userManager?.clearStaleState(), + this.#userManager?.removeUser() ]); } - private async signinSilent(args?: SigninSilentArgs): Promise { - this.notifyRenew(true); + async #signinSilent(args?: SigninSilentArgs): Promise { + this.#notifyRenew(true); try { - await this.userManager?.signinSilent(args); + await this.#userManager?.signinSilent(args); } catch (error) { - await this.removeUser(); + await this.#removeUser(); throw error; } finally { - this.notifyRenew(false); + this.#notifyRenew(false); } } - private async callSignin(managerCall: () => Promise, redirectUrl: string | null): Promise { + async #callSignin(managerCall: () => Promise, redirectUrl: string | null): Promise { try { - this.notifyRenew(true); + this.#notifyRenew(true); await managerCall().catch((error: Error) => { if (error?.message === 'Attempted to navigate on a disposed window') { error = new Error('[OIDCAuthManager] Attempted to navigate on a disposed window.'); @@ -437,16 +437,16 @@ export class OIDCAuthManager extends AuthManager { } throw error; }); - await this.redirect(redirectUrl); + await this.#redirect(redirectUrl); } catch (error) { - await this.redirect('/', error); + await this.#redirect('/', error); throw error; } finally { - this.notifyRenew(false); + this.#notifyRenew(false); } } - private async callSignout(managerCall: () => Promise, redirectUrl: string | null): Promise { + async #callSignout(managerCall: () => Promise, redirectUrl: string | null): Promise { try { await managerCall().catch((error: Error) => { if (error?.message === 'Attempted to navigate on a disposed window') { @@ -456,14 +456,14 @@ export class OIDCAuthManager extends AuthManager { } throw error; }); - await this.redirect(redirectUrl); - await this.removeUser(); + await this.#redirect(redirectUrl); + await this.#removeUser(); } catch (error) { redirectUrl = '/'; - await this.redirect(redirectUrl, error); + await this.#redirect(redirectUrl, error); throw error; } finally { - this.postLogoutVerification(redirectUrl); + this.#postLogoutVerification(redirectUrl); } } } diff --git a/projects/auth-js/oidc/oidc-user-manager.ts b/projects/auth-js/oidc/oidc-user-manager.ts index 9bd2409..a222c21 100644 --- a/projects/auth-js/oidc/oidc-user-manager.ts +++ b/projects/auth-js/oidc/oidc-user-manager.ts @@ -14,7 +14,7 @@ import { OIDCAuthSettings } from './models/oidc-auth-settings.model'; * (ex: signinMobile, signoutMobile, MobileNavigator, MobileWindow) */ export class OidcUserManager extends UserManager { - private _mobileNavigator!: MobileNavigator; + #mobileNavigator!: MobileNavigator; constructor( public libSettings: OIDCAuthSettings @@ -28,7 +28,7 @@ export class OidcUserManager extends UserManager { ...libSettings.internal } as UserManagerSettings); - this._mobileNavigator = new MobileNavigator(); + this.#mobileNavigator = new MobileNavigator(); } /* public async readRequestTypeFromState(url = location.href): Promise { @@ -63,7 +63,7 @@ export class OidcUserManager extends UserManager { mobileWindowHeight: mobileWindowHeight ?? this.libSettings.internal?.mobileWindowHeight }; - const handle = this._mobileNavigator.prepare(this.settings.post_logout_redirect_uri!, params); + const handle = this.#mobileNavigator.prepare(this.settings.post_logout_redirect_uri!, params); await this._signout({ request_type: 'so:m', @@ -92,7 +92,7 @@ export class OidcUserManager extends UserManager { mobileWindowHeight: mobileWindowHeight ?? this.libSettings.internal?.mobileWindowHeight }; - const handle = this._mobileNavigator.prepare(this.settings.redirect_uri, params); + const handle = this.#mobileNavigator.prepare(this.settings.redirect_uri, params); const user = await this._signin({ request_type: 'si:m', diff --git a/projects/ngx-auth/core/auth.guard.ts b/projects/ngx-auth/core/auth.guard.ts index c5f9775..a8ef277 100644 --- a/projects/ngx-auth/core/auth.guard.ts +++ b/projects/ngx-auth/core/auth.guard.ts @@ -20,20 +20,20 @@ export interface AuthGuardData extends Data { @Injectable() export class AuthGuard implements CanLoad, CanActivate, CanActivateChild { - private authService = inject(AuthService); - private router = inject(Router); + #authService = inject(AuthService); + #router = inject(Router); public canLoad(route: Route): Observable { - const inFlightUrl = this.router.getCurrentNavigation()?.extractedUrl?.toString(); - return this.isAllowed(route.data, inFlightUrl); + const inFlightUrl = this.#router.getCurrentNavigation()?.extractedUrl?.toString(); + return this.#isAllowed(route.data, inFlightUrl); } public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.isAllowed(route.data, state.url); + return this.#isAllowed(route.data, state.url); } public canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.isAllowed(childRoute.data, state.url); + return this.#isAllowed(childRoute.data, state.url); } public isAuthorized(): Observable | Promise | boolean { @@ -42,17 +42,17 @@ export class AuthGuard implements CanLoad, CanActivate, CanActivateChild { // ---- HELPER(s) ---- - private isPromise(value: Promise): value is Promise { + #isPromise(value: Promise): value is Promise { return Boolean(value?.then && typeof value.then === 'function'); } - private isAuthorized$(data?: AuthGuardData): Observable { + #isAuthorized$(data?: AuthGuardData): Observable { const transformToObs = (value: Observable | Promise | boolean): Observable => { if (typeof value === 'boolean') { return of(value); } else if (isObservable(value)) { return value; - } else if (this.isPromise(value)) { + } else if (this.#isPromise(value)) { return from(value); } else { return of(false); @@ -62,8 +62,8 @@ export class AuthGuard implements CanLoad, CanActivate, CanActivateChild { const validator = data?.authGuardValidator; if (typeof validator === 'function') { return forkJoin({ - userProfile: this.authService.userProfile$.pipe(take(1)), - accessToken: this.authService.accessTokenDecoded$.pipe(take(1)) + userProfile: this.#authService.userProfile$.pipe(take(1)), + accessToken: this.#authService.accessTokenDecoded$.pipe(take(1)) }).pipe( switchMap(({ userProfile, accessToken }) => transformToObs(validator(userProfile, accessToken))) ); @@ -75,14 +75,14 @@ export class AuthGuard implements CanLoad, CanActivate, CanActivateChild { } } - private isAuthenticated$(redirectUrl = location.href): Observable { - return this.authService + #isAuthenticated$(redirectUrl = location.href): Observable { + return this.#authService .isAuthenticated$ .pipe( take(1), switchMap(isAuthenticated => { if (!isAuthenticated) { - return from(this.authService.login({ redirectUrl })) + return from(this.#authService.login({ redirectUrl })) .pipe( catchError(() => of(false)) ); @@ -92,14 +92,14 @@ export class AuthGuard implements CanLoad, CanActivate, CanActivateChild { ); } - private isAllowed(data?: AuthGuardData, redirectUrl = location.href): Observable { - return this.isAuthenticated$(redirectUrl) + #isAllowed(data?: AuthGuardData, redirectUrl = location.href): Observable { + return this.#isAuthenticated$(redirectUrl) .pipe( - switchMap(isAuthenticated => (isAuthenticated) ? this.isAuthorized$(data) : of(false)), + switchMap(isAuthenticated => (isAuthenticated) ? this.#isAuthorized$(data) : of(false)), map(isAuthorized => { if (!isAuthorized) { - const notAllowedUrl = data?.authGuardRedirectUrl || this.authService.getSettings()?.authGuardRedirectUrl; - return this.router.parseUrl(notAllowedUrl ? notAllowedUrl : this.router.url); + const notAllowedUrl = data?.authGuardRedirectUrl || this.#authService.getSettings()?.authGuardRedirectUrl; + return this.#router.parseUrl(notAllowedUrl ? notAllowedUrl : this.#router.url); } return true; }) diff --git a/projects/ngx-auth/core/auth.service.ts b/projects/ngx-auth/core/auth.service.ts index 332744a..e3fd287 100644 --- a/projects/ngx-auth/core/auth.service.ts +++ b/projects/ngx-auth/core/auth.service.ts @@ -14,66 +14,66 @@ import { AuthSettings } from './auth-settings.model'; providedIn: 'root' }) export class AuthService implements OnDestroy { - private manager = inject(AUTH_MANAGER); - private ngZone = inject(NgZone); - private router = inject(Router); + #manager = inject(AUTH_MANAGER); + #ngZone = inject(NgZone); + #router = inject(Router); - private _idToken$: ReplaySubject = new ReplaySubject(1); - private _accessToken$: ReplaySubject = new ReplaySubject(1); - private _userProfile$: ReplaySubject = new ReplaySubject(1); - private _userSession$: ReplaySubject = new ReplaySubject(1); - private _isAuthenticated$: ReplaySubject = new ReplaySubject(1); - private _isRenewing$: ReplaySubject = new ReplaySubject(1); + #idToken$: ReplaySubject = new ReplaySubject(1); + #accessToken$: ReplaySubject = new ReplaySubject(1); + #userProfile$: ReplaySubject = new ReplaySubject(1); + #userSession$: ReplaySubject = new ReplaySubject(1); + #isAuthenticated$: ReplaySubject = new ReplaySubject(1); + #isRenewing$: ReplaySubject = new ReplaySubject(1); - private authManagerSubs: AuthSubscription[] = []; + #authManagerSubs: AuthSubscription[] = []; constructor() { - this.listenForManagerChanges(); + this.#listenForManagerChanges(); } public ngOnDestroy(): void { - this.authManagerSubs.forEach(sub => sub.unsubscribe()); + this.#authManagerSubs.forEach(sub => sub.unsubscribe()); } /* eslint-disable @typescript-eslint/member-ordering */ public readonly isRenewing$: Observable = - this._isRenewing$.asObservable().pipe( + this.#isRenewing$.asObservable().pipe( distinctUntilChanged() ); public readonly isAuthenticated$: Observable = - this._isAuthenticated$.asObservable().pipe( + this.#isAuthenticated$.asObservable().pipe( distinctUntilChanged() ); public readonly userProfile$: Observable = - this._userProfile$.asObservable().pipe( + this.#userProfile$.asObservable().pipe( distinctUntilChanged() ); public readonly userSession$: Observable = - this._userSession$.asObservable().pipe( + this.#userSession$.asObservable().pipe( distinctUntilChanged() ); public readonly idToken$: Observable = - this._idToken$.asObservable().pipe( + this.#idToken$.asObservable().pipe( distinctUntilChanged() ); public readonly idTokenDecoded$: Observable = - this._idToken$.asObservable().pipe( + this.#idToken$.asObservable().pipe( distinctUntilChanged(), map(token => AuthUtils.decodeJwt(token)) ); public readonly accessToken$: Observable = - this._accessToken$.asObservable().pipe( + this.#accessToken$.asObservable().pipe( distinctUntilChanged() ); public readonly accessTokenDecoded$: Observable = - this._accessToken$.asObservable().pipe( + this.#accessToken$.asObservable().pipe( distinctUntilChanged(), map(token => AuthUtils.decodeJwt(token)) ); @@ -85,107 +85,107 @@ export class AuthService implements OnDestroy { * @see {@link OIDCAuthManager.login} */ public async login(args?: LoginArgs): Promise { - return this.manager.login(args); + return this.#manager.login(args); } /** * @see {@link OIDCAuthManager.logout} */ public async logout(args?: LogoutArgs): Promise { - return this.manager.logout(args); + return this.#manager.logout(args); } /** * @see {@link OIDCAuthManager.renew} */ public async renew(args?: RenewArgs): Promise { - return this.manager.renew(args); + return this.#manager.renew(args); } /** * @see {@link OIDCAuthManager.getSettings} */ public getSettings(): AuthSettings { - return this.manager.getSettings() as AuthSettings; + return this.#manager.getSettings() as AuthSettings; } /** * @see {@link OIDCAuthManager.isRenewing} */ public isRenewing(): boolean { - return this.manager.isRenewing(); + return this.#manager.isRenewing(); } /** * @see {@link OIDCAuthManager.isAuthenticated} */ public async isAuthenticated(): Promise { - return this.manager.isAuthenticated(); + return this.#manager.isAuthenticated(); } /** * @see {@link OIDCAuthManager.getUserProfile} */ public async getUserProfile(): Promise { - return this.manager.getUserProfile(); + return this.#manager.getUserProfile(); } /** * @see {@link OIDCAuthManager.getUserSession} */ public async getUserSession(): Promise { - return this.manager.getUserSession(); + return this.#manager.getUserSession(); } /** * @see {@link OIDCAuthManager.getIdToken} */ public async getIdToken(): Promise { - return this.manager.getIdToken(); + return this.#manager.getIdToken(); } /** * @see {@link OIDCAuthManager.getIdTokenDecoded} */ public async getIdTokenDecoded(): Promise { - return this.manager.getIdTokenDecoded(); + return this.#manager.getIdTokenDecoded(); } /** * @see {@link OIDCAuthManager.getAccessToken} */ public async getAccessToken(): Promise { - return this.manager.getAccessToken(); + return this.#manager.getAccessToken(); } /** * @see {@link OIDCAuthManager.getAccessTokenDecoded} */ public async getAccessTokenDecoded(): Promise { - return this.manager.getAccessTokenDecoded(); + return this.#manager.getAccessTokenDecoded(); } // --- HELPER(s) ---- - private listenForManagerChanges(): void { - this.authManagerSubs.push( - this.manager.onIdTokenChanged(value => this.ngZone.run(() => this._idToken$.next(value))), - this.manager.onAccessTokenChanged(value => this.ngZone.run(() => this._accessToken$.next(value))), - this.manager.onUserProfileChanged(value => this.ngZone.run(() => this._userProfile$.next(value))), - this.manager.onUserSessionChanged(value => this.ngZone.run(() => this._userSession$.next(value))), - this.manager.onAuthenticatedChanged(value => this.ngZone.run(() => this._isAuthenticated$.next(value))), - this.manager.onRenewingChanged(value => this.ngZone.run(() => this._isRenewing$.next(value))), - this.manager.onRedirect(value => { + #listenForManagerChanges(): void { + this.#authManagerSubs.push( + this.#manager.onIdTokenChanged(value => this.#ngZone.run(() => this.#idToken$.next(value))), + this.#manager.onAccessTokenChanged(value => this.#ngZone.run(() => this.#accessToken$.next(value))), + this.#manager.onUserProfileChanged(value => this.#ngZone.run(() => this.#userProfile$.next(value))), + this.#manager.onUserSessionChanged(value => this.#ngZone.run(() => this.#userSession$.next(value))), + this.#manager.onAuthenticatedChanged(value => this.#ngZone.run(() => this.#isAuthenticated$.next(value))), + this.#manager.onRenewingChanged(value => this.#ngZone.run(() => this.#isRenewing$.next(value))), + this.#manager.onRedirect(value => { // Avoid cancelling any current navigation - if (!this.router.getCurrentNavigation()) { - this.ngZone.run(() => { + if (!this.#router.getCurrentNavigation()) { + this.#ngZone.run(() => { /** * Angular only navigates to an absolute path from the base url. * So we need to substract the base url from the received url. * ex: transform 'http://domain/base/private' to '/private' */ const absoluteUrl = value.href.replace(AuthUtils.getBaseUrl(), ''); - void this.router.navigateByUrl(absoluteUrl); + void this.#router.navigateByUrl(absoluteUrl); }); } })