Microsoft Entra ID (MSAL) setup
Step-by-step walkthrough for wiring AlphaSwarm's MsalEntraProvider to a
multi-tenant Microsoft Entra ID app registration. The provider lives
at alphaswarm/auth/providers/msal_entra.py
and auto-registers via the
IdentityProviderMeta metaclass.
1. Create the Entra app registration
- Sign in to the Entra admin center.
- Identity → Applications → App registrations → New registration.
- Name:
AlphaSwarm. - Supported account types: Accounts in any organizational
directory + personal Microsoft accounts (B2B/B2C). This is
what makes the app multi-tenant. The matching MSAL authority
becomes
https://login.microsoftonline.com/organizations(work / school accounts only) or/common(incl. personal accounts). - Redirect URI — add two:
- Platform: Web →
https://<prod-host>/auth/callback - Platform: Single-page application (SPA) →
http://localhost:3001/auth/callbackand the prod equivalent.
- Platform: Web →
2. Generate a client secret
- App registration → Certificates & secrets → New client secret.
- Description:
alphaswarm-backend-secret. Expiry: max allowed (24 months). - Copy the secret value immediately; Entra hides it after page reload.
- Set:
Or store it in your secret backend and reference via
ALPHASWARM_MSAL_CLIENT_SECRET=<paste value>CredentialResolver(preferred — see alphaswarm_docs/cloud-credentials.md).
3. Define app roles
App registration → App roles → Create app role (five times):
| Display name | Member types | Value |
|---|---|---|
| AlphaSwarm admin | Users / Apps | alphaswarm.admin |
| AlphaSwarm editor | Users | alphaswarm.editor |
| AlphaSwarm viewer | Users | alphaswarm.viewer |
| Terraform operator | Users | alphaswarm.terraform.operator |
| Terraform approver | Users | alphaswarm.terraform.approver |
The provider's first-login provisioning logic
(alphaswarm/auth/user.py::_apply_entra_tenant_link)
maps these onto the AlphaSwarm role lattice (viewer < editor < admin < owner). The alphaswarm.terraform.* sub-roles fold to editor (operator)
and admin (approver) by default; override via the
EntraTenantLink.role_mapping JSON column.
4. Expose an API scope
App registration → Expose an API → Add a scope:
- Application ID URI:
api://<app_id>(Entra suggests this; accept). - Scope name:
.default(this enables theclient_credentialsgrant used by M2M). - Admin consent display name:
AlphaSwarm API access.
5. (Optional) Pre-authorize the SPA client
If you split the SPA client into its own app registration, add it to
Expose an API → Authorized client applications with the
api://<app_id>/.default scope so the token flow lands without an
admin-consent prompt.
6. Configure AlphaSwarm
ALPHASWARM_AUTH_PROVIDER=msal_entra
ALPHASWARM_MSAL_TENANT_ID=<home tenant id (only for single-tenant mode)>
ALPHASWARM_MSAL_CLIENT_ID=<app id from Overview>
ALPHASWARM_MSAL_CLIENT_SECRET=<from step 2>
ALPHASWARM_MSAL_AUTHORITY=https://login.microsoftonline.com/organizations
ALPHASWARM_MSAL_REDIRECT_URI=https://<prod-host>/auth/callback
ALPHASWARM_MSAL_SCOPES=openid profile email offline_access User.Read
ALPHASWARM_MSAL_MULTI_TENANT=true
ALPHASWARM_MSAL_B2B_ENABLED=true
Frontend Vite build:
VITE_MSAL_TENANT_ID=<same as backend>
VITE_MSAL_CLIENT_ID=<same as backend>
VITE_MSAL_AUTHORITY=https://login.microsoftonline.com/organizations
VITE_MSAL_REDIRECT_URI=https://<prod-host>/auth/callback
VITE_MSAL_SCOPES=openid profile email offline_access User.Read
7. Link your home Entra tenant to an AlphaSwarm organization
Two paths:
- Frontend wizard (recommended): navigate to
/admin/onboarding→ Link Entra tenant tab, select your AlphaSwarm org, paste the Entra tenant id (tid), set primary domain + allowed email domains + role mapping, click "Activate". - MCP tool / API:
POST /tenancy/entra-links
{
"organization_id": "<wiley-tech org id>",
"entra_tenant_id": "<your tid>",
"primary_domain": "wiley.tech",
"allowed_email_domains": ["wiley.tech"],
"role_mapping": {
"alphaswarm.admin": "admin",
"alphaswarm.editor": "editor",
"alphaswarm.viewer": "viewer",
"alphaswarm.terraform.operator": "editor",
"alphaswarm.terraform.approver": "admin"
},
"activate": true
}
Once the link is active, every user that signs in from that tenant
gets a Membership row auto-provisioned on the linked org +
workspaces (provider == "msal_entra" in
alphaswarm/auth/user.py::provision_user_from_claims).
8. (Optional) Conditional Access for external tenants
For B2B guest users, configure Entra Conditional Access policies on your home tenant (MFA + IP restrictions + device compliance). AlphaSwarm does NOT enforce these — Entra denies the token before AlphaSwarm sees it, which is the correct boundary.
9. SCIM / Provisioning Service webhook
To pre-provision AlphaSwarm users before they sign in (useful for large orgs), point an Entra Logic App or SCIM provider at:
POST https://<prod-host>/_internal/msal/sync
Authorization: Bearer <m2m-token>
{
"object_id": "<user oid>",
"tenant_id": "<tid>",
"email": "user@wiley.tech",
"display_name": "User",
"app_roles": ["alphaswarm.editor", "alphaswarm.terraform.operator"],
"lifecycle_event": "created"
}
The endpoint is M2M-protected via require_m2m_token (mirrors
/_internal/auth0/sync) and upserts the matching User +
Membership rows so the user lands on a usable surface on their very
first request.
Troubleshooting
| Symptom | Likely cause |
|---|---|
AADSTS50194 invalid issuer | Authority pinned to wrong tenant — use /organizations for multi-tenant. |
AADSTS65001 consent required | Admin consent on the SPA / API scope wasn't granted. |
provision_user_from_claims returns default user | Settings has auth_provider != "msal_entra". Set the env var. |
| New user lands without org membership | EntraTenantLink.status == "pending" — promote via the wizard. |