Wemap — Microsoft Entra ID integration guide (SSO + SCIM)
0. Overview
| SSO | SCIM | |
|---|---|---|
| Protocol | OpenID Connect, authorization code + PKCE, confidential client | SCIM 2.0 (RFC 7643 / 7644) |
| Direction | Browser → Wemap → Entra ID | Entra ID → Wemap (push) |
| Entra blade | Authentication / Certificates & secrets / API permissions | Provisioning |
| Purpose | Authentication only | Account lifecycle: create, update, deactivate, groups |
Both can be configured on a single Entra application (see 1.2).
Accounts are created exclusively by SCIM; SSO only authenticates. A user who is not provisioned, or whose account is deactivated, is refused at login.
Responsibilities
Wemap provides
- A
provider keyidentifying your integration, assigned by Wemap (e.g.your-organization) — used in all URLs below and noted<provider-key>. - The OIDC redirect (callback) URL to register in Entra ID.
- The SCIM base URL and the SCIM bearer token (generated by Wemap, delivered over a secure channel, rotatable on request).
- Configuration of your tenant parameters, optional restrictions (allowed email domains, MFA requirement) and validation of the end-to-end flow with you.
You provide
- An Entra ID tenant with the licensing required for automatic user provisioning and, if MFA enforcement is wanted, Conditional Access.
- An app registration: tenant ID, application (client) ID, client secret (+ its expiry date).
- Attribute mapping and user/group assignment on the Entra side.
Tenant ID, client ID, client secret, allowed domains and MFA requirement are applied on the Wemap side: plan a lead time with your Wemap contact for any change to them.
1. SSO — OpenID Connect with Entra ID
1.1 Flow and endpoints
- Flow: authorization code with PKCE, executed server-side by Wemap (confidential client). The code exchange and all token validation happen on Wemap's backend; no secret is exposed to the browser.
- Token validation: signature (RS256) against your tenant JWKS,
iss,audand the validity period verified. Thenonceis generated per sign-in request and checked on the returned ID token during the code exchange, which is what blocks token replay. Discovery and JWKS are read from your tenant's standard OIDC metadata. - Entry point: SP-initiated only. Users start from the Wemap front-end (or from a link/tile pointing at the Wemap login URL). IdP-initiated sign-on from the Entra "My Apps" portal is not supported as such — configure the tile to point to the Wemap login URL instead.
- Session: on success, Wemap issues its own API tokens (access token valid 1 hour, refresh token 7 days). Wemap does not maintain a session with Entra beyond login, and does not call Microsoft Graph.
- Login URL:
https://api.getwemap.com/v3.0/sso/<provider-key>/login/(accepts an optionallogin_hintforwarded to the Entra login page). - Logout URL:
https://api.getwemap.com/v3.0/sso/<provider-key>/logout/— RP-initiated logout. Terminates the Wemap session, then redirects the user to the Entraend_session_endpointwithid_token_hintandpost_logout_redirect_uri, and back to Wemap. - Back-channel logout URL:
https://api.getwemap.com/v3.0/sso/<provider-key>/backchannel-logout/— OIDC Back-Channel Logout 1.0 receiver, for sessions ended outside the browser (admin-forced sign-out, Conditional Access, no user agent present). Entra POSTs a signedlogout_token; Wemap verifies its signature and required claims, then revokes every access and refresh token it issued for the matching user and provider. Per the specification the endpoint always answers200withCache-Control: no-store, including for unknown or replayed tokens, so it never reveals whether a session existed.
1.2 Setup in Entra ID
-
Create the application — Entra admin center → Enterprise applications → New application → Create your own application → Integrate any other application you don't find in the gallery (Non-gallery). Steps 2 to 5 are then done on the same application, under App registrations. Send Wemap the two identifiers from App registrations → [your app] → Overview: Application (client) ID and Directory (tenant) ID.
-
Redirect URI — Manage → Authentication → Add a platform → Web, then under Redirect URIs add the callback provided by Wemap:
https://api.getwemap.com/v3.0/sso/<provider-key>/oidc-callback/Add the Wemap front-end as a second redirect URI:
https://api.getwemap.com/v3.0/sso/<provider-key>/logout/Entra requires the post-logout redirect target to be a registered redirect URI, otherwise logout will not return the user to Wemap.
To let Entra notify Wemap when a session ends outside the browser, register the logout notification URL on the same page:
https://api.getwemap.com/v3.0/sso/<provider-key>/backchannel-logout/ -
Client secret — Certificates & secrets → New client secret. Send the value to Wemap over a secure channel with its expiry date. Certificate-based client authentication is not supported; a client secret is required.
-
API permissions — Add a permission → Microsoft Graph → Delegated permissions:
openid,profile,email,offline_access. These four are what Wemap requests at every login. No application permission and no Graph API role is needed. Two ways to consent to them, your choice:- Admin consent — Grant admin consent authorizes them once for the whole tenant, and no user is ever prompted.
- User consent — each user gets an Accept/Cancel screen at first sign-in. Unavailable if your tenant policy disallows user consent, in which case sign-in fails until an admin consents.
-
Token configuration — Add optional claim → ID token →
emailandupn. -
Assignment — on the Enterprise applications side of the same application. Set Manage → Properties → Assignment required? to Yes to restrict sign-in to assigned users, then list them under Manage → Users and groups → Add user/group. Entra assignment is your access control layer; Wemap grants access to every provisioned user.
1.3 Identity mapping
Wemap derives an identity from the ID token claims:
| Wemap field | Source claim | Rule |
|---|---|---|
login (account key) | upn, else preferred_username, else email | The join key. The UPN is taken as-is. |
email | email, else derived from the UPN | |
| Anchor | oid | Entra's immutable per-user GUID, matched against the SCIM externalId. |
| First name | given_name | |
| MFA check | acr, amr | See 1.4 |
If email and given_name are both absent from the token, Wemap falls back to
the OIDC UserInfo endpoint once. If no email can be resolved at all, login
fails.
1.4 Optional restrictions
Both are configured by Wemap at your request, per organization.
- Allowed email domains — an empty list (the default) allows every domain.
When set, a login whose email domain is outside the list is refused
(
domain_not_allowed), and the same check applies to SCIM writes: creating or updating a user with an address outside the list is rejected with400, visible in the Entra provisioning logs. Guests are checked on their home domain, so provisioning guests requires their partner domains to be listed (or the list to be left empty). - Authentication strength / MFA — Wemap can require an MFA signal in the
token (
acroramr, e.g.amrcontainingmfa). This is a verification of what Entra asserts: you must still enforce MFA with a Conditional Access policy on the application. Without such a policy the claim is absent and every login is refused (insufficient_auth_strength).
1.5 Error codes
Failures return the user to the Wemap front-end with an error code. Use these when reporting an issue:
| Code | Meaning |
|---|---|
user_not_provisioned | No active provisioned account matches login. Check the Entra assignment and the provisioning run. |
domain_not_allowed | Email domain outside the configured list. |
insufficient_auth_strength | Required MFA claim missing or not matching. |
invalid_state | Login attempt expired (5 min) or replayed. Restart the login. |
token_exchange_failed | Code exchange with Entra failed — most often an expired client secret or a redirect URI mismatch. |
Missing email claim | No email resolvable from token or UserInfo. |
1.6 SSO limitations
- No IdP-initiated sign-on. Entra-initiated logout is supported, through the back-channel logout receiver in 1.1.
- No group or role claims are consumed at login. Where a status is mapped to a directory group it still comes from your directory, through SCIM provisioning rather than the token (see 3); anything unmapped stays managed in Wemap.
2. SCIM 2.0 — user and group provisioning
2.1 Endpoint and authentication
| Tenant URL (Entra field) | https://api.getwemap.com/v3.0/scim/v2 |
| Secret token (Entra field) | Bearer token supplied by Wemap |
| Content type | application/scim+json |
| Transport | HTTPS only |
The bearer token is a static, long-lived secret bound to your Wemap organization. It is the only credential: treat it as a password, store it in Entra only, and ask Wemap for a rotation if it is ever exposed.
An invalid or missing token returns 401. A valid token whose organization is
inactive returns 503 (retriable — Entra will re-try on the next cycle).
2.2 Supported resources and operations
| Resource | Operations |
|---|---|
/Users | GET (list, filter, by id), POST, PUT, PATCH, DELETE |
/Users/.search | POST |
/Groups | GET (list, filter, by id), POST, PUT, PATCH, DELETE |
/Groups/.search | POST |
/ServiceProviderConfig, /ResourceTypes, /Schemas | GET (discovery) |
/Me, /Bulk, top-level /.search | Not implemented (501) |
Filterable attributes — the standard SCIM operators apply to them:
- Users:
userName,externalId,name.givenName,active - Groups:
displayName,externalId
PATCH supports add, replace and remove (remove on group members) on
the attributes listed in 2.4. Any other path returns a SCIM
NotImplemented error rather than silently succeeding.
Discovery. The three discovery endpoints describe this service, so a client may configure itself from them:
/Schemaspublishes User and Group cut down to what Wemap consumes and returns (2.4), so nothing it advertises is a path that would later be rejected. The enterprise extension is not published./ServiceProviderConfigreports the capabilities this service has:patchandfiltersupported,sort,bulk,etagandchangePasswordnot.authenticationSchemesis the bearer token of 2.1./ResourceTypeslists the User and Group resource types and their endpoints.
2.3 Setup in Entra ID
- Open the application created in 1.2: Enterprise applications → [your app].
- Provisioning → Get started → Mode: Automatic.
- Admin Credentials: Tenant URL and Secret Token from 2.1 → Test Connection → Save.
- Mappings: enable Provision Azure Active Directory Users and
Provision Azure Active Directory Groups, then align the attribute
mappings with the table in 2.4. Remove mappings for attributes Wemap does
not consume: they make subsequent updates of the user fail (
501). - Scope: either Sync only assigned users and groups — then assign them under Users and groups — or Sync all users and groups. Choose the former if you use the group-based policy (3), so that the groups granting a status are provisioned.
- Validate the setup on one pilot user with Provision on demand: Entra reports the outcome of each step directly.
- Start provisioning — Provisioning → Overview → Start provisioning, which sets Provisioning Status to On. The initial cycle walks the full scope; subsequent incremental cycles run roughly every 40 minutes (Microsoft's schedule, not configurable by Wemap).
2.4 Attribute mapping
Users
| Entra source (recommended) | SCIM attribute | Wemap | Notes |
|---|---|---|---|
userPrincipalName (or originalUserPrincipalName) | userName | account key (login) | Required. Must equal the value SSO derives (see 1.3). Globally unique, max 254 chars. |
objectId | externalId | scim_external_id | Strongly recommended: immutable anchor, used to match a user whose userName changed on one side only. Entra's default row sources mailNickname — change it to objectId. |
Coalesce([mail], [userPrincipalName]) (fallback userPrincipalName) | emails[type eq "work"].value, primary: true | Max 254 chars. Must be a valid email; subject to the allowed-domain restriction. | |
givenName | name.givenName | first name | Max 50 chars. If absent, Wemap falls back to displayName, then to the part of userName before @. |
Switch(accountEnabled, ...) | active | enabled / disabled | String forms ("True"/"False") are accepted. |
Not consumed for now: familyName (Wemap stores a single given-name field), phone
numbers, addresses, manager, department, roles, and every
enterprise-extension attribute. Organization statuses come from group
membership instead — see 3.
Read-only in responses: id, meta, groups (a user's group memberships),
displayName.
Guest (B2B) accounts — use userName ← originalUserPrincipalName mapping to allow a guest to be provisioned under its external form,
alice_partner.com#EXT#@contoso.onmicrosoft.com.
Groups
| Entra source | SCIM attribute | Wemap |
|---|---|---|
displayName | displayName | group name (unique per organization). Required for the group-based policy — see 3. |
objectId | externalId | scim_external_id |
| members | members[].value | membership (Wemap user ids) |
2.5 Lifecycle semantics
- Create (
POST /Users) — creates the Wemap account and attaches it to your organization. AuserNamealready taken on the platform returns409(see 2.6). - Update (
PUT/PATCH) — applies the mapped attributes. ChanginguserNamerenames the account key; the same value must then be produced at SSO login. - Disable (
active: false) — the account is deactivated. Login is refused (user_not_provisioned), content and ownership are preserved. Settingactive: trueagain restores access. - Unassign / delete in Entra (
DELETE /Users/{id}) — treated as a deactivation. Wemap accounts own content (maps, points of interest, media); we do not destroy them on an IdP event. Actual deletion is a separate, explicit request to Wemap. - Group delete (
DELETE /Groups/{id}) — removes the group only. Its members keep their Wemap accounts and their membership in the organization. - Group membership — a member must be an active user of your organization;
otherwise the operation returns
400. Deactivated users are silently skipped when added.
2.6 SCIM limitations
- No nested groups. Group members must be users. Entra flattens assignments, so nest-heavy models must be expressed as direct assignments.
- No
familyName, no custom/extension attributes. - Filtering only on the attributes listed in 2.2 — a filter on any other attribute matches nothing rather than erroring.
- Group
displayNameis unique per organization: two Entra groups with the same name cannot both be provisioned (409).
3. User group-based policy
3.1 Statuses in a Wemap organization
| Status | Rights |
|---|---|
| Admin | Administers the organization — members and settings — plus everything a contributor can do. |
| Contributor | Access to the Wemap platform: creates and edits content (maps, points of interest, media). No organization administration. |
| Standard | Member of the organization, no platform access. Read-only access to livemaps. |
A provisioned user is standard unless a group grants otherwise. Wemap does not read Entra groups at login: the status is set by SCIM provisioning only.
3.2 How the status is assigned
Group membership. Wemap holds, per organization, the list of group names granting each status; you provision those groups and manage their members in your directory as usual.
Group names are matched case-insensitively. Wemap reads only the group names you declare and only the statuses you map, so other provisioned groups carry no privilege, and a status left out of the mapping stays managed in Wemap.
Membership is authoritative and evaluated on every sync: joining a mapped group grants the status, leaving it takes the status away, and a user may sit in several mapped groups to hold several statuses at once.
A mapped status is therefore owned by your directory. Manage it through group membership alone: a status granted inside Wemap on a user whose groups do not grant it is replaced at that user's next synchronisation. Ask Wemap to leave a status out of the mapping if you would rather keep it under your Wemap administrators' control.
3.3 Setup in Entra ID
Give Wemap the names of groups you already use, or — preferred — create groups dedicated to Wemap, so that a Wemap status is never a side effect of an unrelated entitlement.
- Create the groups — Entra ID → Groups → New group. Create one per
status, e.g.
Wemap AdminsandWemap Contributors. Security groups, assigned membership. - Assign them to the application — Enterprise applications → [your app] → Users and groups → Add user/group. This puts them in the provisioning scope. Members of a group assigned this way are provisioned as users too.
- Enable group provisioning — Provisioning → Attribute mapping: keep
Provision Microsoft Entra ID Groups enabled, with
displayNamemapped (see 2.4). Wemap matches on the group'sdisplayName. - Set the scope — Provisioning → Settings → Scope: Sync only assigned users and groups.
- Send Wemap the group names (
Wemap Admins,Wemap Contributors).
Validate before rolling out: add one user to a mapped group, run Provision on demand on the group, then check the status with your Wemap contact.
3.4 Rules to respect
- Manage a status by group membership. Adding a user to a mapped group grants the status on the next sync; removing them takes it away. Nothing else on the user needs to change.
- Provision the groups themselves, not only their members: Wemap needs the Group resources to know which group a member belongs to.
- Keep the group names stable, and tell Wemap before renaming one — the mapping is keyed on the name. A renamed group stops granting its status on the next sync.
- Keep the scope on Sync only assigned users and groups.
- Allow a lead time for any change to the mapping itself: it is applied in the Wemap configuration of your organization.
4. Operations
- Client secret expiry — SSO stops working the moment the secret expires
(
token_exchange_failedfor every user). Send the new secret to Wemap before the expiry date; the change requires a configuration update on our side. - SCIM token — Ask Wemap for a rotation on compromise or on your own key-rotation policy.
- Diagnostics you own — Entra Provisioning logs (per-user SCIM request
and response) and Sign-in logs (token issuance, Conditional Access
outcome). When escalating to Wemap, include the SCIM
userName/objectId, the timestamp, and the Wemap error code from 1.5.