This map was written after v0.2.1 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.1 tree:
its routes, the packages present, and the CI suites it ran.
Parity-identical to v0.2.0: the 16 commits in between were well-known Azure
resource audiences, docs and CI work — nothing that moves a row.
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.