Skip to main content

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

  1. Sign in to the Entra admin center.
  2. Identity → Applications → App registrations → New registration.
  3. Name: AlphaSwarm.
  4. 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).
  5. Redirect URI — add two:
    • Platform: Webhttps://<prod-host>/auth/callback
    • Platform: Single-page application (SPA)http://localhost:3001/auth/callback and the prod equivalent.

2. Generate a client secret

  1. App registration → Certificates & secrets → New client secret.
  2. Description: alphaswarm-backend-secret. Expiry: max allowed (24 months).
  3. Copy the secret value immediately; Entra hides it after page reload.
  4. Set:
    ALPHASWARM_MSAL_CLIENT_SECRET=<paste value>
    Or store it in your secret backend and reference via CredentialResolver (preferred — see alphaswarm_docs/cloud-credentials.md).

3. Define app roles

App registration → App roles → Create app role (five times):

Display nameMember typesValue
AlphaSwarm adminUsers / Appsalphaswarm.admin
AlphaSwarm editorUsersalphaswarm.editor
AlphaSwarm viewerUsersalphaswarm.viewer
Terraform operatorUsersalphaswarm.terraform.operator
Terraform approverUsersalphaswarm.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 the client_credentials grant 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

Two paths:

  1. Frontend wizard (recommended): navigate to /admin/onboardingLink Entra tenant tab, select your AlphaSwarm org, paste the Entra tenant id (tid), set primary domain + allowed email domains + role mapping, click "Activate".
  2. 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

SymptomLikely cause
AADSTS50194 invalid issuerAuthority pinned to wrong tenant — use /organizations for multi-tenant.
AADSTS65001 consent requiredAdmin consent on the SPA / API scope wasn't granted.
provision_user_from_claims returns default userSettings has auth_provider != "msal_entra". Set the env var.
New user lands without org membershipEntraTenantLink.status == "pending" — promote via the wizard.