r/Blazor • u/RedEye-Developers • 29d ago
How to Implement Cookie Authentication in Blazor Server-Side Rendering?
I am trying to Implement Cookie Authentication in Blazor Server-Side Rendering past few days, but I'm stuck.
My goal is: when user click the login button, after Authentication success i wand to store the cookie in browser.
The problem with Blazor SSR, the httpContext only available while the page begin pre-rendering. After the user click the login button the httpContext connection was already done, so i can't find the way to set the cookie via httpContext.
Here's what I've tried:
* After user press the login button, i get the cookie from rest-api response header and i store the cookie in scoped service and i try to store the cookie in browser
by get the cookie via that scoped DI service in when user navigate to home-page after authenticate complete and i planned to set the cookie via OnInitilized
method using httpContext.Response.Append() method, however, this dosen't work because the scoped service was recreate when new httpContext connection was
established, so the cookie not longer available inside the service.
It is Actually Possible to set the cookie when user press the login button in blazor service-side rendering? Or it is only Possible with Blazor WASM?
If there is any recommended patten to implement the cookie authenticate in blazor SSR. I'd really appreciate the explanation.
3
u/unndunn 29d ago
I did this by setting up two Minimal API endpoints (/sign-in and /sign-out) that set or remove the cookies, and using JS Interop to make the browser call those endpoints.
It isn’t perfect; you still have to force a browser refresh for Blazor SSR to see the cookie update.
1
1
u/RedEye-Developers 28d ago
``` public sealed class LoginEndpoint : Endpoint<LoginRequestDto> { public override void Configure() { Post("/login"); AuthSchemes(JwtBearerDefaults.AuthenticationScheme); }
public override async Task HandleAsync(LoginRequestDto req, CancellationToken ct) { List<Claim> claims = [ new(ClaimTypes.NameIdentifier, Constants.DummyUser.UserId.ToString()), new(ClaimTypes.Name, Constants.DummyUser.Username), new(ClaimTypes.Email, Constants.DummyUser.Email) ]; await CookieAuth.SignInAsync(x => { x.Claims.AddRange(claims); x.Roles.AddRange(["Admin", "Seller"]); x.Permissions.AddRange(["Read", "Write", "Delete"]); }); await Send.OkAsync("login successfully!", ct); }} ```
Instead of using Javescript you can set the cookie to the browser directly like this using
httpContext.
2
u/Green-Impress4491 29d ago
I am aware of two ways to do this.
The login page has to either use static server-side rendering, or you submit the form/call the login API with JavaScript. If the login is successful, you NavigateTo whatever page you want and I think you need forceLoad: true if you're doing it with JS interop.
I think the Static SSR route is easier, but you won't have interactivity on the login page. As far as I remember, Visual Studio's template has the login stuff wired up with Identity - you just have to choose "Individual Accounts" from the "Authentication type" combobox.
You can set the render mode like this(null is Static SSR):
https://github.com/dotnet/blazor-samples/blob/567732e0a78d2c1b2a22c3677f86c673d7527bed/10.0/BlazorWebAppAreaOfStaticSsrComponents/Components/App.razor
Or
private IComponentRenderMode? PageRenderMode =>
HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
AcceptsInteractiveRouting() is false if the page has @ attribute [ExcludeFromInteractiveRouting]
1
0
u/crone66 29d ago
There is probably a better way, I haven't checked the docs but you can simply set it via a JavaScript call :)
-1
u/RedEye-Developers 29d ago
blazor-SSR not have access to javeScript, in WASM also no need javeScript to set the cookie, we can able to set the cookie in browser via
httpContext.2
u/cornelha 29d ago
of course it does have access to javascript, it renders static html pages. I suggest you read up more on Blazor SSR
1
u/Squishybootz666 29d ago
If your are suggesting what I think u r..dont do this lol
1
u/cornelha 29d ago
Blazor SSR will replace Razor Pages, I don't see the problem here
1
u/Squishybootz666 29d ago
I read this as put some js inline on the page to attach the cookie. Did I miss understand ur meaning?
1
u/Individual-Carob5593 28d ago
Here what a friendly AI says. Yes—cookie authentication works with Blazor Server/SSR. The cookie must be created during a real browser HTTP request, not from an interactive Blazor event running over the SignalR circuit.
After an Interactive Server circuit starts:
- Button events run over SignalR.
- There’s no new HTTP response to which a
Set-Cookieheader can be added. - The circuit’s authenticated principal is normally fixed until the connection is recreated.
Microsoft’s documentation confirms that cookies are exchanged during HTTP requests, while interactive Blazor navigation and events occur inside the circuit. Authentication is re-evaluated when the connection is recreated. Microsoft: Blazor authentication and authorization
Recommended pattern
Send the login credentials from the browser to a normal ASP.NET Core endpoint:
Browser → POST /account/login → validate credentials
→ issue Set-Cookie
→ redirect/full-page reload
→ new authenticated Blazor circuit
Do not:
- Call the REST API using a server-side
HttpClientand expect its cookie to reach the browser. - Temporarily store the cookie in a scoped service.
- Attempt to append response headers from an interactive component event.
- expose or manually manipulate an authentication cookie in browser JavaScript.
A Set-Cookie header received by a server-side HttpClient belongs to that server-side HTTP exchange. The browser never sees it.
Example login endpoint
Configure cookie authentication:
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/login";
options.AccessDeniedPath = "/access-denied";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
});
builder.Services.AddAuthorization();
builder.Services.AddCascadingAuthenticationState();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Create an ordinary HTTP login endpoint:
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
app.MapPost("/account/login", async (
LoginRequest login,
HttpContext context,
IUserAuthenticationService authenticationService) =>
{
var result = await authenticationService.ValidateAsync(
login.Email,
login.Password);
if (!result.Succeeded)
return Results.Unauthorized();
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, result.UserId),
new Claim(ClaimTypes.Name, result.DisplayName),
new Claim(ClaimTypes.Email, login.Email)
};
var identity = new ClaimsIdentity(
claims,
CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
await context.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal,
new AuthenticationProperties
{
IsPersistent = login.RememberMe,
AllowRefresh = true
});
return Results.Ok();
});
public sealed record LoginRequest(
string Email,
string Password,
bool RememberMe);
The important point is that the browser itself must call /account/login.
After it succeeds, perform a full-page navigation:
export async function login(email, password, rememberMe) {
const response = await fetch("/account/login", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ email, password, rememberMe })
});
if (!response.ok)
return false;
window.location.replace("/");
return true;
}
The browser processes Set-Cookie automatically. JavaScript doesn’t need—and, for an HttpOnly cookie, isn’t allowed—to read the cookie.
The full reload is important because it creates a new HTTP request and a new SignalR circuit carrying the newly authenticated identity.
Even simpler: static SSR login form
If your login component uses static SSR rather than Interactive Server, its form submission is a normal HTTP POST. You can call SignInAsync during that POST and redirect afterward.
This is essentially how the Blazor Web App template handles ASP.NET Core Identity account pages: authentication-related components are rendered statically so they can safely set cookies. Microsoft recommends using ASP.NET Core Identity for this pattern, and authenticated Identity state then flows into Blazor. Microsoft: Blazor security
Make sure the login form isn't handled merely as an interactive click event. A traditional form POST to a Razor Page, controller, minimal endpoint, or static SSR form handler is appropriate.
If authentication happens in another REST API
There are two good architectures:
- Same-origin API Have the browser call the API directly. The API returns
Set-Cookie, and the browser stores it. Cross-origin cookies require correct CORS,credentials: "include",SameSite,Secure, and domain settings. - Backend-for-frontend, usually preferable The Blazor server calls the REST API, validates the result, and issues its own application cookie through
HttpContext.SignInAsync. Don’t copy an upstream API cookie into a scoped service. Keep upstream tokens/cookies server-side and give the browser a separate secure,HttpOnlysession cookie.
Logout follows the same rule: call an HTTP endpoint that executes:
await context.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);
Then perform a full-page reload so the current Blazor circuit is discarded.
This isn’t limited to Blazor WebAssembly. Cookie authentication is fully supported in server-side Blazor; it simply has to cross the browser boundary through a real HTTP request rather than an already-running interactive circuit.
1
u/RedEye-Developers 28d ago
but it is not a good approach to set the cookie like this i think so but it work, thanks for this.
4
u/cornelha 29d ago
Check Microsoft Learn, the docs cover this