SurePassID Identity Provider

EAM EntraId Test Client Setup Guide

Purpose: Walk through creating a test Microsoft Entra ID External Authentication Method (EAM) client, running the Entra sign-in that invokes SurePassID as the MFA provider, and — critically — capturing the exact values from Entra that must be entered into the SurePassID Eam.* configuration to complete wiring on the IdP side.

For OIDC follow the instructions from your Relying Party (vendor application documents). idp.surepassid.com is used as the default host, but in an non-cloud environment this will be the host the the SurePassID Identity Provider is installed on.


Roles recap

Role Who In this guide
OpenID Provider (OP) SurePassID IdP Exposes /eam/{domain}/*, issues the MFA id_token.
Relying Party (RP / client) Microsoft Entra ID Redirects the user to us, consumes the id_token.
Subject The Entra test user Completes SurePass MFA.

Entra ID is the client; SurePassID is the provider. EAM only satisfies the MFA (second factor) — Entra ID still does the first factor. This is a Microsoft limitation.


1. Prerequisites

On the SurePassID side (must exist before Entra config):

  • A tenant/partner with a domain (used in /eam/{domain}/...).
  • That partner has signing certificate** configured (SSOCert + SSOPassword).
  • The IdP is reachable over HTTPS at a public host, e.g. https://eam.surepassid.com.
  • The three SurePassID Identity Provider endpoints resolve for your SurePassID account (domain) (even before final config):
    • https://idp.surepassid.com/eam/{domain}/.well-known/openid-configuration
    • https://idp.surepassid.com/eam/{domain}/jwks
    • https://idp.surepassid.com/eam/{domain}/authorize

On the Entra side:

  • A test Entra ID tenant (do not pilot on production).
  • Roles: Authentication Policy Administrator (or Global Administrator) to create the EAM method, and Application Administrator to register the app.
  • One or more test users and a test security group to scope the method.

2. Entra ID setup process

Step 2.1 — Register the EAM application (App registration)

Entra Admin Center → Entra ID → App registrations → New registration.

  1. Name: SurePassID EAM (Test).
  2. Supported account types: Single tenant (this test tenant).
  3. Redirect URI: leave for now; the EAM feature supplies the platform redirect.
  4. Register.

🔑 CAPTURE — Application (client) ID. On the app Overview blade copy Application (client) ID (a GUID). This is entered as the App ID field of the External Authentication Method in Step 2.3. (It is not SurePassID Eam.ClientId — that is the separate free-form Client ID string you set in Step 2.3.)

🔑 CAPTURE — Directory (tenant) ID. Also on Overview, copy Directory (tenant) ID. You need it to build the Entra OIDC metadata URL (Eam.EntraOpenIdConfigurationUrl).

Step 2.2 — Note Entra's OpenID metadata URL

Entra's per-tenant OIDC discovery (used by SurePassID to validate Entra's signed request object):

https://login.microsoftonline.com/<Directory (tenant) ID>/v2.0/.well-known/openid-configuration

🔑 CAPTURE — Entra OpenID configuration URL → SurePassID Eam.EntraOpenIdConfigurationUrl. Optional: open it in a browser and confirm it returns JSON with a jwks_uri. SurePassID uses this to fetch Entra's public keys and verify the request JWT signature.

Step 2.3 — Create the External Authentication Method

In the Microsoft Entra admin center (https://entra.microsoft.com), External Authentication Methods are managed under the Authentication methods policy, not directly under the Entra ID overview. Navigate:

Entra admin center → Protection → Authentication methods → Policies → Add external method

Notes if you don't see the option:

  • The blade lives under Protection (older tenants showed it under Security). If you only see Entra ID → Overview, expand the left nav and open Protection (shield icon), then Authentication methods, then the Policies tab.
  • Direct link: https://entra.microsoft.com/#view/Microsoft_AAD_IAM/AuthenticationMethodsMenuBlade/~/AdminAuthMethods
  • Add external method only appears on the Policies tab (not on User registration details or Monitoring). If the tab shows only built-in methods (FIDO2, Passkey, etc.), scroll to the bottom of the methods list — "Add external method" is the last entry.
  • Requires an Entra ID P2 license (you have this) and that you sign in with a role that can edit the authentication methods policy — Authentication Policy Administrator or Global Administrator. If you are only a lower-privileged admin the method list renders read-only and the Add external method button is hidden.
  • External Authentication Methods is a relatively recent feature; ensure you are in the entra.microsoft.com portal (not the legacy Azure AD blade in portal.azure.com), where the option may not be surfaced.

The Add external method form has exactly three fields. Here is where each comes from and how it maps to SurePassID config:

  1. Client ID — a free-form identifier string you choose for this method (for example entraid-your-company-identifier). This is not the app-registration GUID. Entra places this exact value in the client_id claim of the signed request object it sends to your authorize endpoint, and SurePassID validates it against Eam.{domain}.ClientId (falling back to Eam.ClientId). It also becomes the issued id_token aud. It must match Eam.ClientId exactly (case-sensitive).
  2. Discovery Endpoint — the SurePassID per-domain OIDC discovery URL (this points Entra at your IdP so it can fetch your JWKS and endpoints):
    https://saml2.surepassid.com/eam/<domain>/.well-known/openid-configuration
  3. App ID — the Application (client) ID GUID from the app registration in Step 2.1 (for example 7f3c9a12-4be8-4d61-9c02-1e5a8b7d6f40). This ties the EAM method to the Entra app registration you created; it is a different value from the Client ID string above.

⚠️ Do not confuse Client ID and App ID. They are two separate fields:

  • Client ID = the string you invent (e.g. entraid-your-company-identifier) → must equal SurePassID Eam.ClientId, is sent as the request object client_id, and becomes the token aud.
  • App ID = the app-registration GUID from Step 2.1 → links the method to the app registration.

Earlier revisions of this guide incorrectly said Eam.ClientId was the app-registration GUID. It is the Client ID string, not the GUID.

After filling the three fields, Save.

ℹ️ There is no redirect URI field on this form. The Add external method blade exposes only the three fields above (Client ID, Discovery Endpoint, App ID). Entra's EAM feature uses a fixed, Microsoft-controlled redirect_uri that you do not enter anywhere in the portal — so it is correct that you see no redirect field after saving.

ℹ️ redirect_uri now defaults automatically — no capture needed for the standard flow. Entra's EAM feature always uses the fixed, Microsoft-controlled value https://login.microsoftonline.com/common/federation/externalauthprovider. SurePassID uses this as the default allow-list entry, so you do not have to enter it. Only set Eam.RedirectUris (or Eam.{domain}.RedirectUris) to override the default — e.g. for a national cloud (US Gov, China) whose host differs (see the national-cloud table in §4 for the exact values). The value is still validated (see §4): a redirect_uri that matches neither the default nor the override is rejected, preserving open-redirect protection.

ℹ️ acr is now derived automatically — no capture needed for the standard flow. SurePassID computes the returned acr from the actual second factor (see §4a for the rule and matrix). For every SurePassID MFA method the derived value is possessionorinherence, which Entra accepts. Eam.Acr is therefore optional and only used as an override when a tenant's policy demands a different exact string (see §4a). Sending a bare possession is rejected (AADSTS5001257 / AADSTS5001258).

Step 2.4 — Enable & scope the method

  1. In the External method, set Enable = Yes.
  2. Target: add the test users/group only (not "All users").
  3. Save.

Step 2.5 — (If required) grant the app any consented permissions

For a minimal EAM id_token flow no Graph scopes are needed. If your tenant policy requires admin consent for the app, grant it under App registrations → API permissions → Grant admin consent.

Step 2.6 — About the OIDC scope parameter (no Azure config needed)

The EAM flow is an authentication flow that returns an id_token — it is not an OAuth access-token/resource flow, so there is nothing to configure for scopes on either side:

  • Inbound: Entra sends scope on the authorize request. SurePassID requires that it contains openid and rejects the request otherwise (400 — "The 'openid' scope is required."). Any extra scopes Entra includes (e.g. profile, email) are accepted but ignored.
  • Advertised: SurePassID's discovery document publishes "scopes_supported": ["openid"] only.
  • Returned: scope is not echoed back and no scp/scope claim is added to the minted id_token. There is nothing to "grant" or map — do not add Microsoft Graph or custom API scopes expecting them to flow through.

In short: leave scopes alone. openid is guaranteed by Entra's EAM feature; SurePassID validates it is present and ignores the rest.


3. Fields to capture → SurePassID EAM config

Config source note (UI-first). EAM configuration is now database-backed (EamConfig table, one row per partner). EamConfig resolves each value DB row first, then falls back to the web.config scoped key (Eam.{domain}.{Setting}) and finally the global key (Eam.{Setting}). Enter the captured values through the Admin UI → EAM Configuration screen (field labels from OIDC_Admin_UI_Spec.md §4); web.config keys remain supported only as a fallback for deployments not yet migrated. Direct SQL against EamConfig is an optional fallback only.

Enter in Admin UI → EAM Configuration (select the tenant / Partner Id), using the captured values:

UI field Column Source (Entra step) Notes
Enabled [Enabled] (operator toggle) Gates all eam/{domain}/* endpoints. Ships off.
Client Id (token aud) [Client Id] Step 2.3 Client ID field (free-form string you chose) NOT the app GUID. Must equal request-object client_id; becomes token aud.
Entra OpenID Config URL [Entra OpenId Configuration Url] Step 2.2 (built from tenant ID) Validates Entra's signed request object.
Redirect URIs [Redirect Uris] Optional override (see §4) Leave blank to use the built-in Microsoft EAM default; set only to override (e.g. national cloud).
ACR [Acr] Optional override (see §4a) Leave blank to use the derived value; set to override verbatim.
Client Secret [Client Secret] Optional shared secret Not part of the standard EAM contract; write-only in the UI.

Note: the Step 2.3 App ID field (= the Step 2.1 Application (client) ID GUID) is entered in Entra only to link the method to the app registration. SurePassID does not consume it, so it has no EAM config field.

Multi-tenant resolution. Every value is scoped per tenant {domain} (the same value in the eam/{domain}/... route and the account/domain entered on SSOLogin.aspx). The DB EamConfig row is keyed by Partner Id; when a value is absent it falls back to the web.config scoped key, then the global key. Use per-tenant rows/keys for ClientId (and ClientSecret); RedirectUris, Acr, and EntraOpenIdConfigurationUrl may be shared defaults where tenants agree.

⚠️ Resolution & enablement model — read before setting global defaults.

Precedence per key: EamConfig DB row (per Partner Id) → Eam.{domain}.{Setting} (web.config scoped) → Eam.{Setting} (web.config global) → built-in default. The DB row is the primary source; the web.config keys below are the fallback for un-migrated deployments. A blank value at one level falls back to the next level, not straight to the derived/built-in default. This matters most for Acr and RedirectUris:

  • Acr: Eam.{domain}.Acr → Eam.Acr → derived possessionorinherence. If a global Eam.Acr is set, it overrides the derived value for every tenant that doesn't set its own — so a stale global Eam.Acr (e.g. possessionorknowledge) will surface even when the tenant key is blank. Leave Eam.Acr unset unless you truly want a fixed acr for all tenants.
  • RedirectUris: Eam.{domain}.RedirectUris → Eam.RedirectUris → default Microsoft redirect. Same rule: a global value suppresses the built-in default.

Enablement: Eam.Enabled follows the same fallback. When EAM is disabled for a domain the endpoints return 404 before any other key is read, so global default values sit inert and safe until a domain resolves Enabled=true (scoped or global). This means global defaults are useful even when you can't authenticate against the global scope.

Recommended posture (multi-tenant):

  • Leave global Eam.Enabled false/unset and enable per tenant (Eam.{domain}.Enabled=true). A global Eam.Enabled=true turns EAM on for every resolvable partner domain.
  • ClientId (and ClientSecret) should normally be per-tenant — each Entra tenant assigns its own EAM app id, so a shared global ClientId causes client_id-mismatch rejections.
  • RedirectUris, Acr, and EntraOpenIdConfigurationUrl are safe/useful as global defaults (though EntraOpenIdConfigurationUrl is per-tenant whenever tenants differ).

Fallback: equivalent web.config keys. The Admin UI table in §3 is the primary path. The keys below map one-to-one to the same settings and are read only when no EamConfig DB row supplies the value — use them for deployments not yet migrated to the database (Release 2025.4).

SurePassID key Source (Entra step) Example / format Notes
Eam.Enabled / Eam.{domain}.Enabled (operator toggle) true Turns the endpoints on. Ships false.
Eam.ClientId / Eam.{domain}.ClientId Step 2.3 Client ID field (free-form string you chose) entraid-your-company-identifier NOT the app GUID. Must equal request-object client_id; becomes token aud.
(no SurePassID key) Step 2.3 App ID field = Step 2.1 Application (client) ID GUID 7f3c9a12-4be8-4d61-9c02-1e5a8b7d6f40 Entered in Entra only, to link the method to the app registration. SurePassID does not consume it.
Eam.RedirectUris / Eam.{domain}.RedirectUris Optional override (see §4) https://login.microsoftonline.com/common/federation/externalauthprovider Allow-list. Leave unset to use the built-in Microsoft EAM default. Set only to override (e.g. national cloud); comma/space separated for multiple; exact match.
Eam.Acr / Eam.{domain}.Acr Optional override (see §4a) possessionorinherence Leave unset to use the derived value. When set (non-blank) it overrides the derived acr verbatim.
Eam.EntraOpenIdConfigurationUrl / Eam.{domain}.EntraOpenIdConfigurationUrl Step 2.2 (built from tenant ID) https://login.microsoftonline.com/<tenantId>/v2.0/.well-known/openid-configuration Used to validate Entra's signed request object.

Example single-tenant block (global fallback keys):

<add key="Eam.Enabled" value="true" />
<add key="Eam.ClientId" value="entraid-your-company-identifier" />
<!-- Eam.RedirectUris is optional; omit it to use the default Microsoft EAM redirect. -->
<!-- Eam.Acr is optional; omit it to use the derived possessionorinherence value. -->
<add key="Eam.EntraOpenIdConfigurationUrl" value="https://login.microsoftonline.com/22222222-3333-4444-5555-666666666666/v2.0/.well-known/openid-configuration" />

Example per-tenant override for domain contoso (falls back to the global keys for anything omitted):

<add key="Eam.contoso.Enabled" value="true" />
<add key="Eam.contoso.ClientId" value="entraid-your-company-identifier" />
<!-- Eam.contoso.RedirectUris override only for a non-default redirect (e.g. national cloud). -->
<!-- Eam.contoso.Acr override only if this tenant's policy needs a specific acr string. -->
<add key="Eam.contoso.EntraOpenIdConfigurationUrl" value="https://login.microsoftonline.com/22222222-3333-4444-5555-666666666666/v2.0/.well-known/openid-configuration" />

Also confirm (not in web.config, but required): the {domain} you used in the discovery URL maps to a SurePassID partner that has a signing cert. The domain lives in the URL path, not config.


4. Verifying the redirect_uri (and when to override it)

In the standard Entra EAM flow you do not need to capture or configure redirect_uri at all — SurePassID defaults the allow-list to Microsoft's fixed value https://login.microsoftonline.com/common/federation/externalauthprovider. Use this section only to verify the value Entra actually sends, or to determine the correct override for a non-standard deployment (e.g. a national cloud whose host differs).

Entra sends a signed request object (JAR) to the authorize endpoint. Its claims tell you the exact redirect_uri, client_id, response_type, response_mode, and scope. (Note: your tenant may use parameter mode with an id_token_hint instead of a JAR — the traced parameters are the same.)

The acr is not captured here — SurePassID derives it automatically (see §4a).

EamAuthorize.HandleAuthorizeAsync already writes these values to the diagnostic trace once the request object validates — you do not need to add temporary logging:

  1. Ensure diagnostic tracing is enabled (Server.Trace=1 in web.config).
  2. Trigger a sign-in (Step 5) and open the trace log. Look for these two lines:
    • Non-sensitive OIDC parameters (always traced):
      EamAuthorize.aspx: request object validated for domain=<domain> clientId=<client_id> response_type=<...> response_mode=<...> scope=<...> redirect_uri=<...>.
    • PII hints (login_hint/sub) are logged separately on the "context stashed" line:
      EamAuthorize.aspx: context stashed for domain=<domain>. login_hint=<...> sub=<...>. Redirecting to SSOLogin.aspx...
  3. Confirm the redirect_uri on the first line matches the built-in default. If it does (the usual case), leave Eam.RedirectUris unset. If your tenant's value differs, set Eam.RedirectUris (or Eam.{domain}.RedirectUris) to that exact value to override the default. The acr requires no capture — it is derived to possessionorinherence (see §4a).

The full request object (raw JWT) is only traced when Saml2.TraceSensitiveData=true. Leave it false in production; the claim-level lines above are enough to verify the redirect_uri.

National-cloud redirect_uri values (override reference)

Entra's EAM redirect always follows the pattern https://{login-host}/common/federation/externalauthprovider; only the login authority host changes per cloud. SurePassID defaults to the commercial value, so set Eam.RedirectUris (or Eam.{domain}.RedirectUris) to the matching value below only for a national-cloud tenant.

Cloud Login authority host redirect_uri to configure
Azure Commercial (Global) — default, no config needed login.microsoftonline.com https://login.microsoftonline.com/common/federation/externalauthprovider
Azure Government (GCC High & DoD / "Government High") login.microsoftonline.us https://login.microsoftonline.us/common/federation/externalauthprovider
Azure China (21Vianet) login.partner.microsoftonline.cn https://login.partner.microsoftonline.cn/common/federation/externalauthprovider

Azure Government GCC High and DoD both use the same login.microsoftonline.us authority — there is no separate login host that distinguishes them, so the redirect_uri is identical for both. Always confirm the exact value against the captured request object (above) before relying on it.

Tip: login_hint in the request object is the subject SurePassID will bind the token sub to; the authenticated user's LoginName/Email must match it (see EamCallback.SubjectMatches).


4a. acr / amr values SurePassID returns (and how acr is derived)

Entra validates the minted id_token against two linked rules:

  1. amr must be a recognized RFC 8176 method value, emitted as a JSON array (e.g. "amr": ["otp"]). Exactly one value is sent (the second factor actually used); multiple values are rejected ('amr' claim must have exactly one value).
  2. acr (a single string) must express an authentication type different from the first factor. In EAM the first factor is always the Entra password — a knowledge factor — so the second-factor acr must exclude knowledge. Entra accepts the combined "OR" form possessionorinherence; a bare possession is rejected (AADSTS5001257 / AADSTS5001258).

How SurePassID derives acr (no config needed): every SurePassID second factor (OTP, push, security key, SMS/voice, hardware token, biometric) is a possession or inherence factor — never knowledge. So SamlAuthContextMapper.ResolveEamAcr(...) always returns possessionorinherence, which is the value Entra accepts. This is computed per transaction; there is nothing to capture from the portal.

Optional override: set Eam.{domain}.Acr (or global Eam.Acr) to a non-blank value only if a particular tenant's Conditional Access policy demands a different exact acr string. When set it is emitted verbatim and overrides the derived value. The diagnostic trace records which path was used:

EamCallback.aspx: issuing id_token. ... acr=possessionorinherence acrSource=derived amr=otp ...
EamCallback.aspx: issuing id_token. ... acr=<your value>          acrSource=EamConfig amr=otp ...

Returned value matrix

The second factor performed on SSOLogin.aspx maps to the amr/acr pair below. acr is the same derived value (possessionorinherence) for every method because all are non-knowledge factors.

SurePassID second factor Device / channel Factor type amr (single, in array) Derived acr
SurePassID Authenticator OTP (software) Mobile soft token possession otp possessionorinherence
Google Authenticator (software OTP) Mobile soft token possession otp possessionorinherence
Desktop / other soft-token OTP Software possession otp possessionorinherence
TapAuth push (Accept/Deny) Mobile push possession hwk possessionorinherence
FIDO / passwordless push Mobile push (user-verified) possession + inherence fido possessionorinherence
Push via voice call Voice possession tel possessionorinherence
SMS one-time code SMS possession sms possessionorinherence
Voice / IVR one-time code Voice possession tel possessionorinherence
FIDO2 security key Roaming hardware key possession hwk possessionorinherence
Hardware / OATH token (FOB, card, matrix) Hardware possession hwk possessionorinherence

AMR to SurePassID authentication-method mapping

Use this table when reading an Entra-decoded EAM id_token and mapping the returned amr value back to the SurePassID method the user actually performed.

EAM amr value SurePassID method(s)
otp SurePassID Authenticator OTP, Google Authenticator, desktop soft-token OTP, and other software OTP factors
sms SMS one-time code
tel Voice / IVR one-time code, or push delivered by voice call
hwk TapAuth push (Accept/Deny), FIDO2 security key, and hardware/OATH tokens (FOB, smart card, electronic card, matrix card, Treo)
fido FIDO push / passwordless push performed on the enrolled mobile device

The amr value is the concrete method SurePassID recorded the instant the factor was validated (SsoUtil.RecordAuthMethod), then read back in EamCallback. pwd is never emitted for EAM because Entra already validated the password first factor.

SAML2 vs EAM: the SAML2 assertion still emits the full multi-value AMR list (and appends mfa for two or more factors). Only the EAM path collapses to the single second-factor value plus the derived possessionorinherence acr.


5. End-to-end test run (Entra-driven)

  1. In the Admin UI → EAM Configuration for the tenant, enter the captured values and set Enabled = on, then Save. (Fallback: set the equivalent Eam.* web.config keys and Eam.Enabled=true, then recycle the IdP app pool.)
  2. Pre-checks (browser):
    • GET /eam/<domain>/.well-known/openid-configuration → 200 JSON; issuer == https://idp.surepassid.com/eam/<domain>.
    • GET /eam/<domain>/jwks → 200 JSON with a key whose kid is present.
  3. Sign in as the test user to an app that triggers MFA (e.g., https://myapps.microsoft.com or any app with a Conditional Access policy requiring MFA), so Entra invokes the external method.
  4. After the first factor, Entra redirects the browser to https://idp.surepassid.com/eam/<domain>/authorize?request=<signed JWT>.
  5. SurePassID validates the request object, then redirects to SSOLogin.aspx. Complete SurePass MFA (OTP/push/FIDO).
  6. SurePassID posts the id_token back via auto-submit form to Entra's redirect_uri.
  7. Expected: Entra accepts the factor; sign-in completes. Check Entra → Sign-in logs → the user's entry → Authentication details shows the external method satisfied MFA.

6. Capture checklist (copy/paste)

[ ] EAM Client ID (string you chose) . _______________________  -> Eam.ClientId (= request client_id, token aud)
[ ] Application (client) ID (GUID) ... _______________________  -> Entra EAM "App ID" field only (not a SurePassID key)
[ ] Directory (tenant) ID ........... _______________________  -> used in Eam.EntraOpenIdConfigurationUrl
[ ] Entra OpenID config URL ......... _______________________  -> Eam.EntraOpenIdConfigurationUrl
[ ] EAM redirect_uri ................ default (no config) / override -> Eam.RedirectUris (exact)
[ ] acr override needed? ............ no (derived possessionorinherence) / yes -> Eam.Acr = __________
[ ] SurePass partner domain ......... _______________________  -> {domain} in the endpoint URLs
[ ] Partner signing cert present? ... yes / no
[ ] Discovery URL given to Entra .... https://____/eam/<domain>/.well-known/openid-configuration
[ ] Eam.Enabled ..................... true
[ ] Test user + group scoped ........ _______________________

7. Troubleshooting (Entra-side symptoms → SurePassID cause)

Symptom in Entra Likely cause Fix
"External method failed" immediately, no SurePassID page Discovery URL wrong/unreachable, or issuer mismatch Verify discovery returns 200 and issuer exactly equals https://idp.surepassid.com/eam/<domain>.
SurePassID authorize returns 400 Request-object validation failed Check Eam.ClientId and Eam.EntraOpenIdConfigurationUrl; confirm Entra's kid resolves via its JWKS.
SurePassID authorize returns 400 "The 'openid' scope is required" The authorize request's scope did not contain openid This is Entra-controlled and should always include openid; if seen, capture the request object (Step 4) and verify the scope= trace value. No SurePassID config change fixes this.
SurePassID authorize returns 400 "redirect_uri not allow-listed" The request's redirect_uri matches neither the built-in default nor an Eam.RedirectUris override Verify the value (Step 4). If it is a legitimate non-default host, set Eam.RedirectUris to it exactly; the error message lists the expected allow-list values.
Token minted but Entra silently rejects the factor Wrong aud or nonce aud must == Eam.ClientId; nonce echoed verbatim.
Entra rejects with AADSTS5001257 (amr unexpected) or AADSTS5001258 (acr unexpected) acr did not express the required type-separation from the password first factor (e.g. bare possession was sent) Ensure the derived acr is possessionorinherence (default). Only set an Eam.Acr override if the tenant policy needs a specific string; check the acrSource= value in the trace.
Entra can't validate token signature JWKS kid mismatch Ensure /eam/<domain>/jwks serves the same cert used to sign (same kid); no cert rotation mid-flight.
403 "authenticated user does not match subject" login_hint ≠ SurePass user Align the Entra user's identifier with SurePassID LoginName/Email.

8. Notes / limits

  • This EAM path is a stop-gap MFA integration, not a full OIDC IdP; there is no token endpoint, no code exchange, no refresh token (contract §5).
  • Config is now database-backed per partner (EamConfig table, Admin UI in OIDC_Admin_UI_Spec.md §4), so each tenant has distinct client_id/redirect_uri (and optional acr override) without a shared file. web.config Eam.* keys remain as a fallback for un-migrated deployments.
  • Remove any temporary request-object trace logging (Step 4) before production.
SurePassID 360 Central Avenue #800 St. Petersburg, FL 33701 USA +1 (888) 200-8144 surepassid.com