From e94f77d56dd773b9656183bffe37f971197056e9 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 22 Sep 2025 19:02:03 -0700 Subject: [PATCH 1/2] Fully impliment JWT auto renew --- ToDo.yaml | 1 - src/Server/Entities/JWT.cs | 5 +++++ src/Server/Program.cs | 25 +++++++++++++------------ 3 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 src/Server/Entities/JWT.cs diff --git a/ToDo.yaml b/ToDo.yaml index 2957957..ef34666 100755 --- a/ToDo.yaml +++ b/ToDo.yaml @@ -1,6 +1,5 @@ Server: Auth: - Make sure autorenew works Make sure rate limiting isnt being broken by cloudflare Auth-Key-Value-Storage: diff --git a/src/Server/Entities/JWT.cs b/src/Server/Entities/JWT.cs new file mode 100644 index 0000000..fd9df03 --- /dev/null +++ b/src/Server/Entities/JWT.cs @@ -0,0 +1,5 @@ +namespace BoredCareers.Entities { + public class JWTRenewRequest { + public string JWT { get; set; } = ""; + } +} \ No newline at end of file diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 2e1fdde..84ff262 100755 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -9,6 +9,7 @@ using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Cryptography; using BoredCareers.Services.TimerService; +using BoredCareers.Entities; var builder = WebApplication.CreateBuilder(args); @@ -203,18 +204,18 @@ app.Use(async (context, next) =>{ Claim? expClaim = user.FindFirst(JwtRegisteredClaimNames.Exp); if (expClaim != null && long.TryParse(expClaim.Value, out long expUnix)) { DateTimeOffset expTime = DateTimeOffset.FromUnixTimeSeconds(expUnix); - if ((expTime - DateTimeOffset.UtcNow) < TimeSpan.FromMinutes(2)) { - IHttpClientFactory clientFactory = context.RequestServices.GetRequiredService(); - HttpClient client = clientFactory.CreateClient(); - HttpResponseMessage response = await client.PostAsync("https://auth.mistox.com/api/auth/renew", new StringContent(token)); - if (response.IsSuccessStatusCode) { - string newJwt = await response.Content.ReadAsStringAsync(); - context.Response.Cookies.Append("mistox_session", newJwt, new CookieOptions { - HttpOnly = true, - Secure = true, - SameSite = SameSiteMode.Strict, - Expires = DateTimeOffset.UtcNow.AddYears(1) - }); + if ((expTime - DateTimeOffset.UtcNow) < TimeSpan.FromDays(3)) { + using (HttpClient client = new HttpClient()) { + HttpResponseMessage response = await client.PostAsJsonAsync("https://auth.mistox.com/api/auth/renew", new JWTRenewRequest() { JWT = token }); + if (response.IsSuccessStatusCode) { + string newJwt = await response.Content.ReadAsStringAsync(); + context.Response.Cookies.Append("mistox_session", newJwt, new CookieOptions { + HttpOnly = true, + Secure = true, + SameSite = SameSiteMode.Strict, + Expires = DateTimeOffset.UtcNow.AddYears(3) + }); + } } } } -- 2.52.0 From 06093f54c7de9bb4935ece20480f42538e6f88f4 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 22 Sep 2025 19:10:01 -0700 Subject: [PATCH 2/2] Finalize autorenew method --- src/Server/Program.cs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 84ff262..72662b8 100755 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -111,6 +111,7 @@ using (HttpClient client = new HttpClient()) { Console.WriteLine("PublicKey loaded"); } +// Pull JWT out of cookie for auth builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; @@ -197,28 +198,34 @@ app.UseRouting(); app.UseAuthentication(); +// Autorenew JWT about to expire app.Use(async (context, next) =>{ ClaimsPrincipal user = context.User; if (user.Identity?.IsAuthenticated == true) { string? token = context.Request.Cookies["mistox_session"]; - Claim? expClaim = user.FindFirst(JwtRegisteredClaimNames.Exp); - if (expClaim != null && long.TryParse(expClaim.Value, out long expUnix)) { - DateTimeOffset expTime = DateTimeOffset.FromUnixTimeSeconds(expUnix); - if ((expTime - DateTimeOffset.UtcNow) < TimeSpan.FromDays(3)) { - using (HttpClient client = new HttpClient()) { - HttpResponseMessage response = await client.PostAsJsonAsync("https://auth.mistox.com/api/auth/renew", new JWTRenewRequest() { JWT = token }); - if (response.IsSuccessStatusCode) { - string newJwt = await response.Content.ReadAsStringAsync(); - context.Response.Cookies.Append("mistox_session", newJwt, new CookieOptions { - HttpOnly = true, - Secure = true, - SameSite = SameSiteMode.Strict, - Expires = DateTimeOffset.UtcNow.AddYears(3) - }); + Claim? staySignedIn = user.FindFirst(ClaimTypes.IsPersistent); + if (staySignedIn != null && bool.TryParse(staySignedIn.Value, out bool sli) && sli == true) { + Claim? expClaim = user.FindFirst(ClaimTypes.Expiration); + if (expClaim != null && long.TryParse(expClaim.Value, out long expUnix)) { + DateTimeOffset expTime = DateTimeOffset.FromUnixTimeSeconds(expUnix); + if ((expTime - DateTimeOffset.UtcNow) < TimeSpan.FromDays(3)) { + using (HttpClient client = new HttpClient()) { + HttpResponseMessage response = await client.PostAsJsonAsync("https://auth.mistox.com/api/auth/renew", new JWTRenewRequest() { JWT = token }); + if (response.IsSuccessStatusCode) { + string newJwt = await response.Content.ReadAsStringAsync(); + context.Response.Cookies.Append("mistox_session", newJwt, new CookieOptions { + HttpOnly = true, + Secure = true, + SameSite = SameSiteMode.Strict, + Expires = DateTimeOffset.UtcNow.AddYears(3) + }); + } } } } } + } else { + context.Response.Cookies.Delete("mistox_session"); } await next(); -- 2.52.0