Sharing Authentication Tokens Securely Across Remote Apps #

The Challenge of Secure Token Propagation in Module Federation #

Micro-frontend architectures using Module Federation inherently fragment execution contexts, creating a critical vulnerability surface when sharing authentication tokens securely across remote apps. The core issue stems from independent runtime boundaries that lack a unified security context, causing tokens to leak, expire prematurely, or desynchronize during asynchronous hydration. When baseline distributed state management relies on naive propagation mechanisms, it bypasses established enterprise security controls. Understanding the architectural trade-offs of Cross-App State & Context Sharing is essential before implementing a resilient token distribution layer. Direct exposure via localStorage, unsecured postMessage, or global window objects introduces severe XSS vectors, CSRF vulnerabilities, and race conditions that immediately break enterprise SSO compliance and OIDC session management standards.

Why Tokens Leak or Desynchronize Across Remote Boundaries #

Token synchronization failures in distributed UIs typically manifest from four distinct technical root causes:

Step-by-Step Architecture for Secure Token Distribution #

  1. Centralize Token Storage in HttpOnly Secure Cookies or Memory-Only Singleton Eliminate localStorage and sessionStorage entirely. Implement a host-level secure token manager that enforces strict access controls. Synchronize tokens exclusively via HttpOnly, Secure, SameSite=Strict cookies, or maintain a memory-only singleton that never persists to disk.

  2. Expose a Read-Only Token Bridge via Module Federation Utilize Webpack or Vite exposes to share a strictly typed, read-only token accessor function. Return immutable observables or frozen objects rather than raw mutable references. Enforce strict scope validation and origin verification before granting remote consumers access to the token stream.

  3. Implement Observable Token State with Strict Lifecycle Hooks Deploy RxJS or Redux middleware to broadcast token updates across module boundaries. Ensure remotes establish subscriptions before executing initial API calls. When integrating these token streams into existing state trees without introducing prop drilling, reference Synchronizing Redux Across Micro-Frontends for proven middleware interception patterns.

  4. Enforce Token Validation & CSP on Remote Mount Validate JWT signatures and enforce strict expiration windows before mounting remote components. Apply rigorous Content-Security-Policy headers (script-src, connect-src) to prevent injection attacks during remote hydration. Reject any remote that fails cryptographic validation or attempts unauthorized storage access.

Implementation: Module Federation Config & Secure Token Bridge (TypeScript) #

Module Federation Shared Configuration #

The host configuration must strictly isolate the auth bridge, enforce singleton behavior, and disable eager loading to prevent premature initialization.

// webpack.config.ts (Host)
import { ModuleFederationPlugin } from 'webpack/lib/container/ModuleFederationPlugin';

export const config = {
 plugins: [
 new ModuleFederationPlugin({
 name: 'host_app',
 remotes: {
 remote_dashboard: 'remote_dashboard@https://cdn.example.com/remote/remoteEntry.js',
 },
 exposes: {
 './auth-bridge': './src/auth/SecureTokenBridge.ts',
 },
 shared: {
 react: { singleton: true, eager: false, requiredVersion: '^18.2.0' },
 'rxjs': { singleton: true, eager: false, requiredVersion: '^7.8.0' },
 // Critical: Pin auth bridge to prevent version drift across remotes
 './auth-bridge': { singleton: true, eager: false },
 },
 }),
 ],
};

Secure Token Bridge Implementation #

This TypeScript class provides a memory-only, observable-based token manager with strict null checks, automatic HttpOnly cookie synchronization, and cryptographic validation.

// src/auth/SecureTokenBridge.ts
import { BehaviorSubject, Observable, filter, map } from 'rxjs';

export interface TokenPayload {
 accessToken: string;
 expiresAt: number;
 refreshToken?: string;
}

export class SecureTokenBridge {
 private readonly _token$ = new BehaviorSubject<TokenPayload | null>(null);
 private readonly _scope = 'enterprise_sso';

 constructor() {
 // Initialize from HttpOnly cookie sync (server-side rendered or secure fetch)
 this.syncFromSecureCookie();
 }

 public getToken(): Observable<TokenPayload | null> {
 return this._token$.asObservable().pipe(
 filter((token): token is TokenPayload => token !== null),
 map((token) => Object.freeze({ ...token }))
 );
 }

 public onTokenChange(): Observable<TokenPayload> {
 return this._token$.pipe(
 filter((t): t is TokenPayload => t !== null),
 map((t) => Object.freeze({ ...t }))
 );
 }

 public validateToken(token: TokenPayload): boolean {
 if (!token || !token.accessToken) return false;
 const now = Date.now();
 // Enforce 60-second buffer for clock skew
 return token.expiresAt > now + 60_000;
 }

 public updateToken(payload: TokenPayload | null): void {
 if (payload && !this.validateToken(payload)) {
 throw new Error('SecureTokenBridge: Invalid or expired token payload.');
 }
 this._token$.next(payload);
 // Trigger secure rotation via backend endpoint (not exposed to client)
 if (payload?.refreshToken) {
 this.scheduleRotation(payload);
 }
 }

 private syncFromSecureCookie(): void {
 // In production, fetch via secure endpoint that reads HttpOnly cookies
 // Never expose raw token to JS from document.cookie
 fetch('/api/auth/sync', { credentials: 'include' })
 .then(res => res.json())
 .then(data => this.updateToken(data))
 .catch(() => this._token$.next(null));
 }

 private scheduleRotation(payload: TokenPayload): void {
 // Implement exponential backoff and circuit breaker for rotation
 // Omitted for brevity, but must use AbortController and strict timeout
 }
}

export const tokenBridge = new SecureTokenBridge();

Remote App Integration Pattern #

Remotes must consume the bridge asynchronously, wrapping initialization in React Suspense and error boundaries to prevent cascading UI failures.

// remote_app/src/AuthProvider.tsx
import React, { Suspense, useEffect, useState } from 'react';
import { tokenBridge } from 'host_app/auth-bridge';

interface AuthProviderProps {
 children: React.ReactNode;
}

export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
 const [isReady, setIsReady] = useState(false);
 const [error, setError] = useState<Error | null>(null);

 useEffect(() => {
 const subscription = tokenBridge.getToken().subscribe({
 next: (token) => {
 if (tokenBridge.validateToken(token)) {
 // Attach to global fetch/Axios interceptor
 (globalThis as any).__AUTH_TOKEN__ = token.accessToken;
 setIsReady(true);
 }
 },
 error: (err) => setError(err),
 });

 return () => subscription.unsubscribe();
 }, []);

 if (error) {
 throw new Error(`Auth bridge initialization failed: ${error.message}`);
 }

 if (!isReady) {
 throw new Promise<void>((resolve) => {
 const sub = tokenBridge.getToken().subscribe({
 next: () => { setIsReady(true); resolve(); sub.unsubscribe(); },
 error: () => { setError(new Error('Token sync timeout')); resolve(); sub.unsubscribe(); }
 });
 });
 }

 return <>{children}</>;
};

// Usage in Remote Root
export const RemoteRoot: React.FC = () => (
 <Suspense fallback={<div className="auth-skeleton">Initializing secure context...</div>}>
 <AuthProvider>
 {/* Remote UI Components */}
 </AuthProvider>
 </Suspense>
);

Validation & Security Testing Protocol #

Enterprise deployments require rigorous, automated validation before promoting sharing authentication tokens securely across remote apps to production environments:

Rollback Strategies & Graceful Degradation #

Distributed authentication systems must tolerate partial failures without compromising user sessions or application stability: