openiddict / openiddict-core

Flexible and versatile OAuth 2.0/OpenID Connect stack for .NET
https://openiddict.com/
Apache License 2.0
4.34k stars 507 forks source link

Microsoft web provider - error with SPA / PKCE app registration #2144

Closed ZephyrZiggurat closed 1 month ago

ZephyrZiggurat commented 2 months ago

Personal contribution

Version

5.7.0

Provider name

Microsoft

Describe the bug

I swapped out GitHub for Microsoft in the Balosar sample project, but i'm getting The token request was rejected by the remote server..

The redirect url is added as a SPA in Entra. I believe the only other change made during registration was adding profile/openid to the permissions.

I found this issue https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/2482 that mentions an Origin header is required. I also found this blog that says the value Origin header can be anything: https://www.frodehus.dev/using-pkce-with-asp-net-core-webapp-and-azure-ad/

I made this additional change to the code:

options.UseSystemNetHttp()
       .ConfigureHttpClient(x => x.DefaultRequestHeaders.Add("Origin", "x")) // added this; literally sending just an x
       .SetProductInformation(typeof(Startup).Assembly);

and it worked beautifully.

So, my question now is: should the web provider send the Origin header or is this just a known issue/configuration that I missed?

To reproduce

Using the Balosar sample, all I did was swap out GitHub config for Microsoft:

.AddMicrosoft(ms =>
{
    ms.SetTenant("tenant-guid");
    ms.SetClientId("client-guid");
    ms.SetRedirectUri(new Uri("callback/login/entra", UriKind.RelativeOrAbsolute));
});

The console shows:

OpenIddict.Client.OpenIddictClientDispatcher: Information: The token request was successfully sent to https://login.microsoftonline.com/tenant-guid/oauth2/v2.0/token: {
  "grant_type": "authorization_code",
  "code": "[redacted]",
  "code_verifier": "code",
  "redirect_uri": "https://localhost:44310/callback/login/entra",
  "client_id": "client-guid"
}.
OpenIddict.Client.OpenIddictClientDispatcher: Information: The token response returned by https://login.microsoftonline.com/tenant-guid/oauth2/v2.0/token was successfully extracted: {
  "error": "invalid_request",
  "error_description": "AADSTS9002327: Tokens issued for the 'Single-Page Application' client-type may only be redeemed via cross-origin requests. Trace ID: guid Correlation ID: guid Timestamp: 2024-07-17 02:14:07Z",
  "error_codes": [
    9002327
  ],
  "timestamp": "2024-07-17 02:14:07Z",
  "trace_id": "guid",
  "correlation_id": "guid"
}.
OpenIddict.Client.OpenIddictClientDispatcher: Information: The token request was rejected by the remote authorization server: same as above.
OpenIddict.Client.OpenIddictClientDispatcher: Information: The response was successfully returned as a plain-text document: {
  "error": "invalid_request",
  "error_description": "The token request was rejected by the remote server.",
  "error_uri": "https://documentation.openiddict.com/errors/ID2147"
}.

Exceptions (if any)

No response

kevinchalet commented 2 months ago

Hi,

The redirect url is added as a SPA in Entra.

Is there any particular reason for choosing an SPA app? It would make sense if the OpenIddict client had a Browser/WASM integration, but it cannot currently be used in such apps (at least not yet 😄).

In Balosar, GitHub authentication is purely handled server-side, so the best application model to use in that case would be "Web".

Note: you can restrict the Origin-related configuration to the MSFT provider only:

options.UseSystemNetHttp()
       .ConfigureHttpClient(Providers.Microsoft, client => client.DefaultRequestHeaders.Add("Origin", "x"))
       .SetProductInformation(typeof(Startup).Assembly);
ZephyrZiggurat commented 2 months ago

Thank you for the information! I registered it as an SPA/PKCE due to a bad assumption. MS recommends using PKCE auth codes but I didnt consider that the login is being performed from the server-side in the case of AddClient.

Are there any downsides to leaving it as a SPA?

kevinchalet commented 1 month ago

MS recommends using PKCE auth codes but I didnt consider that the login is being performed from the server-side in the case of AddClient.

Don't assume .AddClient() always means "server-side": the OpenIddict client can also be natively used in desktop and mobile apps thanks to its OpenIddict.Client.SystemIntegration package (and in this case, the "native" application model is the best option).

Are there any downsides to leaving it as a SPA?

You can't define your app as a confidential client and so can't get a client secret. "Web" is really the best option for a web app.