Blazor WASM Logout Issue - authentication

I have a Blazor WASM application deployed to Azure Blob storage as a static website. I am using Azure B2C authentication with Social Login and Email login. When I am running the code through Visual Studio, both login and logout work as expected. When I deploy the code to my static website on azure, login seems to work fine but logout has a problem. See the below screenshots.
Local Debugging
Static website
I am using the default RemoteAuthenticatorView in my code with no changes.
#page "/authentication/{action}"
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="#Action" />
#code{
[Parameter] public string? Action { get; set; }
}
Below is the code for my LoginDisplay.razor component
#using Microsoft.AspNetCore.Components.Authorization
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#inject NavigationManager Navigation
#inject SignOutSessionStateManager SignOutManager
<AuthorizeView>
<Authorized>
Hello, #context.User.Identity?.Name! <MudDivider Vertical="true" FlexItem="true"></MudDivider>
<MudButton Color="Color.Warning" #onclick="BeginLogout" EndIcon="#Icons.Filled.Logout">Log out</MudButton>
</Authorized>
<NotAuthorized>
<MudButton Color="Color.Tertiary" Link="authentication/login" StartIcon="#Icons.Filled.Login">Login</MudButton>
</NotAuthorized>
</AuthorizeView>
#code{
private async Task BeginLogout(MouseEventArgs args)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}
What am I missing?

Related

Blazor WASM stuck on "Authorizing..." and "Completing login..."

After upgrading to .Net 5.0 my blazor WASM app started getting stuck on "Authorizing..." (it gets stuck when the user was already logged in, first load with redirect to login screen works fine, so I have to clear the cache if I want it to work) and "Completing login..." (with a URL like https://localhost:5001/authentication/login-callback?code=51FEF78805049242F1092A249B1746393790AE956B1FC2136092AE0924B3FB42&scope=openid%20profile%20email%20MyApp.WebAPI&state=e8bffbd2b93f44dc8a261cf6d65c6165&session_state=eE0yRtJI0fkjptvZSIfiUsvuHAQoyNStmS_kv--Wcv0.DCACB551F4CC63841C2615456DD4B9BF) - if I go to localhost:5001 after it got stuck on "Completing login..." I am finally logged in.
Initially it was working fine on most modern computers and the issue was only visible on slow computers (~8 year old laptop for instance). However, while I tried to understand the issue and fix it, I managed to brake it completely on my developer laptop as well, where the main branch still works mostly fine.
Creating a new ASP.Net Core app with authentication with IdentityServer4 works just fine, no issues there.
My issue looks similar to https://github.com/dotnet/aspnetcore/issues/26195 - however, I'm using only IdentityServer4 and I don't see any exceptions in the developer console.
I tried to reduce the code to a bare minimum, so here is the Program.cs:
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddApiAuthorization();
builder.Services.AddSingleton<IStringLocalizer>(serviceProvider =>
{
var factory = serviceProvider.GetRequiredService<IStringLocalizerFactory>();
return factory.Create(typeof(SharedResource));
});
builder.Services.AddLocalization();
builder.Services
.AddBlazorise()
.AddBootstrapProviders();
builder.Logging.SetMinimumLevel(LogLevel.Trace);
var host = builder.Build();
host.Services.UseBootstrapProviders();
await host.RunAsync();
}
the App.razor:
#inject IStringLocalizer S
<ThemeManager>
<CascadingAuthenticationState>
<Router AppAssembly="#typeof(App).Assembly" PreferExactMatches="#true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)">
<NotAuthorized>
#if (context.User.Identity?.IsAuthenticated ?? false)
{
<p>#S["You don't have enough permissions to access this page."]</p>
}
else
{
<RedirectToLogin />
}
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="#typeof(MainLayout)">
<NotFoundError>
</NotFoundError>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</ThemeManager>
The MainLayout.razor:
#inherits Microsoft.AspNetCore.Components.LayoutComponentBase
<header>
</header>
<main>
#Body
</main>
The Authentication.razor:
#page "/authentication/{action}"
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="#Action" />
#if (Action == "logged-out")
{
<RedirectToLogin RedirectBackToRoot="true"></RedirectToLogin>
}
#code{
[Parameter]
public string? Action { get; set; }
}
The problem is, that it just gets stuck and I don't even see what could be wrong. The one dependency I have here is on https://github.com/stsrki/Blazorise - however, I added the same dependency also to the new application, and that one still works.
Any idea how I could identify the issue?
The answer was, that the reference of the JS library pace.js broke the authentication. A new version of the library was released (and automatically updated) around the same time, we upgraded to .Net 5.0 so not sure if it was a .Net 5 issue or the library issue.
I also experienced hanging during login but years after this post. Mine happened with .NET 6. I noticed Microsoft.AspNetCore.Authentication.JwtBearer was version 6.0.8 and that version had just been released last week. I downgraded to 6.0.7 and the hanging went away.

Blazor hosted - authorize immediately before displaying application

Can anybody give me a helping push to get my hosted Blazor application (Client, Server and Shared) to request a login immediately, before the application is initially shown. I want the experience that a user must log in before accessing the application at all.
My starting point is the Blazor Webassembly (hosted) template with Api Authorization (Individual User Accounts)
Using the Authorize attribute on either server-side actions or a client-side Razor page will not initiate the authentication flow before the specific action/page with the Authorize attribute is being requested by the user. How would I go about having the authorization flow kicked off as the first thing, before the application is even displayed for the first time?
I am sure this is possible and even trivial for somebody more savvy than me. Can anybody give me a shove in the right direction, please?
I created a control RedirectToLogin.razor
#inject NavigationManager Navigation
#code {
protected override void OnInitialized()
{
String thisPage = Navigation.Uri.Replace(Navigation.BaseUri, "~/");
Navigation.NavigateTo($"Identity/Account/Login?returnUrl={thisPage}");
base.OnInitialized();
}
}
And then inserted it into the mainlayout.razor
<div class="container-fluid">
<AuthorizeView>
<Authorized>
<NavigationLogger />
<ContextMenuMouseClick>
<MenuTopBar />
<NavMenu />
<SubPageContainer>
#Body
</SubPageContainer>
</ContextMenuMouseClick>
</Authorized>
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeView>
</div>
So when the layout is loaded and it is in the NotAuthorized state it will redirect to the login page and after authorising will return to the page it was trying to access.
Hope this helps.
The Blazor Client is bootstrapped from a static index.html in the wwwroot. In the Server project this is mapped to an Endpoint in the Startup.cs, basically a catch-all to all endpoints not taken by your Razor Pages or Controllers:
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
For your scenario:
Create a new layout in Server, say _LayoutBlazor.cshtml
Re-create the contents of the index.html from the Client. Note that the _framework files from the Client are copied to the Server wwwroot upon build:
:
<app>Loading...</app>
<script src="#Href("~/_framework/blazor.webassembly.js")"></script>
Create a new Razor page and put the "Authorize" tag on it and use the _LayoutBlazor.
Remove the endpoints.MapFallbackToFile("index.html"); from Startup.cs
Mark Gould has created a proof of concept here: RazorBlazor

Disable request verification token in ASP.NET Core

ASP.NET Core MVC seems to inject a request verification token in all of my forms:
<form class="actions" method="post">
<input type="submit" class="btn btn-primary" value="Yes">
<a class="btn btn-secondary" href="/some/url">No</a>
<input name="__RequestVerificationToken" type="hidden" value="...">
</form>
I'm handling CSRF in Ajax and don't want this extra input element in all of my forms. Any way to disable it?
The element is added even without a call to AddAntiforgery in Startup.cs. I'm running on ASP.NET Core 3.1.
Antiforgery middleware is added to the Dependency injection container when one of the following APIs is called in Startup.ConfigureServices:
AddMvc
MapRazorPages
MapControllerRoute
MapBlazorHub
Details please check this document
To disable it, try below IgnoreAntiforgeryToken attribute
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
{
// no antiforgery token required
}
}
Details can be found here
The token is appended by the Form Tag Helper. If you don't need the other features of the Tag Helper, it can be removed using #removeTagHelper (in view or globally by adding to _ViewImports.cshtml):
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
See ASP.NET Core documentation for further details/options.
Just idea I would make reference to that [IgnoreAntiforgeryToken] can be used to disable the global [AutoValidateAntiForgeryToken] attribute on certain actions if needed.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new IgnoreAntiforgeryTokenAttribute());
});
}
}

How to enable/fix windows authentication in Blazor Server-Side App?

When we created our server-side blazor app (ASP.NET Core Web App) initially we did not enable authentication. We would like to enable windows authentication now.
I created a test web app with windows authentication and tried adding missing bits into our existing web app. Below are the changes that I made:
Modified app.razor to include cascadingauthenticationstate
<Router AppAssembly="#typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
</Found>
<NotFound>
<CascadingAuthenticationState>
<LayoutView Layout="#typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>
Added missing imports in _imports.razor
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.Authorization
Tried fetching current windows identity using below code block in a razor component:
#code {
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
private async Task LogUsername()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
Console.WriteLine($"{user.Identity.Name} is authenticated.");
}
else
{
Console.WriteLine("The user is NOT authenticated.");
}
}
}
When I run the web app locally in Visual Studio 2019 preview, I am always ending up with an empty identity name. And, IsAuthenticated is always false.
However, if I run the test web app locally, it gets the correct identity name.
Does anyone know what I am missing in my existing web app?
Thanks!
Apparently, I missed checking the box for windows authentication under Project Properties > Debug tab.
I have a Blazor Server project that has two possible debug start profiles
The web project itself XX.SD starts in the browser but the launch profile does not have an option for Windows Authentication.
The IIS Express lauch profile however does have this option in the Properties page.
Just Enable windows Authentication button : Project=>properties => Debug => web server settings

How to sign out over HttpContext in server-side Blazor

I access the HttpContext in a Blazor server-side view to manually log out. I added this line to Startup.cs: services.AddHttpContextAccessor(); and inject it in the view with #inject IHttpContextAccessor HttpContextAccessor.
I've got a log out button which tries to execute this code:
await HttpContextAccessor.HttpContext.SignOutAsync("Cookies");
but I get the following error message:
System.InvalidOperationException: 'Headers are read-only, response has already started.'
How can I prevent this error?
If you scaffolded Identity and overridden the old "LogOut.cshtml" from when you created the project via template, the Logout button won't logout. Assume you've used the default IdentityUser model. I ran into this issue and just wanted to add this if anyone else had this problem as well. I'm using Blazor Server with .Net 5.0.3's template. You can remove the Logout.cshtml.cs after as well.
Replace this \Areas\Identity\Pages\Account\LogOut.cshtml
#page
#model LogoutModel
#{
ViewData["Title"] = "Log out";
}
<header>
<h1>#ViewData["Title"]</h1>
#{
if (User.Identity.IsAuthenticated)
{
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="#Url.Page("/", new { area = "" })" method="post">
<button type="submit" class="nav-link btn btn-link text-dark">Click here to Logout</button>
</form>
}
else
{
<p>You have successfully logged out of the application.</p>
}
}
</header>
Replace with
#page
#using Microsoft.AspNetCore.Identity
#attribute [IgnoreAntiforgeryToken]
#inject SignInManager<IdentityUser> SignInManager
#functions {
public async Task<IActionResult> OnPost()
{
if (SignInManager.IsSignedIn(User))
{
await SignInManager.SignOutAsync();
}
return Redirect("~/");
}
}
This tripped me up too, but you need the logout functionality to be on a Razor Page (not a Blazor component). Create a Logout page and put your logout code in the OnGetAsync() method.
Your logout button can then link to the logout page.
http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/4316/A-Demonstration-of-Simple-Server-side-Blazor-Cookie-Authentication.aspx - this is a helpful example
Don't use IHttpContextAccessor.
I guess that you're using ASP.NET Core Blazor authentication and authorization new system. If not, then start with it right now. Live is too short to be wasted over other things. This is the best product created so far for Blazor's authentication and authorization, and it is based on the Identity UI (This is not Blazor, of course). Additionally, there are a couple of Components which enable controlling the flow of authentication and authorization in your application, such as displaying a "Log in" button and a "Log out" button in your layout, interchangeably altering depending on your authentication state, etc.
Please, go to this page and start learning this excellent system, and then come here for specific issues you face:
https://learn.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.0&tabs=visual-studio
Hope this helps...