Skip to content

[Bug] Unable to launch default browser on Windows when running in headless mode #999

Open

Description

@humbertogrobles

Library version used

1.20.1

Java version

21.0.8

Scenario

PublicClient (AcquireTokenInteractive, AcquireTokenByUsernamePassword)

Is this a new or an existing app?

This is a new app or experiment

Issue description and reproduction steps

I'm working on an extension for VS Code that runs a Java backend server in headless mode. I'm planning on adding support for EntraID, and from what I understand that requires being able to open the default browser to authenticate. Since the backend server is running in headless mode (java.awt.headless=true), the browser fails to be launched on Windows, since the following method relies on java.awt.Desktop which is not available on headless mode:

AcquireTokenByInteractiveFlowSupplier class:

private static void openDefaultSystemBrowserInWindows(URL url){

    try {

        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {

            Desktop.getDesktop().browse(url.toURI());

            LOG.debug("Opened default system browser");

        } else {

            throw new MsalClientException("Unable to open default system browser",

                    AuthenticationErrorCode.DESKTOP_BROWSER_NOT_SUPPORTED);

        }

    } catch (URISyntaxException | IOException ex) {

        throw new MsalClientException(ex);

    }

}

On the other hand, when running on Mac, that platform's method doesn't rely on AWT, and instead relies on a native command, so launching the browser works without problems even in headless mode:

private static void openDefaultSystemBrowserInMac(URL url){

    Runtime runtime = Runtime.getRuntime();

    try {

        // CodeQL [SM00680] False positive: this URL is validated earlier in the interactive flow

        runtime.exec("open " + url);

    } catch (IOException e) {

        throw new RuntimeException(e);

    }

}

Relevant code snippets

Relevant stacktrace:

Suppressed: com.microsoft.aad.msal4j.MsalClientException: Unable to open default system browser
		at com.microsoft.aad.msal4j@1.20.1/com.microsoft.aad.msal4j.AcquireTokenByInteractiveFlowSupplier.openDefaultSystemBrowserInWindows(AcquireTokenByInteractiveFlowSupplier.java:155)
		at com.microsoft.aad.msal4j@1.20.1/com.microsoft.aad.msal4j.AcquireTokenByInteractiveFlowSupplier.openDefaultSystemBrowser(AcquireTokenByInteractiveFlowSupplier.java:139)
		at com.microsoft.aad.msal4j@1.20.1/com.microsoft.aad.msal4j.AcquireTokenByInteractiveFlowSupplier.getAuthorizationResult(AcquireTokenByInteractiveFlowSupplier.java:70)
		at com.microsoft.aad.msal4j@1.20.1/com.microsoft.aad.msal4j.AcquireTokenByInteractiveFlowSupplier.execute(AcquireTokenByInteractiveFlowSupplier.java:46)
		at com.microsoft.aad.msal4j@1.20.1/com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:69)
		at com.microsoft.aad.msal4j@1.20.1/com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:18)
		at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
		... 6 more

Expected behavior

To be able to launch the browser needed for authentication on Windows with headless mode, just like it can be done on Mac.

Identity provider

Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)

Regression

No response

Solution and workarounds

A suggestion would be to also use a native method on Windows, like "start".

Activity

  1. added
    needs attentionAutomatically used when an issue is created through an issue template
    untriagedAutomatically used when an issue is created through an issue template
    on Oct 16, 2025
  2. Avery-Dunn commented on Oct 16, 2025

    @Avery-Dunn
    Contributor

    Hello humbertogrobles : Could you describe your scenario a bit more? If it's running on a backend server, why would you need to use a flow that is meant for direct user interaction?

    Public client flows are intended for apps that run on the client's device, if you have a backend server you'd probably want to use a confidential client flow: https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-applications

    Have a look at some of our confidential client/server-side samples, one of them might be better suited for what you're doing: https://github.com/Azure-Samples/ms-identity-msal-java-samples/tree/main/1-server-side

  3. added
    QuestionAny questions about how MSAL Java works
    Requires more infoMore information is needed, from either the person who opened the issue or another team
    and removed
    needs attentionAutomatically used when an issue is created through an issue template
    untriagedAutomatically used when an issue is created through an issue template
    on Oct 16, 2025
  4. humbertogrobles commented on Oct 16, 2025

    @humbertogrobles
    Author

    Avery-Dunn thanks for the prompt response! Let me explain a bit more. In this VS Code extension we're currently using the JDBC drivers to connect to Oracle DBs. This is out in production now, and we're looking into adding support for EntraID by using the ojdbc extensions, in particular ojdbc-provider-azure:
    https://github.com/oracle/ojdbc-extensions/blob/main/ojdbc-provider-azure/README.md

    This extension uses MSAL4J as an internal dependency to do the authentication. The backend server we're using is launched to support the VS Code extension frontend and execute all the business logic, and all of this runs locally on the user's device.

    Regarding the value I chose for the "Scenario" field, I didn't really know what to put there, so I chose what I thought was the correct value, but let me know if you need any more info to determine if that's the correct scenario or not.

  5. Avery-Dunn commented on Oct 16, 2025

    @Avery-Dunn
    Contributor

    For the scenario, I'd recommend you read up on that first link I pasted about public vs confidential client: https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-applications

    But in short, if your users need to enter their own individual credentials (for example, on a login screen) then it's a public client scenario, and if your application uses its own credentials for all your users' requests (for example, a certificate or managed identity) then it's confidential client.

    However, just to clarify: you aren't using the msal4j package directly, right? You're using the auth APIs in that ojdbc-provider-azure package, which uses msal4j under the hood? If so, it may take a while to update: they seem they get msal4j through the azure-identity package, so it'd be multiple layers of package updates.

    We can look into alternate browser behavior on our end, but I wouldn't even be able to give an ETA on when the investigation would start. In the meantime, it looks like that ojdbc-provider-azure package has other options for the credentials, so maybe you can look into one of the non-browser options: https://github.com/oracle/ojdbc-extensions/blob/main/ojdbc-provider-azure/README.md#defaultazurecredential

  6. humbertogrobles commented on Oct 16, 2025

    @humbertogrobles
    Author

    Thanks for the information Avery-Dunn . I've read the document and I think that the Public Client Scenario is correct.

    Regarding the usage of MSAL4J, you're correct, we're not using it directly, we use the ojdbc drivers, which dynamically load any providers present in the classpath (in this case ojbc-provider-azure) which then internally uses msal4j. Thanks for looking into this, I understand it may take time to solve and for the fix to reach all the way down the pipeline, and in the meantime will look into the other options you mentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAny questions about how MSAL Java worksRequires more infoMore information is needed, from either the person who opened the issue or another team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions