Microsoft Entra ID Setup
This guide walks through setting up Microsoft Entra ID (formerly Azure AD) as the identity provider for both OIDC login and SCIM user provisioning in Open Saasframe.
Entra ID Free is included with any Azure subscription — no paid license required for basic OIDC + SCIM.
1. Create an Azure Account + Entra ID Tenant
- Go to https://azure.microsoft.com/free and create a free account (or use an existing one)
- Navigate to https://entra.microsoft.com (the Entra admin center)
- You'll have a default tenant — note your Tenant ID from Overview -> Tenant ID
2. Create Test Users
- In the Entra admin center, go to Identity -> Users -> All users
- Click + New user -> Create new user
- Fill in:
- User principal name: e.g.,
[email protected] - Display name: e.g.,
Test User - First name / Last name
- Password: auto-generate or set manually
- User principal name: e.g.,
- Click Create
- Repeat for 2-3 test users
3. Register the OIDC Application (SSO Login)
- In the Entra admin center, go to Identity -> Applications -> App registrations
- Click + New registration
- Configure:
| Field | Value |
|---|---|
| Name | Open Saasframe |
| Supported account types | Accounts in this organizational directory only (Single tenant) |
| Redirect URI | Platform: Web, URI: http://localhost:3000/api/sso/callback/oidc |
- Click Register
- You'll land on the app's Overview page — note:
- Application (client) ID — this is your Client ID
- Directory (tenant) ID — used in the issuer URL
Create a Client Secret
- Go to Certificates & secrets -> Client secrets tab
- Click + New client secret
- Description:
Open Saasframe Dev, Expiry:6 months(or your preference) - Click Add
- Copy the secret Value immediately — it's shown only once
OIDC Credentials Summary
| Credential | Where to find it | Value |
|---|---|---|
| Issuer URL | Computed from Tenant ID | https://login.microsoftonline.com/{tenant-id}/v2.0 |
| Client ID | App registration -> Overview | Copy from portal |
| Client Secret | App registration -> Certificates & secrets | Copy the Value (not Secret ID) |
| Redirect URI | You configured this | http://localhost:3000/api/sso/callback/oidc |
Configure Token Claims
By default, Entra ID v2.0 tokens may not include email in the ID token. Fix this:
- Go to your App registration -> Token configuration
- Click + Add optional claim
- Token type: ID
- Check:
email,given_name,family_name - Click Add
- When prompted about Microsoft Graph permissions, check the box and click Add
API Permissions
- Go to API permissions
- Verify these are present (they should be by default):
Microsoft Graph->openid(Delegated)Microsoft Graph->profile(Delegated)Microsoft Graph->email(Delegated)
- If any are missing, click + Add a permission -> Microsoft Graph -> Delegated permissions -> search and add them
- Click Grant admin consent for [your tenant] (green checkmark button)
Assign Users to the Application
- Go to Identity -> Applications -> Enterprise applications
- Find and click Open Saasframe
- Go to Users and groups -> + Add user/group
- Select your test users (or a group containing them)
- Click Assign
If "Assignment required?" is set to Yes (under Properties), only assigned users can log in. Set to No for dev if you want all tenant users to access it.
4. Create the SSO Config in Open Saasframe
- Log into Open Saasframe as admin
- Go to Settings -> Single Sign-On -> Create New
- Select OIDC as the protocol
- Enter:
- Name:
Entra ID - Issuer URL:
https://login.microsoftonline.com/{your-tenant-id}/v2.0 - Client ID: (paste from Entra)
- Client Secret: (paste the secret Value from Entra)
- Name:
- Add allowed email domains (e.g.,
yourtenant.onmicrosoft.com) - Test the connection (Verify Discovery)
- Activate the config
Verify OIDC Login
- Open a private/incognito browser window
- Go to the Open Saasframe login page
- Enter an email address belonging to one of your test users (e.g.,
[email protected]) - The HRD check should detect SSO and redirect to Microsoft login
- Authenticate at Microsoft
- You should be redirected back to Open Saasframe and logged in
5. Configure SCIM Provisioning
You need a SCIM bearer token from Open Saasframe. Generate one via the admin UI (SSO config -> Provisioning tab -> Generate Token) or the API (POST /api/sso/scim/tokens with the SSO config ID).
Set Up Provisioning in Entra ID
- Go to Identity -> Applications -> Enterprise applications
- Find and click Open Saasframe
- Go to Provisioning -> click Get started
- Set Provisioning Mode to Automatic
- In Admin Credentials:
| Field | Value |
|---|---|
| Tenant URL | http://localhost:3000/api/sso/scim/v2 (dev) or https://<your-domain>/api/sso/scim/v2 (prod) |
| Secret Token | Paste the SCIM bearer token from Open Saasframe |
- Click Test Connection — should show "The supplied credentials are authorized to enable provisioning"
- Click Save
Configure Attribute Mappings
- Under Mappings, click Provision Microsoft Entra ID Users
- Verify these mappings exist:
| Entra ID Attribute | SCIM Attribute | Notes |
|---|---|---|
userPrincipalName | userName | Required |
Switch([IsSoftDeleted]...) | active | Required — Entra uses a Switch expression |
givenName | name.givenName | Required |
surname | name.familyName | Required |
mail | emails[type eq "work"].value | Required — user's email |
displayName | displayName | Optional |
objectId | externalId | Required — Entra's unique ID |
- Keep default mappings — they should work out of the box
- Click Save
Start Provisioning
- Back on the Provisioning page, set Provisioning Status to On
- Click Save
- Entra will run an initial provisioning cycle (may take up to 40 minutes for the first cycle)
- Check Provisioning logs for results
Provisioning Cycle Timing
- Initial cycle: Processes all users in scope. Can take 20-40 minutes.
- Incremental cycles: Every 40 minutes, processes changes since last cycle.
- On-demand provisioning: Click Provision on demand to immediately provision a specific user (useful for testing).
6. Test the Full Flow
Test SCIM Provisioning
- In Entra, go to Enterprise applications -> Open Saasframe -> Provisioning
- Click Provision on demand
- Search for a test user and click Provision
- Expected: Entra sends
POST /Usersto your SCIM endpoint -> user appears in Open Saasframe - Check the provisioning log in Open Saasframe admin UI
Test User Update
- In Entra, go to Users -> edit a test user's display name
- Wait for the next provisioning cycle (or use Provision on demand)
- Expected: Entra sends
PATCH /Users/{id}-> user's name updated in Open Saasframe
Test User Deactivation
- In Entra, either:
- Delete the user (soft-delete moves to Deleted users)
- Block sign-in for the user (Users -> select user -> Edit properties -> Block sign in: Yes)
- Remove the user from the application assignment
- Expected: Entra sends
PATCH /Users/{id}withactive: false-> user deactivated in Open Saasframe, all sessions revoked
Test OIDC + SCIM Together
- Create a new user in Entra and assign them to the Open Saasframe Enterprise app
- Provision on demand (or wait for cycle)
- Verify the user exists in Open Saasframe (pre-provisioned, no login needed)
- Log in as that user via OIDC (Open Saasframe login -> redirect to Microsoft -> authenticate -> redirect back)
- Expected: The SCIM-provisioned account is used (no JIT provisioning,
provisioningMethodstaysscim) - Block sign-in for the user in Entra
- Expected: SCIM deactivates the user -> existing sessions revoked -> OIDC login no longer works
Entra ID SCIM Quirks
When building the SCIM endpoint, account for these Entra-specific behaviors:
| Quirk | Description | How to handle |
|---|---|---|
PascalCase op in PATCH | Entra sends "op": "Replace" instead of "op": "replace" | Case-insensitive comparison on PATCH operations |
| String booleans | active may be sent as "True" / "False" strings | Parse with parseBooleanToken |
| Non-standard PATCH paths | Sometimes sends emails[type eq "work"].value in PATCH path | Support bracket-notation in PATCH path parser |
| Mixed-case filter operators | Sends Eq instead of eq in filters | Case-insensitive filter parsing |
externalId mapping | Maps objectId -> externalId by default | Always store externalId from SCIM requests |
| Soft delete | Uses IsSoftDeleted Switch expression -> active: false | Handle as user deactivation |
Troubleshooting
OIDC login redirects but fails
- Verify the Redirect URI in App Registration matches exactly:
http://localhost:3000/api/sso/callback/oidc - Check that the Issuer URL includes the tenant ID:
https://login.microsoftonline.com/{tenant-id}/v2.0 - Verify Client ID and Client Secret (the Value, not the Secret ID)
- Ensure
emailoptional claim is added to the ID token - Ensure API permissions have admin consent granted
"AADSTS50011: The redirect URI does not match"
The redirect URI in the authorization request doesn't match what's registered. Check:
APP_URLin.envmatches what you registered (e.g.,http://localhost:3000)- No trailing slash differences
- Protocol matches (http vs https)
Users not provisioning
- Check that users are assigned to the Enterprise application
- Check Provisioning logs in Entra for error details
- Verify the SCIM token is valid and not revoked
- For local dev, Entra needs to reach your server — use ngrok for SCIM (even though OIDC works with localhost)
SCIM "Test Connection" fails
- For local dev, Entra's provisioning service needs to reach your endpoint over the internet
- Use ngrok:
ngrok http 3000 - Set Tenant URL to:
https://<id>.ngrok-free.app/api/sso/scim/v2
OIDC redirect URIs can use localhost, but SCIM provisioning requires a publicly reachable URL.
email claim missing from ID token
- Go to App registration -> Token configuration -> + Add optional claim -> ID token -> check
email - Go to API permissions -> verify
emailpermission -> click Grant admin consent
Key Differences from JumpCloud
| Aspect | Entra ID | JumpCloud |
|---|---|---|
| Issuer URL | https://login.microsoftonline.com/{tenant-id}/v2.0 | https://oauth.id.jumpcloud.com/ |
| Redirect URI | Supports http://localhost for dev | Requires HTTPS |
| SCIM provisioning | Enterprise App -> Provisioning (automatic) | SSO App -> Identity Management (SCIM API) |
| Provisioning cycles | Every 40 minutes (or on-demand) | Near real-time |
| SCIM quirks | PascalCase ops, string booleans, mixed-case filters | Mostly spec-compliant |
| Free tier | Free with any Azure account | 10 users forever |