Documentation
Accounts and authentication
Multi-account, multi-tenant sign-in with MSAL, token routing and tenant enumeration.
Goals
- Users sign in with their own user identities (delegated permissions). There are no service principals and no stored secrets.
- Multiple accounts can be signed in at once. One account can reach one or many tenants. Workspaces are reachable through:
- the account's home tenant,
- Azure Lighthouse delegations, which appear as subscriptions visible from the home tenant,
- B2B guest memberships in other tenants.
- Queries run with the right account and the right tenant authority automatically.
Auth provider modes (setting auth.provider)
builtin(default): MSAL public client using the Raml KQL multi-tenant app registration. The client ID is injected at build time fromRAML_KQL_CLIENT_ID. Builds without a client ID fall back tocustommode. See Registering your own Entra app.custom: same MSAL flow with a user-supplied client ID (and optional authority host for sovereign clouds). This is for organisations that won't consent to a third-party app and register their own.azureCli: tokens viaaz account get-access-token --tenant <id> --resource <res>. This requires Azure CLI installed andaz logindone. Accounts then mirroraz account list. It is useful where app consent is impossible. "Sign in" for a tenant that needs it runsaz login --tenant <id>, which opens the system browser (D-035).
The provider is chosen per account, so a user can mix a builtin account and an azureCli account.
MSAL setup (builtin / custom)
@azure/msal-nodePublicClientApplication,authority: https://login.microsoftonline.com/organizations.- Client capabilities:
["cp1"](CAE-ready). - Token cache:
@azure/msal-node-extensionspersistence plugin (Keychain on macOS, DPAPI on Windows, libsecret on Linux).- If libsecret is unavailable on Linux, show a clear error with install instructions.
- Do not silently fall back to plaintext. Offer an explicit opt-in "session-only (no persistent sign-in)" mode instead.
- Interactive sign-in:
acquireTokenInteractivewith loopback redirect (http://localhost) and the system browser (shell.openExternal). Never use an embedded webview for login. - Fallback flow: device code, available from the "Add account" dropdown for environments where the loopback redirect is blocked.
- Scopes:
- ARM:
https://management.azure.com/user_impersonation - Log Analytics:
https://api.loganalytics.io/Data.Read - Sign-in:
openid profile offline_access
- ARM:
- Request ARM at sign-in. Acquire Log Analytics tokens silently afterwards (incremental consent).
Token routing
AuthService.getToken({ accountId, tenantId, resource }): Promise<AccessToken>
- Authority =
https://login.microsoftonline.com/{tenantId}. - Try
acquireTokenSilent({ account, authority, scopes }). - On
InteractionRequiredAuthError(MFA, Conditional Access, consent), do not pop a browser in the middle of a fan-out:- Mark that
(account, tenant)asneedsReauth. - Fail the affected workspaces with
AUTH_INTERACTION_REQUIRED. - Show a single aggregated notification: "2 tenants need you to sign in again — Sign in".
- The Sign in action runs interactive auth for those tenants sequentially, then offers "Re-run failed".
- Mark that
- Cache tokens per
(account, tenant, resource)in memory. Refresh 5 min before expiry. Coalesce concurrent requests for the same key into one promise. - Lighthouse: delegated subscriptions are accessed with a token from the account's home/managing tenant. The discovery result records which tenant authority to use for each workspace (spec 03), so the engine never guesses.
Tenant enumeration per account
GET https://management.azure.com/tenants?api-version=2022-12-01with a home-tenant token. This returns tenants the account is a member or guest of, withdisplayNameanddefaultDomainwhere available.- For each listed tenant, try a silent ARM token for that tenant.
- On failure: mark the tenant
needsReauth(show it greyed out with "Sign in to this tenant"). - Tenants where the user has no Azure access at all are hidden by default. Setting:
accounts.showTenantsWithoutAccess.
- On failure: mark the tenant
- Lighthouse customer tenants are not in
/tenants. They come from discovery (spec 03) via subscriptiontenantId.
Accounts UI
- Activity bar bottom: an "Accounts" icon like VS Code, with a badge when an account needs re-auth.
- The Accounts view lists each account (UPN, provider, home tenant) and, under it, its tenants with state: OK / needs sign-in / no access.
- Actions:
- Add account, with a provider picker: Microsoft sign-in (browser), device code, Azure CLI, custom client ID.
- Sign out (removes the account from the MSAL cache), re-authenticate, and refresh.
- Set a display label for the account ("Work – Contoso", "Fabrikam guest").
- The label and ordering are stored in
accounts.jsonc(no secrets; spec 09).
Security rules
- Tokens never leave the main process and are never logged. The redacting logger strips
Authorizationheaders and any JWT-shaped string. - Sign-out clears the in-memory token cache for that account immediately.
- No refresh tokens or accounts are written anywhere except the MSAL encrypted cache.
Sovereign clouds (design for, don't build yet)
Keep the authority host and resource endpoints in a CloudProfile object (public only for now). Do not hard-code URLs in call sites.
Tests
- Unit: routing decisions, request coalescing,
needsReauthaggregation. Uses a fake MSAL client. - Integration: discovery + auth against the fake Azure server in demo mode.
- Manual live test (needs real tenants): sign in with two accounts, verify the Lighthouse workspace listing, verify the guest-tenant re-auth prompt.