PS HarriJaakkonen :~/Blog/Posts> cat ./microsoft-entra-external-id-preview-features-part-2.html

Microsoft Entra External ID: What's in Public Preview and Where It's Headed — Part 2

Microsoft Entra External ID Public Preview Features 2026

What's still in preview

Part 1 covered everything that shipped GA in Entra External ID between November 2025 and February 2026 — regional data residency, machine-to-machine auth, per-app branding, custom banned passwords, alias sign-in, and device auth grant. This part covers what's still in public preview, which honestly has some of the more interesting features for teams planning a migration from an existing customer identity system.

Preview in Microsoft's terminology means the feature is available for use in production, but the API shape may still change, SLAs aren't guaranteed, and you're providing feedback that shapes the final version. For identity infrastructure, that's worth factoring in — don't build a hard dependency on a preview API's exact request/response format if you can avoid it.

Preview status as of February 2026: Just-in-time password migration entered public preview in December 2025. WAF integrations with Akamai and Cloudflare reached documentation GA status in late 2025 with ongoing updates. MFA with Conditional Access authentication context was updated in November 2025. All features are available for external tenants today.

Just-in-time password migration

This is the standout preview feature. If you're running a CIAM migration — moving customers from an existing identity system to External ID — the traditional approach requires a bulk password migration event. You export hashed passwords, reformat them, import them into the new system, and hope nothing goes wrong. In practice, something always goes wrong: hash format incompatibilities, accounts that haven't been touched in years, users who've forgotten they exist, regulatory restrictions on moving password hashes across systems.

Just-in-time (JIT) migration solves this differently. Instead of migrating all passwords upfront, you migrate each customer's password the first time they sign in after the cutover. The flow works like this:

  1. Customer tries to sign in to External ID for the first time after migration
  2. External ID checks whether the account has a local password set
  3. If not, it calls a custom REST API endpoint you control (the "legacy system connector")
  4. Your API validates the credential against your old system and returns a success/failure response
  5. If your API says the credential is valid, External ID sets the password on the account and completes sign-in
  6. On subsequent sign-ins, External ID handles authentication directly — no more calls to your legacy system

The result: customers never notice the migration. They sign in as normal on cutover day, and their password just works. No password reset emails, no "account not found" errors, no forced re-registration.

Just-in-time password migration — first sign-in after cutover Customer Browser initiates sign-in EXT ID Entra External ID external tenant no password stored yet Your legacy API you build this checks old database Old database password hashes still has all records 1. POST /token username + password 2. No local password found JIT migration triggered → calls your legacy API 3. POST /verify-credential { encryptedPasswordContext } lookup hash match 4. HTTP 200 — credential valid 5. External ID stores password on account Migration complete — one-way, permanent This is a one-way permanent migration — steps 2–5 are invisible to the customer 6. JWT issued — user is signed in Subsequent sign-ins (after JIT migration completes) Customer signs in → External ID finds stored password → authenticates locally — legacy API not called Result: zero disruption — the customer never sees the migration happening Migration runs once per user only, on their first sign-in after cutover the first sign-in after cutover calls your legacy API to verify the credential. External ID then stores the password locally. All future sign-ins bypass the legacy system entirely.

The legacy system connector API you need to build

The JIT migration feature doesn't talk to your old database directly — you build a REST API that acts as the bridge. External ID calls this API when it needs to verify a credential that isn't in the external tenant yet. The API has one job: take a credential verification request and return whether it's valid.

The request from External ID to your API includes an encryptedPasswordContext field — a Base64-encoded, encrypted structure that contains the username and the password the user just submitted. Your API decrypts it using a key you've configured in the External ID JIT migration settings, verifies against your old system, and responds with a standardised JSON body.

Public Preview — December 2025: Just-in-time password migration for Entra External ID. Available for external tenants. Requires building a legacy credential verification API. The encryptedPasswordContext field in the request body was updated in January 2026 (preview iteration). Check the current docs for the latest field specification before implementing — this is still in preview and the API shape can change.

A minimal implementation of the legacy connector API looks something like this. The key steps are: receive the request from External ID, decrypt the encryptedPasswordContext to get the username and password, verify against your old system, and return the right JSON structure:

# Conceptual structure of the legacy credential verification endpoint
# Your implementation will vary based on language and old system type

POST /api/verify-credential
Content-Type: application/json
{
    "type": "microsoft.graph.OnTokenIssuanceStartCustomExtensionHandler",
    "source": "/tenants/{tenantId}/...",
    "encryptedPasswordContext": "BASE64_ENCRYPTED_BLOB"
}

# Your API decrypts encryptedPasswordContext using the configured key
# Verifies username + password against your legacy database/system
# Returns one of:

# SUCCESS — credential is valid
HTTP 200 OK
{
    "@odata.type": "microsoft.graph.onAttributeCollectionSubmitResponseSuccess",
    "actions": [
        {
            "@odata.type": "microsoft.graph.validatePassword",
            "passwordIsValid": true
        }
    ]
}

# FAILURE — credential is not valid
HTTP 200 OK
{
    "@odata.type": "microsoft.graph.onAttributeCollectionSubmitResponseSuccess",
    "actions": [
        {
            "@odata.type": "microsoft.graph.validatePassword",
            "passwordIsValid": false
        }
    ]
}

Security note: The encryptedPasswordContext contains the raw password the customer typed. Handle this with the same care as any plaintext password in transit — decrypt it in memory, verify it, discard it. Don't log it, don't store it, don't pass it to a queue. Your legacy connector API should run in a private network segment accessible only to External ID's outbound IP ranges, and use mutual TLS or bearer token validation to reject requests from anything other than External ID.

JIT migration vs bulk migration — when to use which

JIT migration isn't always the right answer. The main trade-offs:

Factor JIT Migration Bulk Migration
Customer disruption None — transparent to users May require password reset if hashes can't transfer
Cutover speed Instant — flip DNS/app config, done Requires import window, verification period, rollback plan
Old system dependency Legacy API must stay up until all users migrate Old system can be decommissioned after bulk import
Inactive accounts Never migrate — accounts that don't sign in stay in limbo All accounts migrated regardless of activity
Regulatory concern Credential only leaves old system on user's sign-in Bulk password hash export may face regulatory scrutiny
Implementation effort Build and host the legacy connector API Export/transform/import tooling, higher upfront effort

For most consumer-facing CIAM migrations, JIT migration is the better choice. The sticking point is inactive accounts — if you have a large tail of dormant users who'll never sign in again, those accounts will sit in your external tenant without passwords indefinitely. You can address this with a parallel cleanup job that periodically removes accounts flagged as inactive past a threshold, or by running a batch migration alongside the JIT flow for accounts that haven't migrated within 90 days.

WAF integrations: Akamai and Cloudflare

External ID now has documented integration patterns for two major web application firewalls: Akamai and Cloudflare. Both reached documentation GA status in late 2025, with ongoing updates to the integration guides through early 2026.

The reason this matters: External ID's sign-in endpoints are public-facing by definition. Your customers need to reach them, which means anyone can reach them. Without a WAF in front, you're exposed to credential stuffing attacks (automated password guessing at scale), bot-driven account enumeration, and volumetric attacks that can degrade sign-in performance for real users.

WAF integration architecture — Akamai or Cloudflare in front of External ID Internet traffic Real users Bots / scrapers Credential stuffing Account enumeration DDoS / volumetric → all mixed together WAF layer Akamai or Cloudflare Bot detection Rate limiting IP reputation Geo-blocking OWASP rule sets Custom rules Blocked (403/429) Entra External ID Sign-in endpoints User flows Token issuance MFA prompts Clean traffic only Your app receives token from real users only
WAF sits between the internet and External ID's sign-in endpoints. Bots and malicious traffic are blocked before reaching External ID — protecting both the sign-in experience and the downstream app.

Akamai integration

The Akamai integration uses Akamai's Bot Manager and Web Application Firewall products in front of your External ID sign-in domain. The setup involves pointing a custom domain for your External ID sign-in endpoint through Akamai's edge, then configuring Akamai's rules to protect the sign-in flow while allowing legitimate authentication traffic through.

A few things specific to the Akamai integration worth knowing:

  • You configure a custom hostname in External ID (e.g., login.yourbrand.com) and then route that hostname through Akamai rather than directly to Microsoft's infrastructure
  • Akamai's bot detection runs on every request to the sign-in endpoints — it can distinguish automated credential-stuffing traffic from real browser sessions using behavioural signals
  • Akamai can be configured to inject a challenge (JavaScript, CAPTCHA) for suspicious sessions before they reach External ID, which offloads the challenge handling to the WAF layer
  • The integration supports Akamai's Security Information and Event Management (SIEM) connector, so sign-in attack telemetry flows into your security tooling

Cloudflare integration

The Cloudflare integration follows a similar pattern — your External ID custom domain routes through Cloudflare's network before reaching Microsoft's infrastructure. Cloudflare's WAF and bot management rules apply to the authentication traffic.

The Cloudflare integration was updated in December 2025 with revised configuration steps that account for changes in Cloudflare's proxy settings for custom domains. If you configured this before December 2025, check the current docs — the origin server configuration in Cloudflare's dashboard changed slightly.

Practically, both Akamai and Cloudflare provide similar protection capabilities for this use case. The choice between them usually comes down to what your organisation already has a contract with, rather than feature differentiation specific to External ID.

Configuration note: Both WAF integrations require a verified custom domain in your external tenant. The domain needs to be set up under External Identities → Custom URL domain in the Entra admin center before you configure routing through the WAF. The custom domain is also what makes your sign-in URL look like login.yourbrand.com rather than a Microsoft-branded endpoint.

MFA with Conditional Access authentication context

Conditional Access authentication context lets you require step-up authentication at specific points in your application — not just at sign-in. The update in November 2025 extended this to MFA enforcement within external tenants, meaning you can trigger MFA challenges mid-session based on what the user is trying to do.

The practical use case: a customer signs in to your portal with password only (standard friction for browsing). When they go to change their email address or initiate a high-value transaction, your application requests the acrs claim with a specific authentication context value. External ID checks whether the current session satisfies that context, and if not, prompts for MFA before proceeding.

This is the right pattern for apps where you want low-friction browsing but high-friction for sensitive operations — it's much better UX than requiring MFA on every sign-in, and more secure than never requiring MFA during a session.

# In your application, when a sensitive action is triggered:
# Request a token with the acr_values or claims parameter
# specifying your configured authentication context class reference

# Example: initiating a high-value operation
POST /token
{
    "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "claims": "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"c1\"}}}"
}

# If the current session doesn't satisfy context "c1",
# External ID returns an interaction_required error.
# Your app then redirects to the authorization endpoint with:
GET /authorize?
  claims={"access_token":{"acrs":{"essential":true,"value":"c1"}}}
  &prompt=login
# User is challenged for MFA, session is elevated, token re-issued.

Licensing: what the add-ons actually cost

External ID has two add-ons that are easy to miss if you're just looking at the base pricing:

Component What it covers Billing model Required for
Base External ID User authentication, social IdP federation, user flows, Conditional Access, MFA, branding Per MAU/month (50k free) Everything basic
Go-Local add-on Data residency in selected region (US, EU, AU, JP) Per MAU/month (premium rate) AU/JP data residency requirements
M2M Premium add-on Client credentials grant (machine-to-machine auth), app-only token issuance Per token (not per user) Backend service auth, daemon flows

The M2M Premium add-on pricing per-token can add up if your backend services call External ID frequently — for example, if every API request from your service requires a fresh token rather than caching and reusing tokens for their full lifetime. Standard OAuth 2.0 token caching applies: cache tokens until they're within a few minutes of expiry, then refresh. Don't fetch a new token on every API call.

When External ID is the right call

External ID makes sense when you need a managed CIAM platform without building and operating identity infrastructure yourself. But it's not the right answer for everything. A few factors worth thinking through:

External ID decision framework External (non-employee) users who need sign-in to your app? No Use workforce tenant Entra ID (employees) Yes Partners (B2B) or customers / consumers (CIAM)? Partners Entra B2B / External Collab may suit better Customers Building on Microsoft stack and <5M MAU initially? No / complex Evaluate Auth0 Okta CIC / DIY Keycloak Yes Strict data residency requirements (AU, JP, or non-supported region)? AU / JP External ID + Go-Local add-on No / US / EU Entra External ID Standard tier — base pricing (50k MAU free)
A simplified decision tree for choosing between External ID and other identity options. The real choice depends on your specific requirements — this is a starting point, not a definitive guide.

A few things the decision tree doesn't capture:

  • If you're already running Keycloak, Auth0, or another CIAM platform and it's working, factor in the switching cost — External ID isn't automatically better just because it's Microsoft's product.
  • External ID is still catching up to more mature CIAM platforms in some areas. Check specifically for advanced progressive profiling, complex attribute mapping, and highly customised authentication flows before committing.
  • If your identity team knows Entra ID well, External ID is a natural extension. If they know Okta or Auth0 deeply, the operational overhead of switching platforms is real.

Preview features summary

Feature Preview Since Status (as of Feb 2026) Production-ready?
JIT password migration Dec 2025 Public Preview — API updated Jan 2026 Use with caution — API may still change
Akamai WAF integration Nov 2025 Documented — ongoing config updates Yes — pattern is stable
Cloudflare WAF integration Late 2025 Documented — Dec 2025 config refresh Yes — verify against latest docs first
MFA with CA authentication context Nov 2025 Updated — external tenant support Yes — GA capability with updated config

Official Microsoft docs

Topic Microsoft Learn link
JIT password migration (Preview) learn.microsoft.com → JIT password migration
Configure Akamai WAF learn.microsoft.com → Akamai integration
Configure Cloudflare WAF learn.microsoft.com → Cloudflare integration
MFA in external tenants learn.microsoft.com → MFA in external tenants
What's new (External ID) learn.microsoft.com → What's new

Where things stand in early 2026

External ID is in a noticeably better state in early 2026 than it was twelve months ago. The GA features in Part 1 fill meaningful gaps — machine-to-machine auth, regional data residency for APAC, per-app branding, and several authentication flow improvements that matter for specific industry verticals.

The preview features are the ones I'd watch most closely for migrations. JIT password migration in particular could significantly reduce the operational risk of moving a large customer base from a legacy CIAM platform — the fact that you don't need a coordinated cutover event, and customers never hit a "reset your password" wall, is a real practical win.

The platform still has gaps. If you need features that more mature CIAM platforms offer, do the feature comparison before committing. But if you're starting fresh on a Microsoft stack, or you've been deferring a CIAM migration because the tooling wasn't good enough, the gap has closed considerably.