C
C#2mo ago
Yarden

Need help with "creating an authentication cookie"

I'm reading the documentation of Microsoft of "Use cookie authentication without ASP.NET Core Identity": https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-9.0 It's my first time trying to follow documentation and implement something that fits my program. I'm in the stage of "Create an authentication cookie" and this is Microsoft implementation:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;

if (ModelState.IsValid)
{
// Use Input.Email and Input.Password to authenticate the user
// with your custom authentication logic.
//
// For demonstration purposes, the sample validates the user
// on the email address maria.rodriguez@contoso.com with
// any password that passes model validation.

var user = await AuthenticateUser(Input.Email, Input.Password);

if (user == null)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}

... too much code, I can't add it
};

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);

_logger.LogInformation("User {Email} logged in at {Time}.",
user.Email, DateTime.UtcNow);

return LocalRedirect(Url.GetLocalUrl(returnUrl));
}

// Something failed. Redisplay the form.
return Page();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;

if (ModelState.IsValid)
{
// Use Input.Email and Input.Password to authenticate the user
// with your custom authentication logic.
//
// For demonstration purposes, the sample validates the user
// on the email address maria.rodriguez@contoso.com with
// any password that passes model validation.

var user = await AuthenticateUser(Input.Email, Input.Password);

if (user == null)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}

... too much code, I can't add it
};

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);

_logger.LogInformation("User {Email} logged in at {Time}.",
user.Email, DateTime.UtcNow);

return LocalRedirect(Url.GetLocalUrl(returnUrl));
}

// Something failed. Redisplay the form.
return Page();
}
The problems are: (1) I don't use razor pages. I'm using React and backend. (2) My plan is: The user to login from the website -> Sends a POST request with HTTP to the backend which uses my controller method -> Then I want to use this method to create the cookie -> Send it back to the frontend. I don't know what I need to change and how.
Use cookie authentication without ASP.NET Core Identity
Learn how to use cookie authentication without ASP.NET Core Identity.
25 Replies
Yarden
YardenOP2mo ago
I suppose I need to totally change the method? What type I need to return? what I need to get as parameters?
Pobiega
Pobiega2mo ago
Doubt you'll have to change a lot to be honest! But first a few control questions Is your FE and BE served from the same host? If not, the cookie created by your BE might not be accessible from your FE Its fairly common to have your BE and FE not be the same during development but actually be the same when built for production (using something like ASP.NETs serve static files), which might complicate things Anyways, I'll post a working backend implementation here for you to look at Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddControllers();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.Cookie.Name = "auth-cookie";
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.IsEssential = true;
});

builder.Services.AddHttpContextAccessor();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddControllers();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.Cookie.Name = "auth-cookie";
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.IsEssential = true;
});

builder.Services.AddHttpContextAccessor();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
AuthController.cs
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace CookieAuthApi;

[ApiController]
[Route("/api/auth")]
public class AuthController : ControllerBase
{
[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
// TODO: Actually check that the username and password are correct here.

// Claims are used to store data about the logged-in user.
var claims = new List<Claim>
{
new(ClaimTypes.Name, request.Username),
};

var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);

var props = new AuthenticationProperties()
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1),
AllowRefresh = true,
IssuedUtc = DateTimeOffset.UtcNow,
};

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal, props);

return Ok();
}

[HttpGet]
[Authorize]
// This endpoint just returns the username of the logged-in user.
// It is protected by the [Authorize] attribute, which means that only logged-in users can access it.
public ActionResult<LoginResponse> Get()
{
var username = User.FindFirstValue(ClaimTypes.Name);

if (username == null)
{
return Unauthorized();
}

return new LoginResponse(username);
}
}

public record LoginResponse(string Username);

public record LoginRequest(string Username, string Password);
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace CookieAuthApi;

[ApiController]
[Route("/api/auth")]
public class AuthController : ControllerBase
{
[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
// TODO: Actually check that the username and password are correct here.

// Claims are used to store data about the logged-in user.
var claims = new List<Claim>
{
new(ClaimTypes.Name, request.Username),
};

var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);

var props = new AuthenticationProperties()
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1),
AllowRefresh = true,
IssuedUtc = DateTimeOffset.UtcNow,
};

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal, props);

return Ok();
}

[HttpGet]
[Authorize]
// This endpoint just returns the username of the logged-in user.
// It is protected by the [Authorize] attribute, which means that only logged-in users can access it.
public ActionResult<LoginResponse> Get()
{
var username = User.FindFirstValue(ClaimTypes.Name);

if (username == null)
{
return Unauthorized();
}

return new LoginResponse(username);
}
}

public record LoginResponse(string Username);

public record LoginRequest(string Username, string Password);
Yarden
YardenOP2mo ago
@Pobiega I'm very sorry, I didn't hear any notification from discord, it's always making a sound and show me visual notification on the phone. I'll look at your messages very soon, I'm fixing something else on my project and I'll be back to this. Thank you very much, I appreciate it ❤️ I'll work on it in around 1 hour Hey BE = ASP.NET FE = React I don't use Razor since I'm a begginer, I didn't want to start with something complicated until I have good foundations
Pobiega
Pobiega2mo ago
Yeah I got that part from your first post but how do you host your react app? what I usually do (during dev) is run my FE separately using vite or similar, and use a dev server proxy to let the FE talk to BE
Yarden
YardenOP2mo ago
VSC with vite
Pobiega
Pobiega2mo ago
but during prod, I use ASP.NETs UseStaticFiles to host the built FE
Yarden
YardenOP2mo ago
My FE and BE already communicate through database (Postgre) and http calls, isn't that enough? The guide I sent here doesn't suppose to work? because I have separated development languages? Is it meant only for Razor pages? I'm trying to make sure I understand you
Pobiega
Pobiega2mo ago
The guide you linked is specifically for Razor yes, but as I showed above, its quite easily adapted for non-razor What you are not getting is how cookies work cookies is part of the http protocol and not something ASP or react decide what to do with, and part of that is whats called domain restriction
Yarden
YardenOP2mo ago
So should I follow the guide and change it compared to yours? Because I'm trying to learn the process, otherwise it will be like magic and I won't understand anything. I'm scared of not understanding anything
Pobiega
Pobiega2mo ago
if I have a website called www.pobiega.com I cant read cookies from www.google.com
Yarden
YardenOP2mo ago
Right
Pobiega
Pobiega2mo ago
so if your FE is on localhost:9000 and your BE is on localhost:7000.... they cant read eachothers cookies either, I'd imagine Cookie auth is pretty unusual for SPA apps, I'd say they mostly use some form of bearer tokens, usually JWTs There might be a workaround thou, I'm not an expert. a quick google search tells me Vite has a fix for this
Pobiega
Pobiega2mo ago
Matthew Sullivan
Matt's Life Bytes
Unbreaking Cookies in Local Dev with Vite Proxy
Vite is a popular and powerful local dev server. One of my favorite Vite features is the proxy, which allows you to develop your frontend locally against an arbitrary backend URL. I found myself in…
Yarden
YardenOP2mo ago
So can you just explain me what your implementation does? and why it works? What you implemented that the the guide didn't give
Pobiega
Pobiega2mo ago
can't you do the comparison yourself? I just removed the razor specific parts
Yarden
YardenOP2mo ago
Okie I will
Pobiega
Pobiega2mo ago
ie Page, ControllerWithViews etc and my auth controller is the simplest possible implementation of "a way to log in" it doesnt check that the user is valid, has the right password, password hashing... nothing it just says "sure you are now authenticated!" and the GET endpoint is just to show that "it worked" if you hook this up with your react app, it may or may not work I suggest you try that
Yarden
YardenOP2mo ago
I will, hopefully everything works fine 😂 Thank you very much for your help and time!
Pobiega
Pobiega2mo ago
removing the secure flag on the cookie might be enough maybe tweaking the SameSite policy too again, idk I don't use cookie auth in SPA apps ;D
Yarden
YardenOP2mo ago
So what do you use? Maybe I can get a better idea
Pobiega
Pobiega2mo ago
Bearer token specifically JWTs, but thats not really important
Yarden
YardenOP2mo ago
Okie! Will play with what you gave me , hopefully it will work for now
Pobiega
Pobiega2mo ago
something like this
Pobiega
Pobiega2mo ago
No description
Yarden
YardenOP2mo ago
Tbh that's what I wanted at the beginning, but didnt find any guide to do this where I didn't implement my whole models (User identity, etc...) with JWT. The cookies is the first thing I found that didn't force me to implement my models with JWT(or any other algorithm). I just need a way to send data of login user to the frontend, but seems like there are not many options

Did you find this page helpful?