working #6

Merged
derek merged 17 commits from working into main 2025-07-29 10:20:18 -07:00
4 changed files with 98 additions and 23 deletions
Showing only changes of commit bd5a909bcd - Show all commits
@@ -34,19 +34,9 @@ namespace BoredCareers.Controllers {
test.CurrentPasswordAttempts = 0; test.CurrentPasswordAttempts = 0;
await _databaseService.SetAccount(test); await _databaseService.SetAccount(test);
List<Claim> claims = new List<Claim>() { string jwt = BoredCareersJWT.GenereateJWTToken(test.ID, StayLoggedIn);
new Claim("ID", test.ID.ToString()), BoredCareersJWT.SignIn(Response, StayLoggedIn, jwt);
new Claim(ClaimTypes.NameIdentifier, test.ID.ToString())
};
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); return Ok(test);
} else { } else {
test.CurrentPasswordAttempts += 1; test.CurrentPasswordAttempts += 1;
@@ -151,9 +141,9 @@ namespace BoredCareers.Controllers {
[Route("logout")] [Route("logout")]
[HttpPost] [HttpPost]
public async Task<ActionResult> Logout() { public ActionResult Logout() {
if (isLoggedIn()) { if (isLoggedIn()) {
await HttpContext.SignOutAsync(); BoredCareersJWT.SignOut(Response);
return Ok(); return Ok();
} }
return NotFound(); return NotFound();
+39 -9
View File
@@ -5,6 +5,11 @@ using BoredCareers.Services.DatabaseService;
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using Stripe; using Stripe;
using System.Security.Claims; 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); var builder = WebApplication.CreateBuilder(args);
@@ -81,15 +86,40 @@ if (IPayment._PaymentType == PaymentType.StripeIntent) {
} }
// Authentication Service // Authentication Service
builder.Services.AddAuthentication( options => { builder.Services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
} ).AddCookie(options => { options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.Cookie.HttpOnly = true; }).AddJwtBearer(options => {
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.TokenValidationParameters = new TokenValidationParameters {
options.Cookie.SameSite = SameSiteMode.Strict; ValidateIssuer = true,
options.LoginPath = "/account/login"; ValidateAudience = true,
options.LogoutPath = "/account/logout"; ValidateLifetime = true,
options.SlidingExpiration = 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 => { 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="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.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.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="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.3.0" />
<PackageReference Include="MySql.Data" Version="9.2.0" /> <PackageReference Include="MySql.Data" Version="9.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <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");
}
}
}