This map was written after v0.2.0 shipped — the release predates the parity map
itself, and a git tag is immutable — so it is a retrospective reading rather
than a document published at the time. It is drawn from the v0.2.0 tree:
its routes, the packages present, and the CI suites it ran.
The shape of v0.2.0: SCIM 2.0 landed here (both directions), and the PDP
compatibility suite grew to seven real engines. The stateful directory work
— consent grants, directory roles, the recycle bin, auth-methods — had not
landed yet, nor had MSAL instance discovery.
Row names deliberately match the current map so the
parity changelog can diff them.
How the emulator’s surface maps to real Entra ID (as documented at
learn.microsoft.com/entra ), and —
the point of this table — whether real work happens or just the API shape .
The design bet is that the durable, testable surface is protocol + real
cryptography + directory state , and those are done for real: real RS256 JWTs
that third-party validators accept, every OAuth2 grant a real MSAL speaks, real
WebAuthn ceremonies, a real SQLite directory. What is deliberately left out is
the policy engine — Conditional Access, MFA, Identity Protection — which is
what would turn a dev-loop emulator into an IdP.
“Real via our own wire-protocol implementation.” A row is 🟢 Real not
only when real cryptography does the work, but also when the emulator itself
implements Entra’s wire protocol and the logic behind it — so a real,
unmodified client (MSAL in five languages, the Graph SDK, a SCIM connector)
gets byte- and behaviour-identical responses.
Absent means 404, not 501
This emulator has no 501 stubs : a feature that isn’t implemented simply
has no route, so a client sees a 404 . A 🔴 below therefore means “absent”,
not “honestly refused”. Likewise 🟠 means “real when you attach a real engine”
— where a toy fallback ships instead, the row says so.
Meaning 🟢 Real Genuine work: real signed JWTs, real crypto, real state, real logic enforced — no pretending. 🟡 Emulated Faithful API contract + persisted state, but no engine — clock-derived or management-only. 🟠 Bring-your-own-engine Real when you attach a real external engine; a toy or companion stands in otherwise. 🔴 Not implemented Absent (404).
Entra feature Emulator Type RS256-signed access / ID tokens Real compact JWS over crypto/rsa — real 2048-bit keys, real signatures any validator accepts 🟢 Real kid / JWKSRFC 7638 JWK thumbprint; JWKS publishes active and retired-but-unexpired keys 🟢 Real Signing-key rotation Real: new active key, old retired but still served until it expires, mutex-guarded signer swap 🟢 Real Entra v2.0 claim shapes (tid/oid/azp/appid/scp/roles/ver/idtyp, pairwise sub) Full 🟢 Real amr (pwd / fido)Threaded from the actual grant used 🟢 Real wids (directory-role template GUIDs)— directory roles do not exist yet, so no wids are emitted 🔴 Not implemented Optional claims + group overage (_claim_names / _claim_sources) Real Entra overage payload above the limit; protocol claims non-overridable 🟢 Real Signing algorithms other than RS256 (ES256 / PS256) RS256 only — a deliberate, documented choice 🔴 Not implemented
Entra feature Emulator Type OIDC discovery + JWKS Full, conformance-tested against the real Entra discovery document 🟢 Real Instance discovery (/common/discovery/instance)— not served; MSAL must be configured to skip authority validation 🔴 Not implemented authorization_code + PKCE (S256/plain)Real, with atomic single-use code consumption 🟢 Real refresh_tokenReal rotation, plus family revocation on reuse — replaying a rotated token kills the whole chain 🟢 Real client_credentialsReal; .default only, tolerating the stray scopes MSAL-Go/azidentity send 🟢 Real password (ROPC)Real scrypt verification → amr:["pwd"] 🟢 Real urn:ietf:params:oauth:grant-type:jwt-bearer (on-behalf-of)Real; enforces assertion audience, rejects app-only assertions 🟢 Real Device code (spec form and the bare device_code msal-node sends) Real, with an atomic approve→mint step that closes the double-mint window 🟢 Real private_key_jwt client assertionImplemented — but discovery advertises only client_secret_post/client_secret_basic, so a spec-driven client never tries it 🟡 Emulated Front-channel logout /logout exists; frontchannel_logout_supported is not advertised🟡 Emulated Implicit / hybrid flow response_types_supported: ["code"] only🔴 Not implemented mTLS / PoP / certificate-bound tokens — 🔴 Not implemented PAR / JAR (request_uri)— 🔴 Not implemented CAE (continuous access evaluation)— 🔴 Not implemented Token-lifetime / claims-mapping policies — 🔴 Not implemented
Entra feature Emulator Type Directory reads (/me, users, groups, members, memberOf, userinfo) Full, over the real store 🟢 Real Directory writes (users, groups, applications; group membership $ref) Full CRUD, persisted 🟢 Real Recycle bin (directory/deletedItems, restore, permanent delete) — directory/deletedItems does not exist 🔴 Not implemented OAuth2 permission grants (consent) — no oauth2PermissionGrants; consent does not shape tokens 🔴 Not implemented Directory roles (roleManagement/directory) — no roleManagement/directory 🔴 Not implemented Authentication methods inventory (password / FIDO2) — no authentication/methods 🔴 Not implemented OData $select / $filter / $top / $skiptoken / $count Supported (single $filter clause) 🟢 Real Graph permission enforcement (scopes/roles gating operations)Any valid Graph-audience token authorizes any operation — documented, deliberate🔴 Not implemented Separate servicePrincipal store An app registration is its own SP; object id and appId are conflated 🟡 Emulated Custom role definitions Only the seeded built-ins exist 🔴 Not implemented Administrative units , custom security attributes — 🔴 Not implemented Graph beta endpoint v1.0 only 🔴 Not implemented Sign-in / audit logs (Graph auditLogs, signIns)— (the emulator’s own /admin/api/audit is a different, test-only thing) 🔴 Not implemented
Entra feature Emulator Type SCIM service provider (inbound): ServiceProviderConfig, ResourceTypes, Schemas; Users + Groups CRUD, PatchOp Real RFC 7643/7644 shapes over real HTTP, bearer static-secret auth as Entra does 🟢 Real SCIM provisioning client (outbound): filter-probe → create / update / active:false deprovision, member-correlated groups, incremental watermark Real — the emulator pushes the directory out using Entra’s actual sequence 🟢 Real Provisioning scheduler (the ~40-minute cycle) Admin-triggered instead of timed — deliberate, so tests are deterministic 🟡 Emulated PUT /Groups/{id}Users have PUT; Groups do not (GET/POST/PATCH/DELETE only) 🔴 Not implemented
Entra feature Emulator Type Passkey / WebAuthn sign-in Real ceremonies (real assertion verification, real CBOR/COSE); RP derived per-request from the Host, so passkeys work on any origin; drives amr:["fido"] 🟢 Real Attestation policy / AAGUID allowlists / cross-device CTAP Stated non-goals 🔴 Not implemented MFA / step-up authentication — 🔴 Not implemented Conditional Access (policies, named locations, auth strengths)— the line the project deliberately doesn’t cross 🔴 Not implemented Identity Protection / risky users — 🔴 Not implemented SSPR / password reset — 🔴 Not implemented SAML / WS-Federation — stated non-goal 🔴 Not implemented B2C user flows / External ID / CIAM — stated non-goal 🔴 Not implemented B2B guest invitations / cross-tenant access — 🔴 Not implemented
Entra feature Emulator Type Managed identity (/msi/token, App Service protocol)Real — azidentity’s ManagedIdentityCredential gets a real token 🟢 Real Fabric-audience tokens + workspace identity (app reg + SP + managed credential, state machine, cascade delete) Real at the token layer 🟢 Real Fabric control plane Out of scope by design — the companion fabric-emulator serves it 🟠 BYO-companion Workload identity federation (federatedIdentityCredential)— despite private_key_jwt being present 🔴 Not implemented Device registration / Intune compliance / device-bound tokens — 🔴 Not implemented Application Proxy — 🔴 Not implemented PIM / privileged access , entitlement management , access reviews — 🔴 Not implemented Group writeback / hybrid sync (AD Connect) — 🔴 Not implemented
Entra feature Emulator Type Externalized authorization (fine-grained, relationship-based) A PDP port : real engines attach — OpenFGA, SpiceDB, Keto, Permify, Casbin, OPA, Cedar, all exercised in CI. A ~50-line InMemoryPDP ships so the sample runs with nothing attached; it is explicitly not a real engine 🟠 BYO-engine Custom authentication extensions (token-issuance webhook callout) Real callout — you bring the endpoint 🟠 BYO-engine
Entra feature Emulator Type Persisted directory Real SQLite (WAL, foreign keys, forward-only migrations) 🟢 Real Credential hashing Real scrypt for passwords/secrets; SHA-256 for refresh/device codes 🟢 Real Concurrency contracts (single-use codes, refresh-reuse detection, device-code approve→mint) Real atomic SQL — not best-effort 🟢 Real Multi-tenant Multiple tenants exist and are isolated 🟡 Emulated TLS with a wildcard cert over the emulator’s origins Real self-signed X.509, regenerated on SAN drift, stable fingerprint otherwise 🟢 Real Cloud-instance metadata (tenant_region_scope, cloud_instance_name, sovereign clouds)— 🔴 Not implemented
Feature Purpose Token forge (/admin/api/tokens) Mint arbitrary claims, negative expiry, or a deliberately invalid signature — test your validator’s failure paths Clock control (/admin/api/clock) Freeze/advance — makes token expiry and the 30-day recycle bin deterministic Fault injection (/admin/api/faults) Forced token errors, latency, probabilistic flakiness Audit trail (/admin/api/audit) Every authorize/token exchange, for assertions Export / import Snapshot a directory; deliberately excludes signing keys and live grants Admin portal Inspect and drive the directory in a browser
The bar: real clients, unmodified . Two knobs make them work — instance
discovery disabled per-SDK, and TLS trust injected per-SDK.
Real client (pinned) Surface exercised Status @azure/msal-nodeclient_credentials, auth code + PKCE, refresh, device code; client_info account identity, nonce, ver:"2.0" 🟢 CI sdk-e2e @microsoft/microsoft-graph-client— the Graph SDK suite is not wired yet 🔴 Not wired MSAL Go + azidentity client_credentials, device code, ClientSecretCredential, ManagedIdentityCredential , embedded-library mode 🟢 CI sdk-e2e MSAL Python client_credentials, device code 🟢 CI sdk-e2e MSAL.NET (Microsoft.Identity.Client)client_credentials + token-cache hit, app-only claim shape 🟢 CI sdk-e2e msal4j (Java)client_credentials, with the emulator cert in a real trust store 🟢 CI sdk-e2e OpenFGA · SpiceDB · Keto · Permify · Casbin · OPA · Cedar The PDP port against real engines 🟢 CI pdp-compat (7-way matrix) Flutter (http, flutter_appauth) Device code on real Android/iOS 🟡 Nightly, not a PR gate; the auth-code leg is a manual screen @azure/msal-browser— 🔴 Not wired
The stated non-goals — SAML/WS-Fed, B2C user flows, MFA/Conditional Access,
production hardening — are a deliberate line. Crossing it changes the project’s
character from “the identity provider your tests run against” to “an identity
provider” , which is a different product with a different duty of care.
What that buys: everything above the line can be real , because none of it
needs a policy engine, a risk model, or a tenant’s compliance posture. A token
this emulator signs is a real token; a passkey it verifies is really verified.