Skip to content

Commit 5452ef7

Browse files
committed
Enhance JWT handling: set token as HttpOnly cookie for secure authentication and implement logout functionality
1 parent c371de6 commit 5452ef7

2 files changed

Lines changed: 67 additions & 7 deletions

File tree

Configurations/SecurityConfiguration.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using Microsoft.Extensions.Hosting;
44
using Microsoft.AspNetCore.Authentication.JwtBearer;
55
using Microsoft.IdentityModel.Tokens;
6+
using Microsoft.AspNetCore.Http;
7+
using System.Threading.Tasks;
68
using System.Text;
79

810
namespace Flowboard_Project_Management_System_Backend.Configurations
@@ -21,15 +23,23 @@ public static IServiceCollection AddFrontendCors(this IServiceCollection service
2123
{
2224
if (environment.IsDevelopment())
2325
{
24-
policy.AllowAnyOrigin()
26+
// In development allow all origins but allow credentials, so we reflect the origin
27+
// with SetIsOriginAllowed. Avoid AllowAnyOrigin together with AllowCredentials.
28+
policy.SetIsOriginAllowed(_ => true)
2529
.AllowAnyHeader()
26-
.AllowAnyMethod();
30+
.AllowAnyMethod()
31+
.AllowCredentials();
2732
}
2833
else
2934
{
30-
policy.WithOrigins(productionFrontendOrigin)
31-
.AllowAnyHeader()
32-
.AllowAnyMethod();
35+
// In production only allow configured frontend origin and credentials
36+
if (!string.IsNullOrWhiteSpace(productionFrontendOrigin))
37+
{
38+
policy.WithOrigins(productionFrontendOrigin)
39+
.AllowAnyHeader()
40+
.AllowAnyMethod()
41+
.AllowCredentials();
42+
}
3343
}
3444
});
3545
});
@@ -63,6 +73,23 @@ public static IServiceCollection AddJwtAuthentication(this IServiceCollection se
6373
ValidAudience = jwtAudience,
6474
IssuerSigningKey = new SymmetricSecurityKey(keyBytes)
6575
};
76+
77+
// Allow reading token from an HttpOnly cookie named "jwt" if present
78+
options.Events = new JwtBearerEvents
79+
{
80+
OnMessageReceived = context =>
81+
{
82+
if (string.IsNullOrEmpty(context.Token))
83+
{
84+
// Try reading from the cookie
85+
if (context.Request.Cookies.TryGetValue("jwt", out var cookieToken) && !string.IsNullOrEmpty(cookieToken))
86+
{
87+
context.Token = cookieToken;
88+
}
89+
}
90+
return Task.CompletedTask;
91+
}
92+
};
6693
});
6794

6895
// Register Authorization as well, so callers don't have to explicitly add it

Controllers/PublicController.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Flowboard_Project_Management_System_Backend.Services;
33
using MongoDB.Driver;
44
using Microsoft.IdentityModel.Tokens;
5+
using Microsoft.AspNetCore.Http;
56
using System.IdentityModel.Tokens.Jwt;
67
using System.Security.Claims;
78
using System.Text;
@@ -13,10 +14,12 @@
1314
public class PublicController : ControllerBase
1415
{
1516
private readonly MongoDbService _mongoDbService;
17+
private readonly IHostEnvironment _env;
1618

17-
public PublicController(MongoDbService mongoDbService)
19+
public PublicController(MongoDbService mongoDbService, IHostEnvironment environment)
1820
{
1921
_mongoDbService = mongoDbService;
22+
_env = environment;
2023
}
2124

2225
[HttpPost("register")]
@@ -79,14 +82,44 @@ public IActionResult Login([FromBody] FlowModels.LoginRequest loginRequest)
7982
// Generate JWT token
8083
var token = GenerateJwtToken(user);
8184

85+
// Set JWT as HttpOnly cookie for automatic browser authentication
86+
var expiryMinutes = int.Parse(Environment.GetEnvironmentVariable("JWT_EXPIRY_MINUTES") ?? "60");
87+
var cookieOptions = new CookieOptions
88+
{
89+
HttpOnly = true,
90+
Secure = !_env.IsDevelopment(), // Secure cookie in production
91+
SameSite = _env.IsDevelopment() ? SameSiteMode.Lax : SameSiteMode.None,
92+
Expires = DateTime.UtcNow.AddMinutes(expiryMinutes),
93+
Path = "/"
94+
};
95+
96+
Response.Cookies.Append("jwt", token, cookieOptions);
97+
8298
return Ok(new
8399
{
84100
message = "Login successful!",
85101
user,
86-
token
102+
87103
});
88104
}
89105

106+
[HttpPost("logout")]
107+
public IActionResult Logout()
108+
{
109+
// Remove cookie by setting expired options
110+
var cookieOptions = new CookieOptions
111+
{
112+
HttpOnly = true,
113+
// Secure = !_env.IsDevelopment(),
114+
Secure = _env.IsDevelopment(),
115+
SameSite = _env.IsDevelopment() ? SameSiteMode.Lax : SameSiteMode.None,
116+
Expires = DateTime.UtcNow.AddDays(-1),
117+
Path = "/"
118+
};
119+
Response.Cookies.Delete("jwt", cookieOptions);
120+
return Ok(new { message = "Logout successful" });
121+
}
122+
90123
// ---------------- JWT Helper ----------------
91124
private string GenerateJwtToken(FlowModels.User user)
92125
{

0 commit comments

Comments
 (0)