AzureAD / microsoft-authentication-library-for-dotnet

Microsoft Authentication Library (MSAL) for .NET
https://aka.ms/msal-net
MIT License
1.39k stars 340 forks source link

[Bug] When session and cookie timeout call to Azure B2C authorize endpoint fails with CORS error. #3917

Closed mschaefer-gresham closed 1 year ago

mschaefer-gresham commented 1 year ago

Which version of MSAL.NET are you using? Microsoft.AspNetCore.Authentication.OpenIdConnect 7.0.2

Platform .NET 7

What authentication flow has the issue?

Other?

Is this a new or existing app? This is a new app or experiment.

Expected behavior If the server initiates a call to the authorize endpoint when a request arrives after the session and cookie expire, then the call to the authorize endpoint does not result in a CORS error.

Actual behavior My frontend is a React SPA. There is no javascript library (e.g. MSAL) for auth and no tokens in the browser - only the cookie from the server. My server is acting as a Backend-For-Front-End Gateway. It handles the cookie and token management for logging in to the SPA using Microsoft.AspNetCore.Authentication.OpenIdConnect. And it proxies API requests using YARP.

The core team told me this is a MASL.NET issue and to open a ticket here.

The server is configure using the typical:

builder.Services.AddAuthentication(options => ...)
.AddCookie(options => ...)
.AddOpenIdConnect(options => ...)

Logging in and logging out from the SPA work just fine. The issue occurs when the cookie and session timeout (they are set to be the same). On the first API call after the session timeout the server ends up in the OnRedirectToIdentityProvider event handler which I assume is the result of the server initiating a call to the authorize endpoint that I see in the browser. But this always results in a CORS error:

image image

My app's Redirect URIs look like this in Azure B2C:

image

Why does this result in a CORS error? When login or logout is initiated from the frontend I don't get these CORS errors - only in this case when it's initiated from the server. To handle the scenario I return a 401 from OnRedirectToIdentityProvider. This prevents the call to the authorize endpoint. I then handle the 401 in the frontend, redirect the browser to the login endpoint, and everything works fine.

This thread is Okta related, but it looks like the same issue. The Okta team states "The /authorize call requires a user-agent (browser) redirect. Okta will not set access control headers for this endpoint even if your trusted origins is setup correctly.", and points to the documentation documentation.

So this seems to confirm the behavior I see. The call to authorize endpoint must originate from the UI.

Can you please confirm or deny this? If I am wrong, please explain why the CORS error occurs and how it can be avoided.

jmprieur commented 1 year ago

@mschaefer-gresham Microsoft.AspNetCore.Authentication.OpenIdConnect does not leverage MSAL.

See in the logs, the client-SKU is ID_NETSTANDARD2, which is ASp.NET Core using Identity.Model

Also you mention you have a SPA, but this is .NET Core with OpenIdConnect.

Did you try to use Microsoft.Identity.Web (which does it all for you): https://github.com/AzureAD/microsoft-identity-web/wiki/web-apps or https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview?tabs=aspnetcore

mschaefer-gresham commented 1 year ago

@jmprieur and @neha-bhargava

Thank you for taking the time to look at this and providing a suggestion.

Unfortunately, I could not use Microsoft.Identity.Web even if I wanted to (we did try btw). M.I.W. requires using MSAL.js in the browser, which results in insecure tokens in the browser. The only way to silently renew the access token using M.I.W. is by using MSAL.js - M.I.W. prevents you from requesting an access token or refresh token in the server. Having tokens in the browser is prohibited by our security team. This is a known security flaw which is solved by the BFF Pattern, which M.I.W. does not support. This leaves us the option of paying Duende or implementing it ourselves.

It would be fantastic if Microsoft supported this pattern and provided samples for how to achieve it.

Until that happens we are left to implement this pattern ourselves using Microsoft.AspNetCore.Authentication.OpenIdConnect. It would be extemely helpful if you could address the question I asked above. It would be useful for those of us trying to implement this needed pattern.

bgavrilMS commented 1 year ago

CC @peterzenz for the SPA implication

bgavrilMS commented 1 year ago

@mschaefer-gresham - since MSAL is not being used here, as ASP.NET Core goes to the authorization endpoint on its own, I would recommend you reach out to ASP.NET Core - https://github.com/dotnet/aspnetcore

Note that we are not aware of any security flaw in using SPAs and many Microsoft products, including the Azure Portal, obtain tokens in SPA.

Pieter-1337 commented 3 months ago

I am experiencing the same issue, simply loggin/loggin out through the registered redirects uri in the app registration works, however if a challenge due a cookie/session timeout is triggered from the server (which indeed as described above acts as the BFF and also serves the frontend) it results in a CORS error image image I would expect this call to success just the same as when we are just logging in via the signin-oidc uri