Appearance
Multi-Account SPI (keycloak-multiaccount)
The keycloak-multiaccount extension implements a Google-style account switcher for Keycloak. It allows accounting staff and other ERP users to be simultaneously authenticated into multiple company tenants in the same browser session, switching between them without requiring logout and re-login.
Use Case
In Microtec ERP, a single person (e.g., an accounting consultant) may manage multiple companies, each registered as a separate tenant with its own isolated database. Without this extension, the user would need to:
- Log out of Company A
- Navigate back to the login page
- Log in as Company B
With the Multi-Account SPI, the user sees a company switcher UI and can jump between tenants in a single click.
Cookie Architecture
Account data is persisted in an AES-256-GCM encrypted browser cookie. This approach was chosen over server-side sessions to avoid shared state between Keycloak cluster nodes and to enable offline account switching.
Cookie Specification
| Property | Value |
|---|---|
| Cookie name | KC_MULTI_ACCOUNT |
| Encryption | AES-256-GCM |
| Key source | Keycloak SPI provider configuration (multiAccountEncryptionKey) |
| Payload | JSON array of serialized account objects |
| Max accounts | 10 per browser session |
| Expiry | Matches the longest active SSO session |
| HttpOnly | true |
| SameSite | Lax |
| Secure | true (HTTPS only) |
Encrypted Payload Structure
json
[
{
"userId": "3f8a1b2c-...",
"username": "ahmed.hassan",
"tenantId": "tenant-al-noor",
"tenantName": "Al-Noor Trading",
"realmName": "microtec",
"sessionId": "abc123...",
"addedAt": "2026-05-30T10:00:00Z",
"avatarUrl": "https://...",
"isActive": true
},
{
"userId": "7d2e4f5a-...",
"username": "ahmed.hassan",
"tenantId": "tenant-gulf-supplies",
"tenantName": "Gulf Supplies Co.",
"realmName": "microtec",
"sessionId": "def456...",
"addedAt": "2026-05-30T10:15:00Z",
"avatarUrl": "https://...",
"isActive": false
}
]Encryption Configuration
Security-Critical Configuration
The encryption key must be set as a Keycloak Secret and never hardcoded. Rotate this key during security incidents.
The encryption key is configured via Keycloak's SPI provider configuration in standalone.xml / environment variable:
bash
# Environment variable passed to Keycloak container
KC_SPI_MULTI_ACCOUNT_ENCRYPTION_KEY=<base64-encoded-256-bit-key>Key generation:
bash
# Generate a new 256-bit key
openssl rand -base64 32The SPI reads the key using:
java
KeycloakSession session = ...;
String encryptionKey = session.getContext()
.getRealm()
.getAttribute("multiAccountEncryptionKey");Authenticator Flow Integration
The MAC authenticator runs at priority 10 in the browser flow — before the standard Cookie authenticator (priority 20):
Priority Bug History
In earlier deployments, the MAC authenticator was assigned priority 30 (same as IdP Redirector). This caused federated users to be redirected to the external IdP before the account cookie was checked. Fixed by setting MAC priority to 10.
UI: Account Switcher
The switcher is rendered as a dropdown in the Keycloak account console theme. It supports:
- RTL layout for Arabic UI (
dir="rtl"on the root element) - Company logo per tenant (loaded from
tenantLogoUrlclaim) - Active indicator showing the current session's tenant
- Add account option to authenticate a new tenant
- Remove account to clear a specific account from the cookie
html
<!-- Simplified account switcher markup (custom theme) -->
<div class="account-switcher" dir="{{ locale == 'ar' ? 'rtl' : 'ltr' }}">
{% for account in accounts %}
<div class="account-item {{ account.isActive ? 'active' : '' }}">
<img src="{{ account.avatarUrl }}" class="account-avatar" />
<div class="account-info">
<span class="tenant-name">{{ account.tenantName }}</span>
<span class="username">{{ account.username }}</span>
</div>
<a href="/switch?targetTenant={{ account.tenantId }}">Switch</a>
</div>
{% endfor %}
<a href="/add-account">+ Add Another Account</a>
</div>IdP Integration (Federated Identity Linking)
When a user authenticates via an external Identity Provider (e.g., Azure AD), the MAC SPI links the federated identity to a Microtec tenant account:
Capacity and Limits
| Limit | Value | Reason |
|---|---|---|
| Max accounts per cookie | 10 | Cookie size limit (~4KB encrypted) |
| Cookie expiry | Longest active SSO session | Auto-cleaned with session expiry |
| Encryption algorithm | AES-256-GCM | FIPS-compliant, authenticated encryption |
| IV/nonce size | 96 bits (12 bytes) | GCM standard, unique per encryption |
Exceeding 10 Accounts
If a user needs more than 10 company accounts, older accounts are evicted using an LRU (Least Recently Used) policy. The 10 most recently accessed accounts are retained.
Related Documentation
- Realm Configuration — How tenants map to realm clients
- Seeding SPI — How tenant clients are provisioned
- Keycloak Overview — Authentication flow priority reference