Web

Fix Cross-Origin-Opener-Policy Error in Blazor WASM Entra ID Localhost

Resolve cross-origin-opener-policy errors in Blazor WASM standalone apps with Entra ID/Azure AD authentication on localhost (https://localhost:1234). Works on Azure Static Web Apps? Switch to redirect mode or add COOP headers via web.config.

1 answer 2 views

How to fix cross-origin-opener-policy error in Blazor WASM Standalone app with Entra ID authentication on localhost (works fine on Azure Static Web Apps)?

I’m using Entra ID (External) for authentication and user management in a Blazor WASM Standalone app targeting .NET 10 with the latest updates/patches.

  • Publishes and runs perfectly on Azure Static Web Apps.
  • In debug mode locally at https://localhost:1234, after signing in and before the popup closes, I get a cross-origin-opener-policy error.

The https://localhost:1234 redirect URI is correctly configured in the Entra ID app registration.

What is causing this issue and how can it be resolved?

The cross-origin-opener-policy error in your Blazor WASM standalone app with Entra ID authentication hits right after sign-in on localhost (like https://localhost:1234), but vanishes on Azure Static Web Apps. This stems from missing COOP headers on your local dev server, blocking MSAL’s popup from safely communicating back via window.opener—Azure handles it automatically. Switch to redirect mode in Program.cs for the quickest fix, or add COOP: same-origin headers via web.config to keep popups working.


Contents


Understanding the Cross Origin Opener Policy Error in Blazor WASM with Entra ID

Picture this: You’re debugging your Blazor WASM standalone app locally at https://localhost:1234. Entra ID sign-in popup opens fine, you authenticate, and then—bam. The browser console screams about a cross-origin-opener-policy mismatch right before the popup tries to close and hand control back to your app. Your app freezes, auth doesn’t complete, and you’re stuck refreshing.

This isn’t a config glitch in your Microsoft Entra ID app registration (those redirect URIs are spot on). Nor is it unique to .NET 10—folks have reported identical issues in Blazor WASM with Azure AD/Entra ID auth flows. The popup relies on window.opener to post the auth result back, but modern browsers (Chrome 83+, Edge, Firefox) enforce stricter Cross-Origin Opener Policy (COOP) to prevent tabnabbing attacks. Without the right headers, the opener window (your localhost app) and popup (login.microsoftonline.com) end up in separate browsing contexts. No communication. Deadlock.

Why localhost specifically? Your dev setup—likely dotnet watch or IIS Express—serves static files without these security headers out of the box. Azure Static Web Apps? They inject them automatically. Simple as that.


Why It Works on Azure Static Web Apps but Fails on Localhost

Ever deploy to Azure Static Web Apps and sigh in relief? Your Blazor WASM with Entra ID just… works. No cross origin opener policy drama. That’s because Azure’s platform auto-applies Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers for static sites, especially those needing SharedArrayBuffer (which Blazor WASM leans on for performance).

Localhost, though? Brutal. IIS Express (default for HTTPS debug) or dotnet serve prioritize speed over full header support. No COOP means the MSAL popup can’t safely link back. It’s like inviting a friend over but locking the door midway through the conversation.

Switch to hosted Blazor (server-side scaffolding)? Problem often vanishes too—server middleware handles headers. But standalone WASM? You’re flying solo, serving pure static files. Time to roll up sleeves.


Root Cause: COOP/COEP Headers and MSAL Popup Issues

Dig deeper, and it’s all about browsing context groups. COOP controls if cross-origin windows can access each other’s window.opener or window.parent. Default is unsafe-none, but browsers push same-origin for security. Your localhost lacks it, so Entra ID’s popup (different origin) gets isolated.

MSAL.js exacerbates this. In popup mode, it opens https://login.microsoftonline.com/..., grabs tokens, then calls opener.handleResponse() to relay success. Blocked by COOP? Silent failure. Add COEP (require-corp), and you enable credentialless SharedArrayBuffer—key for WASM threading.

From official tracking, this plagues standalone Blazor WASM auth precisely because no server means no automatic headers. Popup mode shines for UX (no full reloads), but demands header parity with production.

Trade-off? Redirect mode sidesteps popups entirely, using full-page navigation. Both viable—pick based on your flow.


Fix 1: Switch to Redirect Mode for Entra ID Authentication

Fastest win, no headers needed. MSAL handles redirects natively, and localhost plays nice.

In Program.cs, tweak AddMsalAuthentication:

csharp
builder.Services.AddMsalAuthentication(options =>
{
 builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
 options.ProviderOptions.DefaultAccessTokenScopes.Add("api://your-api-scope");
 options.ProviderOptions.LoginMode = "redirect"; // Key change: ditch popup
});

Update index.html or App.razor to trigger redirect:

html
<!-- In your login button/event -->
Navigation.NavigateToLogin("authentication/login");

No more popups. User hits sign-in, browser redirects to Entra ID, callbacks to /authentication/login-callback, extracts tokens. Smooth. Drawback? Full page reloads feel clunkier than popups. But for local dev? Gold.

Test: dotnet run --project YourWasmApp.csproj --urls "https://localhost:1234". Sign in. Done. Works across browsers, no COOP fuss.

Pro tip: If hybrid (Azure AD connect scenarios), this aligns with enterprise redirect prefs anyway.


Fix 2: Add Cross-Origin-Opener-Policy Headers Locally

Want to keep popups? Serve headers yourself. Three spots: IIS Express (web.config), static server flags, or index.html meta.

Option A: web.config (IIS Express)

Drop this in your WASM project’s root (publish-ready too):

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
 <httpProtocol>
 <customHeaders>
 <add name="Cross-Origin-Opener-Policy" value="same-origin" />
 <add name="Cross-Origin-Embedder-Policy" value="require-corp" />
 </customHeaders>
 </httpProtocol>
 <staticContent>
 <mimeMap fileExtension=".wasm" mimeType="application/wasm" />
 </staticContent>
 </system.webServer>
</configuration>

Restart VS/IIS Express. Headers stick to all static files.

Option B: dotnet serve (CLI dev)

bash
dotnet tool install -g Microsoft.dotnet-dev-certificate
dotnet serve --https --cors --headers "Cross-Origin-Opener-Policy=same-origin,Cross-Origin-Embedder-Policy=require-corp" -p 1234

Option C: index.html meta (fallback, client-side)

html
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">
<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">

Popup mode resumes. Verify in DevTools Network tab—headers present? Popup closes cleanly.

same-origin safest; unsafe-none looser but riskier. Matches Azure’s defaults.


Validate Entra ID App Registration and Blazor WASM Configuration

Double-check these—missteps mimic COOP errors.

  1. Entra Portal: App registrations > Your App > Authentication. Redirect URIs: https://localhost:1234/authentication/login-callback (port matters). Disable “ID tokens” and “Access tokens” implicit grants—use code flow.

  2. appsettings.json:

json
{
 "AzureAd": {
 "Authority": "https://login.microsoftonline.com/your-tenant-id",
 "ClientId": "your-client-id",
 "ValidateAuthority": true
 }
}
  1. Program.cs scopes match API permissions.

  2. .NET 10? Latest templates include MSAL 2.x—update NuGets: Microsoft.Authentication.WebAssembly.Msal.

Clear site data (DevTools > Application > Storage > Clear). HTTPS enforced? Cert trusted?


Troubleshooting Common Issues and Best Practices

Issue Cause Fix
Still blocked post-headers Cache/stale service worker Hard refresh (Ctrl+Shift+R), unregister SW in DevTools
Firefox quirks Stricter COEP Add Cross-Origin-Resource-Policy: cross-origin
Azure AD password protection loops Conditional policies Exclude localhost in Entra Conditional Access
WASM threading fails Missing SharedArrayBuffer COEP: require-corp mandatory
Production preview SWA custom headers Already handled; test with az staticwebapp dev locally

Best practices: Default to redirect for simplicity. Monitor MSAL docs. For azure entra id hybrids (like azure ad connect), test both modes. Profile perf—headers add negligible overhead.

Exit azure ad device joins? Irrelevant here, but for full-stack, script dsregcmd /leave.


Sources

  1. Cross-origin-opener-policy error using localhost in Blazor WASM — User-reported identical Entra ID standalone localhost popup failure: https://stackoverflow.com/questions/79868046/cross-origin-opener-policy-error-using-localhost-in-blazor-wasm
  2. Standalone Blazor WASM needs COOP/COEP headers — Official discussion on missing headers in WASM static serving vs Azure SWA: https://github.com/dotnet/aspnetcore/issues/42114
  3. Blazor WASM MSAL popup blocked by COOP — MSAL AuthenticationService window.opener workaround details: https://github.com/dotnet/aspnetcore/issues/40610
  4. Secure ASP.NET Core Blazor WebAssembly with Microsoft Entra ID — Official config for standalone Entra ID redirect URIs and MSAL setup: https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/standalone-with-microsoft-entra-id?view=aspnetcore-9.0
  5. How to enable CORS on Blazor WebAssembly with custom headers — web.config example for COOP/COEP in IIS: https://stackoverflow.com/questions/72156797/how-to-enable-cors-on-a-blazor-webassembly-hosted-by-a-razor-pages-app-with-con
  6. Blazor WASM dev server requires COOP for SharedArrayBuffer — Dev server header needs for WASM features: https://github.com/dotnet/aspnetcore/issues/38327

Conclusion

Nail the cross-origin-opener-policy error in your Blazor WASM standalone with Entra ID by flipping to redirect mode—zero headers, instant local dev wins—or layering in COOP: same-origin via web.config for popup loyalty. Both keep Azure Static Web Apps humming untouched. Prioritize redirects for reliability, validate your Microsoft Entra ID setup, and you’re shipping secure auth flows confidently. Questions on azure ad tweaks? Drop 'em below.

Authors
Verified by moderation
NeuroAnswers
Moderation
Fix Cross-Origin-Opener-Policy Error in Blazor WASM Entra ID Localhost