Entra External Identities over B2C — lessons from a greenfield product

In early April I recorded a VoD for Warsaw IT Days 2026 about Microsoft Entra External Identities — the Polish title was something like “scalable cloud auth without rolling your own AuthN/AuthZ stack.” English write-up here. YouTube recording · demo repo.

I picked the topic partly because of the calendar. Azure AD B2C is not vanishing overnight — Microsoft still supports existing tenants until at least May 2030 — but the product line has been in wind-down for a while. New customers could not buy B2C after May 2025. B2C Premium P2 went away on 15 March 2026, a few weeks before I sat down to record. No new features land on B2C anymore; External ID gets those instead. If you are starting a B2B portal in 2026, that is worth knowing before you copy an old B2C sample repo.

Most of the talk came from customer delivery, not slide templates. One client had Azure AD B2C on an older product — custom policies, a tenant from years back, a team that knew how it worked. The next product needed the same kind of external users (partners, customers, self-service sign-up), but standing up fresh B2C in 2025 felt like buying into a migration you would owe yourself later. We built the new stack on Entra External Identities from scratch — React front end, .NET API, OpenFGA for fine-grained permissions because Entra scopes alone were never going to describe who may touch which row. The old B2C product kept running on its own tenant; this was not a lift-and-shift between them.

The GitHub repo is the teaching cut — MSAL in React, token validation in .NET 10, two app registrations, a couple of scopes. Production had more tenants, weirder edge cases, and a lot of meetings about whether identity or the app team owns “who can do what.”

I opened with AuthN versus AuthZ because every identity project blurs them until something breaks in QA.

AuthN is login — password, MFA, Google, whatever IdP you allow. AuthZ is what happens after: can this person see that customer’s data, invite a colleague, delete a thing. Entra External ID is good at the first part. User flows, social IdPs, email OTP, custom fields at sign-up, Conditional Access if your tenant has it. That covers a lot of B2B portals.

It stops being enough when authorization has to follow your domain. In the demo, the API exposes Things.Read and Things.Write scopes — coarse gates on whether the token may call the API at all. Production needed finer rules: org A’s admin can invite users, org B’s viewer can read but not edit, support can look read-only under audit. Scopes do not encode that.

So after Entra validates the token, our API reads oid / sub and asks OpenFGA. Same idea you would use with any OIDC provider; Entra just happens to handle external-user lifecycle and MFA in one place. If your app only needs “logged-in users may hit the API,” scopes might suffice. If tenants share a portal but must not see each other’s data, plan the second layer early — not after the first security review.

The old product’s B2C setup was fine for what it was. Custom policies, known quirks, nobody rushing to rewrite it.

The new product was different timing. Procurement and architecture reviews already ask why you would standardise on something Microsoft is not extending. P2 retirement in March 2026 removed Identity Protection-style features from the B2C tier we would have leaned on. And migrating later is not a portal export — B2C IEF XML and External ID user flows do not map one-to-one. You rewrite sign-in UX, MSAL config, and claim handling. Starting on External ID avoided doing that twice.

Microsoft’s B2C FAQ and the migration tooling announcement have the official dates. I put them on a slide so architects could photograph the timeline for their backlog. Boring slide, useful photo.

External Identities treats external users as guests in your tenant — with self-service sign-up if you turn it on — instead of a separate B2C directory per product.

In the talk I walked through the knobs we touched in delivery: external collaboration settings (who can invite whom, self-service on or off), identity providers on the sign-in page, user flows (Entra’s name for scripted sign-up and sign-in — IdPs, branding, languages, which apps get tokens), custom attributes collected at registration, API connectors if you need to call your own REST endpoint mid-flow (“is this email domain allowed?”), and the usual app registrations — SPA plus API, exposed scopes, delegated permissions.

The repo mirrors that: React with @azure/msal-react, .NET API checking JWTs and scopes. Two registrations, same dance as internal SSO, which helped — half the team had done workforce Entra before.

A lot of External ID content stops once the tenant is configured. Even on a greenfield project, the slow part was application wiring — not portal clicks.

MSAL on the React side: clientId, authority, knownAuthorities, scope strings in .env, all matching what the API app registration exposes. One typo gives you an AADSTS error that sends you to the wrong Stack Overflow thread. On the API side, JWT validation and scope checks on ASP.NET controllers had to agree with what the SPA requested at login — obvious in hindsight, fiddly in week two.

I mentioned B2C migration patterns in the talk because plenty of teams will face them, but that was not our path. We looked at the older product for comparison — what worked, what we would not copy — and built this one on External ID from day one. No parallel auth backends, no token diff between old and new tenants. If you are migrating off B2C later, expect extra pain around authority URLs, custom policy logic, extension claims, and test fixtures tied to old tokens. Starting fresh skipped that entire class of problem.

The demo scopes mean “you may call /things.” Production needed “you may edit this thing belonging to that org.” OpenFGA holds tuples — user Alice is a viewer on org Acme — and answers at request time. Entra logs you in; OpenFGA decides on the row.

I left OpenFGA out of the public repo so the sample stays focused on Entra. Without it, the customer story would have been “we clicked through the portal” instead of “we built auth for real users with real isolation rules.”

I closed the VoD with a pointer to Entra Native Authentication for teams that outgrow stock user flows but do not want to go back to full custom-policy XML. I have not put it in production yet; it is on the list for the next product that complains about branding limits.

For the current portal, user flows plus an API connector covered us.

Three weeks earlier I had been at AzureDay Poland 2026 — same Airport Hotel Okęcie, Warsaw crowd instead of my usual Wrocław circuit. I submitted a similar talk to AzureDay; it did not make the cut. I stayed for another speaker’s session on External ID / B2C anyway — strong on tenant configuration and policy. This VoD is the other half: application wiring, AuthN versus AuthZ, and why we picked External ID for a greenfield product instead of standing up another B2C tenant. My AzureDay notes cover the rest of that day; this post is the companion piece.


If you still run B2C, you are not moving this weekend. You are also not planning around 2030 as if nothing happens before then — March 2026 already moved a licensing line. For anything new, I would start on External ID, keep scopes for API gates, and decide early what happens after the token arrives. The repo is there if you want a small stack to break before you touch production config.