Change to JWT inside Secure Cookie Auth

This commit is contained in:
derek.holloway
2025-07-21 10:07:26 -07:00
parent 1687b097f8
commit bd5a909bcd
4 changed files with 98 additions and 23 deletions
@@ -34,19 +34,9 @@ namespace BoredCareers.Controllers {
test.CurrentPasswordAttempts = 0;
await _databaseService.SetAccount(test);
List<Claim> claims = new List<Claim>() {
new Claim("ID", test.ID.ToString()),
new Claim(ClaimTypes.NameIdentifier, test.ID.ToString())
};
string jwt = BoredCareersJWT.GenereateJWTToken(test.ID, StayLoggedIn);
BoredCareersJWT.SignIn(Response, StayLoggedIn, jwt);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(new ClaimsIdentity(claims, "Auth")),
new AuthenticationProperties {
ExpiresUtc = DateTime.UtcNow.AddYears(30), // Add 30 years with sliding on
IsPersistent = StayLoggedIn, // Is set from the StayLoggedIn
}
);
return Ok(test);
} else {
test.CurrentPasswordAttempts += 1;
@@ -151,9 +141,9 @@ namespace BoredCareers.Controllers {
[Route("logout")]
[HttpPost]
public async Task<ActionResult> Logout() {
public ActionResult Logout() {
if (isLoggedIn()) {
await HttpContext.SignOutAsync();
BoredCareersJWT.SignOut(Response);
return Ok();
}
return NotFound();
+38 -8
View File
@@ -5,6 +5,11 @@ using BoredCareers.Services.DatabaseService;
using System.Threading.RateLimiting;
using Stripe;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Org.BouncyCastle.Bcpg.Sig;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
var builder = WebApplication.CreateBuilder(args);
@@ -82,14 +87,39 @@ if (IPayment._PaymentType == PaymentType.StripeIntent) {
// Authentication Service
builder.Services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
} ).AddCookie(options => {
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
options.LoginPath = "/account/login";
options.LogoutPath = "/account/logout";
options.SlidingExpiration = true;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-app",
ValidAudience = "your-app",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret-key")),
ClockSkew = TimeSpan.FromMinutes(1)
};
options.Events = new JwtBearerEvents {
OnMessageReceived = context => {
context.Token = context.Request.Cookies["access_token"];
return Task.CompletedTask;
},
OnTokenValidated = context => {
var jwtToken = context.SecurityToken as JwtSecurityToken;
if (jwtToken != null) {
var exp = jwtToken.ValidTo;
var now = DateTime.UtcNow;
if ((exp - now) < TimeSpan.FromMinutes(5)) {
int accountID = Convert.ToInt32(context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value);
bool isPersistent = bool.Parse(context.Principal?.FindFirst(ClaimTypes.IsPersistent)?.Value);
var newJWT = BoredCareersJWT.GenereateJWTToken(accountID, isPersistent);
BoredCareersJWT.SignIn(context.HttpContext.Response, isPersistent, newJWT);
}
}
return Task.CompletedTask;
}
};
});
builder.Services.AddCors(o => o.AddDefaultPolicy(builder => {
+1
View File
@@ -11,6 +11,7 @@
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.3.0" />
<PackageReference Include="MySql.Data" Version="9.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+54
View File
@@ -0,0 +1,54 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace BoredCareers.Services {
public class BoredCareersJWT {
public static string GenereateJWTToken(int accountID, bool StayLoggedIn) {
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes("your-super-secret-key");
var tokenDiscriptor = new SecurityTokenDescriptor {
Subject = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.NameIdentifier, accountID.ToString()),
new Claim(ClaimTypes.IsPersistent, StayLoggedIn.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(15),
IssuedAt = DateTime.UtcNow,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.RsaSha512),
Audience = "your-app",
Issuer = "your-app"
};
var token = tokenHandler.CreateToken(tokenDiscriptor);
return tokenHandler.WriteToken(token);
}
public static void SignIn(HttpResponse Response, bool StayLoggedIn, string jwt) {
if (StayLoggedIn) {
// Stay logged in cookie
Response.Cookies.Append("access_token", jwt, new CookieOptions {
Secure = true,
HttpOnly = true,
SameSite = SameSiteMode.Strict,
Expires = DateTime.UtcNow.AddMinutes(15)
});
}
else {
// Session cookie
Response.Cookies.Append("access_token", jwt, new CookieOptions {
Secure = true,
HttpOnly = true,
SameSite = SameSiteMode.Strict,
});
}
}
public static void SignOut(HttpResponse Response) {
Response.Cookies.Delete("access_token");
}
}
}