Blazor WASM auth
-
Here's my current settings and code:
"FusionAuth": { "Authority": "http://localhost:9011/", "ClientId": "[id]", "ClientSecret": "[secret]", "RedirectUri": "https://localhost:7281/authentication/login-callback", "PostLogoutRedirectUri": "https://localhost:7281/authentication/logout-callback", "ResponseType": "code" },
builder.Services .AddOidcAuthentication(options => { builder.Configuration.Bind("FusionAuth", options.ProviderOptions); options.ProviderOptions.DefaultScopes.Add("openid"); });
-
@kern-markus , my best guess at this point is that you are right in that it comes down to the way the request is sent and not having the expected headers to allow the CORS. I don't see any open issues for FusionAuth that may implement the suggested changes and I am not aware of a workaround other than to change the technology used for the application or insert some sort of proxy that can add the appropriate headers.
-
Thank you!
-
@mark-robustelli I'm also interested in using Fusion Auth in a .NET 7 Blazor application. I went through similar steps shown in the thread and arrived at the same result.
@kern-markus Did you ever get anything working, or did you go a different route?
-
Would love if this was something FusionAuth could improve on. I have auth flows for a number of SPAs in React set up, which work fairly well. But FusionAuth doesn't seem to work well with the standard Blazor WASM auth template, using PKCE and OIDC. That template works fine with other auth providers though - Okta, Azure AD, and Google were all basically plug and play. Plus Microsoft doesn't exactly make it easy to open up the hood and figure out what's going on, so I feel pretty stuck.
-
@cody I got to the point of the WASM application hanging on "Completing login...", and spent some time tonight trying to figure out where things are going wrong. In my WASM project, I was using
Microsoft.Authentication.WebAssembly.Msal
instead ofMicrosoft.AspNetCore.Components.WebAssembly.Authentication
, so maybe that was part of my problem to begin with. But none of this was obvious to me from the start, since initializing a Blazor WASM project set me up that way to begin with based on docs here saying to use this command:dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" -o {PROJECT NAME} --tenant-id "{TENANT ID}"
Anyways, perhaps some of the following info may help someone else. I first turned on trace level logging in my
Program.cs
file:builder.Logging.SetMinimumLevel(LogLevel.Trace);
In Blazor WASM projects, the following script is added to the index.html file.
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
I'm not sure how to find the original (TS, I would assume) source - it's embedded from Nuget somehow, but the doc specifying its addition to WASM is here - it is in the project by default if you initialize using the
dotnet new
command mentioned above. You can see a minified version of it in the Nuget package here under thestaticwebassets
directory.https://gist.github.com/codyaweber/928d4ec6c82094c82e71605514fcbdad
After turning on trace logging, the following log shows up after coming back from the auth redirect to
/authentication/login-callback
with query string parameters containing the code, state, etc:Verbose - Hash does not contain known properties, returning cached hash
.Hash? Apparently AAD puts the authorization code and other redirect parameters in the url hash instead of the querystring. There's some mention of this in this github issue where some others have run into trouble because of this.
The FusionAuth redirect hash is empty because all the parameters are in the querystring, hence the error
Hash does not contain known properties
. I made several modifications to the AuthenticationService.js file to parse the querystring instead and see if it would work, but ran into one more error from the trace logs:The client info was empty. Please review the trace to determine the root cause.
.In addition to the auth code querystring parameters that FusionAuth sends, I guess AAD also responds with a
client_info
parameter; I dunno what's even in it, but it's mentioned in the docs. I just forced the value to{}
, which fixed the problem, and resulted in a successful authentication. Granted, that was just the initial authentication. I have no idea if refreshing or anything would work after that.I'm sure someone else could figure out more than me here - I'm pretty new to dotnet. No idea how to proceed from all this information to a reasonable solution, but hopefully it helps someone else.
-
@cody A few more updates. I tried performing PKCE auth with the
Microsoft.AspNetCore.Components.WebAssembly.Authentication
nuget package, instead ofMicrosoft.Authentication.WebAssembly.Msal
. It is also packaged with anAuthenticationService.js
file, but it's a bit different than the equivalent file for theMsal
package.It worked quite seamlessly when I tried the default WASM setup with Auth0, but had a long pause when using FusionAuth. I compared the two flows step for step. For FusionAuth, the app pauses until the iframe timeout is hit while attempting to do silent authentication, during which the application displays "Authorizing...". At first, this timeout was due to the
X-Frame-Options: Deny
header preventing the iframe from working at all. I wrote an nginx proxy config to remove that header, at which point the iframe showed up, but still hung until timeout.In the Auth0 flow, on successful authentication inside the iframe, the iframe posts a message containing the redirect uri with the querystring parameters containing the code, state, etc, which continues the flow in the application. FusionAuth's /authorize/oauth2 page never posts any such message, so the flow hangs.
-
@cody Finally got this working in a reasonable, albeit not ideal way. It boiled down to disabling the silent authentication via iframe by reducing the timeout to 0. Once the timeout is hit, Microsoft's authentication library falls back to a conventional PKCE redirect. Unfortunately, I couldn't find a way to override the timeout value via an option in the
Program.cs
file. So instead, I copied the contents of theAuthenticationService.js
file into my project, and instead of importing from the nuget package, I just use that local copy:<!-- <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script> --> <script src="./AuthenticationService.js"></script>
with this singular change (line
12474
in version 7.0.11):// var e = t.silentRequestTimeout || 1e4; var e = 0;
This removes the need for the iframe to work altogether, and basically gets the authentication library to do what you'd expect.
I dunno if there's a spec somewhere for how silent authentication is performed via iframe, but any other identity provider I tried out using Microsoft's package - Okta, Google, Auth0, Azure AD - they all seemed to handle the iframe fine. Would be nice if FusionAuth did the same so this debacle could be avoided for other Blazor WASM apps. It might be as simple as checking if the auth flow is occurring in an iframe, and calling
postMessage
after authenticating, though I'm really not sure.Anyways, hope some of this info may help someone else.
-
@cody Thank you for sharing!