This subsystem handles user onboarding, secure route protection, administrative controls, polymorphic credential restoration, and reactive session synchronization. It communicates with the Central Authentication Service (Auth Service / Internal IdP) using a secure, cookie-encapsulated JWT architecture driven by modern client-side security best practices.
The module is engineered entirely using modern Angular Standalone design patterns, functional paradigms, and the Signals reactivity ecosystem. The application strictly implements a modern Feature-driven Vertical Slice Architecture coupled with isolated core and shared layers to enforce low coupling and a clean separation of concerns:
The core orchestrator of the active client session lifecycle. It acts as a global gateway guarding outbound and inbound traffic:
- Session Orchestration: Traps
JWT_EXPIRED(401) responses to halt the request pipeline, initiates the background synchronization sequence, and manages the queue of parallel in-flight requests. - Terminal Exception Mapping: Intercepts critical infrastructure faults (e.g.,
ACCESS_FORBIDDEN,SERVICE_UNAVAILABLE,AUTHENTICATION_FAILED) to break recursive loops, trigger profile cleanups, and dispatch toast notifications viaSnacksService.
A decoupled, atomic service dedicated strictly to token-refresh execution and identity profile persistence. Adhering to the Single Responsibility Principle (SRP), it houses the loop-free RxJS exhaustMap semaphore pipeline. It triggers the background /refresh API endpoint, securely broadcasts completion/error signals back to the orchestrating interceptor.
A transparent transport-level modifier registered at the absolute top of the HTTP pipeline. Its sole purpose is to clone outbound API traffic to explicitly append { withCredentials: true }. This signals the browser engine to attach local authentication cookies to cross-origin requests natively, isolating transport configurations from business-level error interception.
roleGuard: Safeguards administrative route configurations by ensuring the active user identity payload contains required'ADMIN'clearance roles.
To achieve maximum protection against Cross-Site Scripting (XSS) token theft vulnerabilities, the JavaScript layer never captures, stores, or interacts with raw JWT strings.
- Zero Local Footprint: Tokens are completely absent from
localStorage,sessionStorage, and global window variables. - Browser-Enforced Security: Authorization lifecycles rely entirely on HttpOnly, Secure, SameSite=Lax cookies managed implicitly by the browser's native engine. This architecture avoids the pitfalls of obsolete and heavily restricted
SameSite=Nonethird-party cookie patterns. - CORS Pipeline: Seamless cross-domain transport is guaranteed by the global chaining of the
withCredentialsInterceptorbefore any error handlers process the traffic.
Simultaneous layout rendering often causes a cascade of parallel API requests to fail with a JWT_EXPIRED footprint at the exact same millisecond. To resolve this without spawning procedural, error-prone lock variables (like isRefreshing = true/false) in the interceptor, the application leverages an RxJS exhaustMap Semaphore Pattern inside the JwtHandlerService.
[ Concurrent Requests ] ──► ( httpErrorsHandlerInterceptor ) ──► [ Catch 401 / JWT_EXPIRED ]
│
┌─────────────────────────────────────────────────────────────────────┘
▼
[ jwtHandlerService.refreshTokenSub.next(true) ]
│
├──► Signal 1 ──► [ exhaustMap Active ] ──► Fires GET /users/refresh to Backend
├──► Signal 2 ──► [ exhaustMap Busy ] ──► IGNORED (Dropped, preventing API flood)
└──► Signal 3 ──► [ exhaustMap Busy ] ──► IGNORED (Dropped, preventing API flood)
│
┌─────────────────────────────────────────────────────────────────────┘
▼
[ All In-Flight Requests Subscribe to: refreshTokenReady.pipe(take(1)) ]
│
├──► If Success (true) ─────────► Safely re-fire original next(req) with new cookies
└──► If Admin Revocation (401) ─► Forces redirect to /login
- The Catch: Multiple concurrent requests get knocked back by the backend due to an expired Access Cookie (
401 JWT_EXPIRED). - The Signal: Every request catches the status code and pushes a notification payload to
jwtHandlerService.refreshTokenSub.next(true). - The Lockless Filter:
- The primary signal enters the
exhaustMappipeline within the service constructor, launching a single active network request toGET /users/refresh. - While this network call is in-flight, the
exhaustMapdrops all sibling incoming signals, neutralizing token refresh spam and network flooding.
- The primary signal enters the
- The Synchronized Wait: Parallel requests switch their execution sequence to listen to the
refreshTokenReadystream via.pipe(take(1))and remain suspended in a non-blocking wait state. - The Safe Resolution:
catchErroris strategically bound inside the inner observable of theexhaustMapscope. This critical encapsulation ensures that a failing network transaction transforms into an atomic state emission rather than breaking or halting the outer subscription engine.- When the backend writes the new cookies via
Set-Cookie, the service firestruedown the stream. The blocked requests re-runnext(req)and proceed using the freshly baked cookies natively attached by the browser.
Administrators have the capability to instantly wipe or drop any user's active session records from the backend administration dashboard by purging their Refresh Token.
- When the revoked user's local Access Cookie expires, the background
exhaustMaptask fails with a structuralAUTHENTICATION_FAILED(401) response. - The internal
catchErrorcaptures this event, safely packs the error payload, and transmits it down to therefreshTokenReadychannel. - The interceptor captures this fatal notification, blocks the cascading request retries from causing infinite retry loops, invokes
purgeAuthSession()to clear local state variables, and flushes the user straight back to the/loginportal.
The architecture reuse-optimizes individual standalone views to behave dynamically based on url parameter entry contexts, employing defensive validation techniques.
The password reset journey utilizes a single view component that morphs its state and validation rules by examining the active ActivatedRoute snapshot map:
- State A: Requesting Reset Link (
formProcess == 'SendEmail')- Triggered via default
/reset-passwordnavigation. Active inputs evaluate identity parameters via asyncvalidateEmailExistcriteria, setting an anti-spam resend cooldown timeout.
- Triggered via default
- State B: Executing Credential Overwrite (
formProcess == 'ResetPassword')- Triggered if initialized with url parameters (via parametric
/reset-password/:id/:tokenor query-string layouts/reset-password?id=X&token=Y). Email constraints are detached, injecting strict complexity configurations (strongPasswordValidation) and matching validations.
- Triggered if initialized with url parameters (via parametric
Handles inbound validation links dispatched to user mailboxes. To protect network resources, the engine performs explicit defensive guard boundary evaluation on entry:
- Universal Parameter Mapping: The routing layer scans both
paramMapandqueryParamMapproperties to safely capture context variables regardless of whether the email template utilized semantic tokens or classic query string variables. - Guard Clauses: Strict boolean verification (
if (!id || !token)) filters out incomplete requests at the initialization boundary, blocking corrupted or partial URLs from dispatching futile network traffic to the database.
The application completely moves away from legacy, object-oriented framework boilerplate, adhering strictly to modern functional APIs and signal-driven reactive design:
- Functional Dependency Injection: All service, router, and configuration dependencies are loaded directly within the class execution scope using the functional
inject()API tokens, entirely eliminating heavy, non-inlined class constructors. - Signal-Based I/O Properties: Traditional decorator-based inputs and outputs are replaced with modern
input(),input.required(), andoutput()function wrappers. This ensures incoming values behave as pure reactive signals, forcing strict compliance with compile-time type validation. - Functional View Queries: DOM element references and child component mappings are handled via compile-time stable
viewChild()andviewChildren()functional queries instead of legacy, error-prone@ViewChildmetadata decorators. - Declarative Component Lifecycles (
effect+onCleanup): LegacyngAfterViewInitlifecycle hooks are completely banned. Input-driven data stream bindings and asynchronous setup procedures are evaluated inside declarative constructoreffect()blocks. The nativeonCleanuphook is leveraged to cleanly tear down and invalidate previous execution contexts or stale async subscriptions whenever reactive signal boundaries mutate. - Memory Leak Mitigation via
DestroyRef: Manual subscription arrays (this.subscriptions.add()) are eliminated. Out-of-order component unmounting and asynchronous execution delays are guarded via the declarativetakeUntilDestroyed(destroyRef)operator. If a user triggers a long-lived HTTP transaction or background timer and instantly navigates away from the layout before the stream engine resolves, the context tears down cleanly, preventing ghost operations or detached memory leak threads.
💡 Architectural Note for Reviewers: Interceptors must be injected inside the standalone bootstrap structure in the correct linear order. Transport filters (
withCredentialsInterceptor) must run before the error mapping chains (httpErrorsHandlerInterceptor) to ensure that any recursively re-fired requests (next(req)) after a successful cookie refresh still go through the complete interceptor lifecycle. Reversing this order will bypass transport configurations on retries, creating unauthorized infinite loops.