Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] PublicClientApplication acts like confidential client application #818

Open
ShmuelCammebys opened this issue May 20, 2024 · 12 comments
Labels
Bug Something isn't working, needs an investigation and a fix public-client For questions/issues related to public client apps

Comments

@ShmuelCammebys
Copy link

Library version used

1.14.3

Java version

17

Scenario

PublicClient (AcquireTokenInteractive, AcquireTokenByUsernamePassword)

Is this a new or an existing app?

This is a new app or experiment

Issue description and reproduction steps

Even though my application is a public client application, and uses MSAL for iOS and Android perfectly fine, when I try connecting to the same client id on Desktop (after registering the application for desktop), it says I need a client_secret:

com.microsoft.aad.msal4j.MsalServiceException: AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'. Trace ID: f8ca8882-0b92-4e97-9fac-bc491d333900 Correlation ID: f43ae1e8-856f-4ef3-a259-fa58a651ab2c Timestamp: 2024-05-08 10:58:05Z

Relevant code snippets

val application = PublicClientApplication
                    .builder(BuildConfig.ENTRA_CLIENT_ID)
                    .authority(BuildConfig.ENTRA_AUTHORITY)
                    .build()
                val prefs = Preferences.userRoot()

                val storedAccountId = prefs.get(Constants.ENTRA_ACCOUNT_IDENTIFIER, "")
                val redirectUri = "http://localhost:55259"
                val scopes = setOf("User.Read")

                val authResult = application.run {
                    if (storedAccountId.isNullOrBlank()) {
                        acquireToken(
                            InteractiveRequestParameters
                                .builder(URI(redirectUri))
                                .scopes(scopes)
                                .build()
                        )
                    } else {
                        acquireTokenSilently(
                            SilentParameters
                                .builder(
                                    scopes,
                                    application
                                        .accounts
                                        .join()
                                        .find { it.tenantProfiles[storedAccountId] != null }
                                )
                                .build()
                        )
                    }
                }
                entraIdToken = authResult.join().idToken()

Expected behavior

Returns auth token

Identity provider

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

Regression

No response

Solution and workarounds

No response

@ShmuelCammebys ShmuelCammebys added needs attention Automatically used when an issue is created through an issue template untriaged Automatically used when an issue is created through an issue template labels May 20, 2024
@Avery-Dunn Avery-Dunn added Bug Something isn't working, needs an investigation and a fix public-client For questions/issues related to public client apps and removed needs attention Automatically used when an issue is created through an issue template untriaged Automatically used when an issue is created through an issue template labels May 20, 2024
@Avery-Dunn
Copy link
Collaborator

Hello @ShmuelCammebys : Just some clarifications:

  • When you say that it "uses MSAL for iOS and Android perfectly fine", you mean the same Azure app registration/client ID/authority/etc. configuration works for those other MSALs but not MSAL Java?
  • And "registering the application for desktop" means that you configured the app in Azure to allow public client flows? (Authentication tab -> advanced settings -> 'Allow public client flows')
  • In your code you have both an interactive and silent request. Is the error being thrown during the interactive request? And not during a refresh attempt in the silent request?

That 'AADSTS' is an error message coming directly from the token service rather than from our library, so my first guess is a configuration issue in Azure. If you ran your code immediately after configuring the app then maybe the config was slow to propagate? You can also check the 'Manifest' tab to see if the 'allowPublicClient' field is set to true.

@ShmuelCammebys
Copy link
Author

ShmuelCammebys commented May 21, 2024

@Avery-Dunn

  1. Correct
  2. Don't public client flows have to be allowed for MSAL iOS and Android to work? I mean that the localhost redirect URI was added under the desktop platform.
  3. Interactive.

This has been happening for months after we changed the configuration.

I'd prefer to take this as far as I can before having to contact the security/DevOps team about the Azure config, but if more info is needed, so be it.

@Avery-Dunn
Copy link
Collaborator

Avery-Dunn commented May 21, 2024

Don't public client flows have to be allowed for MSAL iOS and Android to work? I mean that the localhost redirect URI was added under the desktop platform.

I'm not too familiar with the implementations for MSAL iOS and Android, but historically the MSALs for mobile scenarios work a bit differently so that setting might not be required for them.

Just looking at one of Android's samples and one for macOS I noticed that the instructions don't mention that "Allow public client flows" setting.

Since there is different platform config options for "Mobile and desktop applications" (iOS/Android) and "Web applications" (more common for Java, .NET, Python, etc.) at least some config in Azure is difference for these scenarios. I can't find good documentation covering exactly when that public client flows setting should be used, but just from experience my understanding was that it's needed for any public client scenario so it may be the missing piece here. Can you confirm whether or not it's enabled for your app?

@rayluo
Copy link

rayluo commented May 21, 2024

  • And "registering the application for desktop" means that you configured the app in Azure to allow public client flows? (Authentication tab -> advanced settings -> 'Allow public client flows')

Don't public client flows have to be allowed for MSAL iOS and Android to work?

Not really. That "allow public client flows" was historically used to enable username password flow and/or device code flow for confidential clients. Mobile platforms like iOS and Android do not require this setting to work.

I mean that the localhost redirect URI was added under the desktop platform.

@ShmuelCammebys , do you have a redirect URI http://localhost, with or without different port, registered as a Web platform in your app's registration? If so, that would cause this symptom. Either remove it from Web platform, or use a different redirect URI with a different /path. Port alone can't be used to differentiate multiple localhost URIs.

@ShmuelCammebys
Copy link
Author

This is the config:
image

@rayluo
Copy link

rayluo commented May 21, 2024

@ShmuelCammebys , no, I mean this setting.

Configure platforms

There shouldn't be a web platform with http://localhost:12345 (whatever port number it might be) registered for your app. Can you double check to confirm or rule out this possibility?

@Avery-Dunn
Copy link
Collaborator

Avery-Dunn commented May 21, 2024

@ShmuelCammebys It looks like I got some public client flows mixed up, and interactive flow is one where you don't need that 'allow public client' config, and like @rayluo said having a "Web application" platform is also incorrect.

I just did some testing to confirm, this is the config that worked for me:

image

@ShmuelCammebys
Copy link
Author

ShmuelCammebys commented May 22, 2024

@rayluo Yes, there was a SPA redirect URI to localhost. However, when the redirect URI for desktop was set to http://localhost:55259/desktop, MSAL4J hangs at CompletableFuture.join and returns "No Authorization code was returned from the server" (Caused by: java.util.concurrent.CompletionException: com.microsoft.aad.msal4j.MsalClientException: No Authorization code was returned from the server). The browser opens a window to that URL, but MSAL4J never receives the response.

@rayluo
Copy link

rayluo commented May 22, 2024

Yes, there was a SPA redirect URI to localhost.

I haven't personally tried it, but I suppose SPA does not require a redirect URI in the Web platform either. If there is no other usage for a Web platform, @ShmuelCammebys, can you simply delete the http://localhost:whateverport from your Web platform setting, and try again?

However, when the redirect URI for desktop was set to http://localhost:55259/desktop, MSAL4J hangs ...

That might be a different issue. Not all the MSALs currently support a path /path in their interactive flow. Not sure whether that is also applicable to MSAL Java. I'll leave that for @Avery-Dunn to comment. Regardless, I still think there is a good chance that (1) using a path-less localhost redirect URI in your public client; plus (2) removing http://localhost:whateverport from your Web platform setting, shall work.

@Avery-Dunn
Copy link
Collaborator

However, when the redirect URI for desktop was set to http://localhost:55259/desktop, MSAL4J hangs ...

That might be a different issue. Not all the MSALs currently support a path /path in their interactive flow. Not sure whether that is also applicable to MSAL Java.

I'm realizing now that our docs don't explicitly say a path will cause issues, but yes only http://localhost or http://localhost:port should be used: https://learn.microsoft.com/en-us/entra/msal/java/getting-started/acquiring-tokens-interactively

You may be able to use SystemBrowserOptions or a custom HTTPClient to handle a redirect with a path, but that's probably more complicated than it's worth.

@ShmuelCammebys
Copy link
Author

@Avery-Dunn @rayluo Unfortunately, a localhost redirect URI seems to be required in both platforms. Local web development requires a redirect URI, and our local desktop app development also requires a redirect uri. How can we accomodate both?

@rayluo
Copy link

rayluo commented May 23, 2024

@Avery-Dunn @rayluo Unfortunately, a localhost redirect URI seems to be required in both platforms. Local web development requires a redirect URI, and our local desktop app development also requires a redirect uri. How can we accomodate both?

Generally, a web app is more flexible in terms of having their redirect URI - and its path - configurable; besides, web app typically uses a path in their redirect URI (such as http://localhost/auth). So, a usual workaround is to have your web app to use a redirect URI with path, thus save the path-less redirect URI http://localhost for your desktop app. That way, you can accommodate both, @ShmuelCammebys

You may be able to use SystemBrowserOptions or a custom HTTPClient to handle a redirect with a path, but that's probably more complicated than it's worth.

Indeed. There is a better approach. FWIW, there is a pending improvement for MSAL Python to support customizable path in Desktop's interactive auth, but that PR has been postponed indefinitely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working, needs an investigation and a fix public-client For questions/issues related to public client apps
Projects
None yet
Development

No branches or pull requests

3 participants