I am developing an ASP.NET Core 7 MVC project. I am pretty new to ASP.NET Core and web development in general. I am almost done with the application functionality but I hadn't tried implementing the security of the application. Now I am trying to protect some of my urls. I have little bit of experience using Json Web Token in node js (express).
I have made some research and I managed to create the Jwt using the following method
private string CreateToken(string user)
{
List<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:JwtSecret").Value!));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
var token = new JwtSecurityToken(
claims: claims,
expires: DateTime.Now.AddMinutes(60),
signingCredentials: cred
);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
return jwt;
}
and I send It back to the user after the validation process
[HttpPost]
public IActionResult Authenticate(string user,string passwd)
{
//validation logic
if (someLogic)
{
string token = CreateToken(user);
Response.Cookies.Append("TokenCookie", token);
// Redirect the user to a protected page
return RedirectToAction("Index", "Home");
}
else
{
TempData["ErrorLogin"] = "Invalid credentials";
return RedirectToAction("Index", "Login");
}
}
According to what I have read up until this point. The usage of the Startup.cs
file is no longer required so in my Program.cs
file I added the following configuration.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("AppSettings:JwtSecret").Value!))
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseAuthorization();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Index}/{id?}");
app.Run();
The following step would be using the [Authorize]
attribute in one of the routes that I want to protect but I get a 401 when I hit the protected url.
I don't know how the middleware that the application is using works, I I am not sure how it is supposed to get the json from the action. Where do I attach the jwt? Are the cookies a good way?
I am pretty lost so I would like to get someone else's opinion, feedback, any advice. What can I do from here? Am I following the right way? Do I go some steps back or further?
I think I was facing the wrong direction all this time.
I store the jwt on the client's cookies like so
string token = CreateToken(user);
Response.Cookies.Append("jwt", token, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.None
});
return RedirectToAction("Index", "Home");
This way the jwt will be stored on the user's cookies.
As said by @MdFaridUddinKiron
As you have written token in cookie, so while reading from header have you checked if your header contains the token you have written? In addition, did you able to extract token from cookie also?
We need a way to take this from the cookies and attach it to the request header. In order to solve this I created a Middleware class that does the following.
public class JwtCookieToHeaderMiddleware
{
private readonly RequestDelegate _next;
public JwtCookieToHeaderMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// Check if the "Authorization" header is present
if (!context.Request.Headers.ContainsKey("Authorization") && context.Request.Cookies.TryGetValue("jwt", out string jwtToken))
{
// Add "Authorization" along with the jwt stored on the cookies
context.Request.Headers.Add("Authorization", $"Bearer {jwtToken}");
}
// call next middleware in the pipeline
await _next(context);
}
}
In the Program.cs
file we add the Middleware.
app.UseMiddleware<JwtCookieToHeaderMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
This way every time the Authentication Middleware runs the Custom Middleware is going to happen first according to the pipeline order.