Entra sign-in does not offer passkeys when the User-Agent contains "Electron/" - is this intended?

carlintveld 31 Reputation points
2026-11-03T08:03:52.2466667+00:00

Setup

An Electron 44 app (Chromium 152) on Windows 11 embeds a browser tab. WebAuthn works in that browser: PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() returns true, and passkeys work on webauthn.io with 1Password as the passkey provider.

Problem

On login.microsoftonline.com, after entering the username, Entra does not offer the passkey / security key option. Only Authenticator and password options appear.

A/B test (same build, same machine, same account)

  • The request header User-Agent contains ... Chrome/152.0.7977.169 Electron/44.10.0 Safari/537.36: no passkey option.
  • Same request with only the Electron/44.10.0 token removed from the header (only for Microsoft sign-in hosts): the passkey option appears and sign-in works.

navigator.userAgent in the page was identical in both runs. Only the outgoing request header changed.

Questions

  1. Is hiding passkeys for User-Agents that contain Electron/ intended?
  2. Is the Electron token the only trigger, or does the app token also matter?
  3. What do you recommend for desktop apps that embed Chromium: the system browser, a changed User-Agent, or something else?

Note: I have not captured the outgoing header on the wire. The conclusion is based on the behavior change after the rewrite. The docs matrix at https://learn.microsoft.com/entra/identity/authentication/concept-fido2-compatibility lists only Chrome, Edge, Firefox and Safari.

Microsoft Security | Microsoft Entra | Microsoft Entra ID
0 comments No comments

2 answers

Sort by: Most helpful
  1. Paul Thomas 80 Reputation points
    2026-11-03T15:07:52.8133333+00:00

    On questions 1 and 2, Microsoft doesn't document a rule about Electron/, so only Microsoft can confirm whether it's intended. What the documentation does say is consistent with your A/B result. The FIDO2 developer guidance says that for web sign-in "the availability of FIDO2 passwordless authentication … will depend on the combination of browser and platform", and the Windows column of the passkey compatibility matrix lists Chrome, Edge and Firefox. An embedded Electron browser isn't one of them, so Entra deciding from the request's User-Agent not to offer passkeys fits the documented behaviour.

    Narrowing down question 2

    Since you already rewrite the header for Microsoft sign-in hosts, you can test the token question directly:

    Remove only your app's token and keep Electron/44.10.0.

    Keep your app's token and remove only Electron/44.10.0.

    To rule out other differences, log the header actually sent. session.webRequest.onBeforeSendHeaders gives you the outgoing User-Agent for login.microsoftonline.com requests. If the passkey option appears or disappears with the Electron/ token alone, include that minimal A/B result and a correlation ID in a support case.

    Question 3: what to use instead

    I wouldn't ship the User-Agent rewrite. It relies on undocumented classification that can change at any time. For Windows apps, Microsoft's FIDO2 developer guidance recommends, in this order: the Windows broker (WAM), then WebView2 for an embedded browser, then the system browser. For an Electron app, MSAL Node covers the two most practical options.

    Option A: Windows broker (WAM). Install @azure/msal-node-extensions and register the redirect URI ms-appx-web://Microsoft.AAD.BrokerPlugin/<client-id> on the app registration. The broker is Windows-only, and MSAL Node doesn't fall back to another flow if the broker fails.

    const { PublicClientApplication } = require("@azure/msal-node");
    const { NativeBrokerPlugin } = require("@azure/msal-node-extensions");
    
    const pca = new PublicClientApplication({
      auth: { clientId: "<client-id>", authority: "https://login.microsoftonline.com/<tenant-id>" },
      broker: { nativeBrokerPlugin: new NativeBrokerPlugin() },
    });
    
    const result = await pca.acquireTokenInteractive({
      scopes: ["User.Read"],
      windowHandle: mainWindow.getNativeWindowHandle(), // keeps the prompt on top of your window
    });
    
    

    Option B: system browser. Register http://localhost as a redirect URI under Mobile and desktop applications. acquireTokenInteractive runs the loopback listener and the PKCE exchange for you; you only open the URL: The user then signs in with their default browser. If that's Edge, Chrome or Firefox on Windows, passkeys are offered as documented, including passkeys in 1Password.

    const { shell } = require("electron");
    
    const result = await pca.acquireTokenInteractive({
      scopes: ["User.Read"],
      openBrowser: async (url) => { await shell.openExternal(url); },
    });
    
    
    

    I haven't run this against an Electron 44 build myself, so test both options with your tenant. The API calls are taken from the MSAL Node documentation.

    If this helps, please consider marking it as the accepted answer so others can find it.

    Was this answer helpful?

    0 comments No comments

  2. AI answer

    2026-11-03T08:05:02.2233333+00:00

    Use MSAL Node with authorization-code flow plus PKCE and perform interactive Entra authentication in the Windows system browser; do not depend on removing Electron/ from the User-Agent.

    What the A/B test establishes

    Your result strongly indicates that Entra’s server-side client classification changes when the outgoing header contains Electron/. It also explains why identical navigator.userAgent values do not produce identical results: JavaScript sees the renderer value, while login.microsoftonline.com makes decisions from the HTTP request and other request context.

    However, the public documentation does not specify:

    • An intentional rule that suppresses passkeys whenever Electron/ is present.
    • Whether matching is based only on Electron/, the full product/version token, another application token, or multiple signals.
    • A supported User-Agent override that makes Electron an officially supported Chrome client.

    Consequently, the exact heuristic—and whether the observed behavior is intentional or an implementation detail—cannot be confirmed publicly.

    The published Windows browser matrix lists Chrome, Edge, and Firefox, but not Electron or generic Chromium embeddings.^1^ A successful WebAuthn capability check or webauthn.io test proves that the embedded Chromium surface can perform WebAuthn; it does not establish that Entra classifies that surface as a supported browser.

    For Windows applications, Microsoft recommends these options in order:

    1. Windows Authentication Manager (WAM) for supported .NET/MSAL desktop applications.
    2. WebView2 when FIDO2 must remain embedded.
    3. The system browser, which is the normal fallback for desktop MSAL applications.^2^

    For an Electron application, the practical supported direction is:

    1. Register the application as a Mobile and desktop application.
    2. Configure http://localhost as its redirect URI.
    3. Use MSAL Node, not MSAL Browser, for the native desktop authentication flow.
    4. Generate an authorization URL using authorization-code flow with PKCE.
    5. Open that URL in the user’s default browser.
    6. Receive the redirect on a loopback listener and redeem the authorization code through MSAL Node.
    7. Return the resulting account/session state to the Electron renderer.

    Microsoft’s Electron tutorial specifically uses MSAL Node, authorization-code flow with PKCE, and a desktop http://localhost redirect; it also states that MSAL Browser is not supported for Electron applications.^3^ MSAL Node’s documented flow is to create the authorization URL, let the user open it in a browser, and then pass the returned code to acquireTokenByCode().^4^

    At a high level, the Electron handoff looks like this:

    const { shell } = require("electron");
    
    // authProvider starts a localhost listener and creates
    // an MSAL Node authorization URL containing PKCE parameters.
    const authorizationUrl = await authProvider.getAuthorizationUrl();
    
    await shell.openExternal(authorizationUrl);
    
    // The localhost callback supplies the authorization code;
    // MSAL Node validates state and redeems it with the PKCE verifier.
    const result = await authProvider.waitForRedirectAndAcquireToken();
    

    Use MSAL’s state, nonce, PKCE, token-cache, and code-redemption functionality rather than implementing the protocol manually. Microsoft recommends supported authentication libraries instead of manually issuing raw OAuth requests.^5^

    Should the User-Agent be rewritten?

    Treat the rewrite as a diagnostic result, not a production solution:

    • It causes Entra to classify an Electron surface as another browser without making the surface equivalent to that browser.
    • The matching behavior is undocumented and can change independently of Electron or Chromium.
    • Other authentication stages, Conditional Access checks, or future passkey behavior could classify the client differently.

    If keeping authentication embedded is mandatory on Windows, evaluate WebView2, which Microsoft documents as supporting FIDO and Windows Hello.^6^ Otherwise, the system-browser/MSAL Node design is the appropriate Electron path.

    To obtain an authoritative answer about the exact Electron/ or app-token matching rule, open a Microsoft support case with the tenant, correlation ID, UTC timestamp, Electron/Chromium versions, and sanitized A/B description. Do not include authorization codes, tokens, cookies, or passkey artifacts.


    References

    1. Passkey (FIDO2) authentication matrix with Microsoft Entra ID
    2. Support passwordless authentication with FIDO2 keys in apps you develop
    3. Tutorial: Sign in users and call the Microsoft Graph API in an Electron ...
    4. Desktop app that calls web APIs: Acquire a token interactively
    5. Microsoft identity platform and OAuth 2.0 authorization code flow
    6. Acquiring tokens interactively
    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.