Cookbook · Microsoft Entra custom OpenID Connect provider

Add CodeB as a custom OpenID Connect provider in Microsoft Entra.

CodeB Sovereign Communications ships a standards-based OpenID Connect identity provider. Microsoft Entra accepts custom OpenID Connect providers on two of its surfaces — Entra External ID for customers (the successor to Azure AD B2C) and Entra ID B2B collaboration guest federation. This cookbook walks both, end-to-end, in configuration only. No custom code. No Graph API. Your Entra tenant gets an additional sign-in button that resolves to the CodeB tenant, and by extension to the European Digital Identity Wallet path CodeB already fronts.

Which surface do you want?
  • Entra External ID for customers — you are building a public-facing app and want end users to be able to sign in with a CodeB account (which itself can be backed by password, passkey, or the European Digital Identity Wallet). Use section External ID.
  • Entra ID B2B collaboration (guest federation) — you are inviting partners or contractors into your own workforce Entra tenant as guests, and want them to authenticate with their existing CodeB account instead of creating a Microsoft Account or receiving a one-time passcode. Use section B2B guest federation.

0 Why this works

Microsoft Entra's custom OpenID Connect provider surface expects the third-party identity provider to speak plain OpenID Connect Core 1.0 with PKCE and a discoverable metadata document. CodeB advertises all of it at fixed URLs on every tenant:

  • /.well-known/openid-configuration — discovery document listing authorization / token / userinfo / jwks endpoints, supported scopes, response types, algorithms.
  • /.well-known/jwks.json — RS256 public key set, with previous-key rotation window.
  • /.well-known/openid-federation — ES256-signed OpenID Federation 1.0 entity statement (optional but present for future Entra federation trust chains).
  • /.well-known/security.txt — RFC 9116 contact + policy.

Entra pulls the discovery document, learns everything about the provider from it, and drives the standard authorization-code + PKCE dance. Nothing bespoke on either side.

1 CodeB endpoints Entra will hit

Replace <CODEB_TENANT_HOST> with your CodeB tenant hostname (for example www.aloaha.com). Every endpoint is HTTPS, per-tenant, and independently rate-limited.

PurposeURL
Discoveryhttps://<CODEB_TENANT_HOST>/.well-known/openid-configuration
Authorizationhttps://<CODEB_TENANT_HOST>/oidc.ashx?action=authorize
Tokenhttps://<CODEB_TENANT_HOST>/oidc.ashx?action=token
UserInfohttps://<CODEB_TENANT_HOST>/oidc.ashx?action=userinfo
JWKShttps://<CODEB_TENANT_HOST>/.well-known/jwks.json
End-sessionhttps://<CODEB_TENANT_HOST>/oidc.ashx?action=end_session
Introspection (RFC 7662)https://<CODEB_TENANT_HOST>/oidc.ashx?action=introspect
Federation entity statementhttps://<CODEB_TENANT_HOST>/.well-known/openid-federation

2 Prerequisites

  • A CodeB tenant with admin access to /oidc-clients.html.
  • An Entra tenant with either the External ID for customers configuration or the workforce tenant where you want B2B guest federation. You need the Entra role External Identity Provider Administrator or higher.
  • Your Entra tenant ID (a GUID). You can read it from the Entra admin center overview page.

The two Entra callback URLs you will need to allow-list at CodeB:

  • External ID for customers: https://<YOUR_ENTRA_TENANT_HOST>/<POLICY_ID>/oauth2/authresp (Entra shows the full URL when you add the provider — copy it verbatim).
  • B2B guest federation: https://login.microsoftonline.com/te/<ENTRA_TENANT_ID>/oauth2/authresp

3 Register the OIDC client at CodeB

  1. Open https://<CODEB_TENANT_HOST>/oidc-clients.html and sign in as an admin.
  2. Click New client. Give it a memorable name (e.g. entra-external-id or entra-b2b-guests).
  3. Set Grant types to authorization_code. Set Response types to code.
  4. Under Redirect URIs, paste the Entra callback URL from the previous step. You can paste more than one if you want the same CodeB client to serve both Entra surfaces.
  5. Confirm PKCE is required (default on). Entra always sends code_challenge_method=S256.
  6. Confirm Client authentication is client_secret_basic or client_secret_post. Both work with Entra.
  7. Save. Copy the client_id and client_secret. The secret is shown once.
If you want the CodeB provider to appear only for a subset of your CodeB users (e.g. only accounts in the partner group), configure the per-client wallet-claim allow-list at App_Data/<tenant>/oidc-clients/<client_id>/wallet-claim-allowlist.json. Default deny.

4 Entra External ID for customers — user-flow setup

External ID for customers is the modern successor to Azure AD B2C. It hosts customer-facing sign-in / sign-up flows against your own directory. Custom OpenID Connect providers are a first-class feature.

  1. Sign in to the Entra admin center as a user with External Identity Provider Administrator.
  2. Navigate to External IdentitiesAll identity providersCustom+ New OpenID Connect provider.
  3. Fill in the form:
    Name                CodeB (European Digital Identity Wallet)
    Client ID           <the client_id from step 3>
    Client secret       <the client_secret from step 3>
    Scope               openid profile email
    Response type       code
    Response mode       query
    Metadata URL        https://<CODEB_TENANT_HOST>/.well-known/openid-configuration
  4. Under Identity provider claims mapping, map (see the claim table for the full list):
    User ID       <-  sub
    Display name  <-  name         (fallback: preferred_username)
    Given name    <-  given_name
    Surname       <-  family_name
    Email         <-  email
  5. Save.
  6. Attach the provider to a user flow. Navigate to External IdentitiesUser flows → pick your sign-up / sign-in flow → Identity providers → tick CodeB (European Digital Identity Wallet). Save.

5 Entra ID B2B collaboration — direct federation via OIDC

The B2B collaboration surface exists inside your workforce Entra tenant and lets you invite guests. Traditionally B2B federation supported SAML / WS-Fed direct federation only. Microsoft has since added OIDC direct federation so you can point a domain (e.g. aloaha.com) at any OpenID Connect provider, including CodeB.

  1. Sign in to the Entra admin center as a user with External Identity Provider Administrator.
  2. Navigate to External IdentitiesAll identity providers+ New OpenID Connect provider (workforce tenant variant).
  3. Fill in:
    Display name        CodeB (European Digital Identity Wallet)
    Client ID           <client_id from step 3>
    Client secret       <client_secret from step 3>
    Metadata URL        https://<CODEB_TENANT_HOST>/.well-known/openid-configuration
    Scope               openid profile email
    Response type       code
    Response mode       form_post
    Domain              <the email domain(s) to federate, comma-separated>
  4. Under Claims mapping, set the same mapping as for External ID (see claim table).
  5. Save. From this point, when you invite a guest whose email ends with a federated domain, Entra redirects them to CodeB instead of prompting for a Microsoft Account or one-time passcode.
B2B direct federation resolves a guest's email domain. If you federate aloaha.com, every guest with an @aloaha.com email will land at your CodeB tenant. To federate a subset, register each subdomain separately (eng.aloaha.com, ops.aloaha.com, etc.).

6 Claim mapping reference

Entra's custom OIDC surface consumes a small, fixed set of standard OpenID Connect claims. CodeB emits all of them at /oidc.ashx?action=userinfo and inside the ID token. Non-standard CodeB claims (eudi_verified, wallet_attestation, role, groups) do not flow through Entra automatically — see the "Custom claims" note below.

Entra fieldCodeB claimNotes
User IDsubStable, opaque. Never reuse across users. This is what Entra keys the external-user record on.
Display namenameFalls back to preferred_username, then to email. Never empty.
Given namegiven_nameOptional in some flows; always present after a wallet-backed sign-in.
Surnamefamily_nameSame as above.
EmailemailVerified via CodeB's activation flow before the account is usable, so Entra can safely treat it as verified.
Locale (optional)localeBCP 47 tag. CodeB emits en or de today.
Custom claims. Entra External ID does not currently forward arbitrary custom claims from an external OpenID Connect identity provider into the token Entra itself issues. If your downstream application needs eudi_verified, wallet_attestation, or a role claim inside the Entra-issued token, add an Entra Custom claims provider that makes a REST call to your own app at issuance time. Your app calls /oidc.ashx?action=userinfo at CodeB with the user's access token, reads the extra claims, and returns them to Entra to inject into the outgoing token.

7 Verify the integration end-to-end

  1. Open a fresh incognito / private window (so no cached Entra session interferes).
  2. External ID: navigate to the sign-in URL of your Entra user flow. B2B: send yourself an invitation to a federated-domain address, then open the invitation link.
  3. Confirm you see a CodeB (European Digital Identity Wallet) button (or, for B2B, that you are redirected to CodeB automatically after Entra resolves the domain).
  4. Complete sign-in at CodeB with any method — password, passkey, or the wallet.
  5. Confirm you are redirected back to Entra, and that Entra provisions the external user and hands you into the target application.
  6. In the Entra admin center under Users, confirm the external user record has the mapped fields populated.
  7. On the CodeB side, tail App_Data/<tenant>/logs/oidc.log for lines starting with [OIDC-AUTHORIZE-DIAG], [OIDC-TOKEN-DIAG], and [OIDC-USERINFO-DIAG]. Each carries the requested client_id, granted scopes, and outcome.

8 Troubleshooting

Entra says "Something went wrong" after CodeB redirects back

Almost always the redirect URI. Copy the URL from Entra's provider-detail page verbatim into the CodeB client's Redirect URIs list. Watch for a trailing slash difference (/oauth2/authresp vs /oauth2/authresp/) — Entra rejects a byte-for-byte mismatch.

Entra says "AADSTS90056: PII / claim missing"

The claim mapping in Entra references a claim CodeB is not emitting. CodeB always emits sub, email, and name. If you mapped given_name or family_name for a user who has not filled out their profile, the token is missing those keys and Entra rejects the whole assertion. Fix: either mark the mapping optional in Entra, or ensure your CodeB users complete profile registration before federating.

Discovery fails: "Unable to reach metadata URL"

Entra fetches discovery from Azure egress IPs. If your CodeB tenant is behind an IP allow-list, add Microsoft's Azure public IP ranges to the allow-list (or move the CodeB tenant to a public egress and rely on OAuth PKCE for authentication).

Wallet-backed sign-in works but Entra token is missing wallet claims

Expected. Read the Custom claims note in section Claim mapping. Entra does not proxy arbitrary claims — wire an Entra Custom claims provider that calls your app, and your app in turn calls CodeB userinfo with the user's access token.

B2B guest federation redirects but Entra prompts a Microsoft Account

The invited email's domain is not one of the domains you registered with the CodeB provider. Edit the provider in Entra and add the missing domain, or invite the guest under a domain that is federated.

9 Appendix — ready-to-paste JSON

If you script the Entra provider setup via the Microsoft Graph API (identityProviders resource), here is a minimal payload that matches the manual walk-through in sections 4 & 5:

{
  "@odata.type": "#microsoft.graph.openIdConnectIdentityProvider",
  "displayName": "CodeB (European Digital Identity Wallet)",
  "clientId": "<CLIENT_ID>",
  "clientSecret": "<CLIENT_SECRET>",
  "scope": "openid profile email",
  "responseType": "code",
  "responseMode": "query",
  "metadataUrl": "https://<CODEB_TENANT_HOST>/.well-known/openid-configuration",
  "claimsMapping": {
    "userId":      { "claim": "sub" },
    "displayName": { "claim": "name" },
    "givenName":   { "claim": "given_name" },
    "surname":     { "claim": "family_name" },
    "email":       { "claim": "email" }
  }
}

Post this to POST https://graph.microsoft.com/v1.0/identity/identityProviders with an access token holding IdentityProvider.ReadWrite.All. You still have to attach the provider to a user flow separately (either through the portal or via the userFlow Graph resource).