Introduction
Authentication in Micro Frontend is more complicated than regular SPA: Shell App handles login, but all MFEs need an access token. This article designs SSO architecture with Keycloak.
1. SSO Architecture
┌──────────┐ 1. Login ┌──────────┐
│ Shell │──────────────►│ Keycloak │
│ App │◄──────────────│ (OIDC) │
│ │ 2. Tokens └──────────┘
│ │
│ 3. Store tokens (memory)
│ 4. Broadcast auth state
│ │
│ ┌───────┴───────┐
│ ▼ ▼
│ MFE A MFE B
│ (useAuth) (useAuth)
│ │ │
│ ▼ ▼
│ API calls with Bearer token
└─────────────────────────
OIDC Authorization Code Flow + PKCE
1. User clicks Login → Shell redirects to Keycloak
2. User authenticates → Keycloak redirects back with auth code
3. Shell exchanges auth code for tokens (PKCE)
4. Tokens: Access (5 min), Refresh (30 min), ID Token
5. Shell stores in memory (NOT localStorage!)
6. Shell broadcasts auth state to MFEs
2. Token Management
2.1 Storage Security
❌ localStorage / sessionStorage: XSS vulnerable
✅ Memory (JS variable): Cleared on refresh + silent renew
✅ HTTP-only Cookie: For BFF pattern (server-side)
2.2 Token Sharing
// Option 1: React Context (same React via Module Federation)
<AuthContext.Provider value={auth}>
<MFEContainer />
</AuthContext.Provider>
// Option 2: Events (framework agnostic)
window.dispatchEvent(new CustomEvent('auth:token-updated', {
detail: { accessToken, user, roles }
}));
2.3 Silent Renewal
useEffect(() => {
const interval = setInterval(async () => {
const newTokens = await keycloak.refresh(refreshToken);
broadcastToken(newTokens.accessToken);
}, 4 * 60 * 1000); // Refresh 1 min before expiry
return () => clearInterval(interval);
}, [refreshToken]);
3. Authorization — RBAC
Keycloak Roles:
├── admin → Full access
├── manager → Order + Product management
├── editor → Product editing only
├── customer → Shopping, order history
└── guest → Browse products only
Frontend Authorization
// Shell: route-level guard
<Route path="/admin/*" element={
<RequireRole role="admin"><AdminMFE /></RequireRole>
} />
// MFE: feature-level guard
{hasRole('editor') && <EditButton />}
{hasRole('admin') && <DeleteButton />}
Backend Authorization (API Gateway)
routes:
- path: /api/products
methods: [POST, PUT, DELETE]
plugins:
jwt-auth: {}
rbac: { required_roles: ["editor", "admin"] }
Important: Frontend auth is UX only — backend must always validate.
4. Security Best Practices
| Practice | Description |
|---|---|
| Tokens in memory | Do not save access tokens in localStorage |
| HTTP-only cookies | For BFF pattern |
| PKCE | Required for SPA |
| Short-lived tokens | Access: 5 min, Refresh: 30 min |
| Token rotation | New refresh token every time refresh |
| CORS strict | Only accept known origins |
| CSP headers | Prevent XSS |
Summary
- Shell own authentication — login, logout, token management
- MFEs receive tokens via Context or Events
- Tokens in memory + silent refresh
- RBAC: route-level + feature-level + API-level
- Backend always validates
Next article: Lesson 17: BFF Pattern — Backend for Frontend