Change to JWT inside Secure Cookie Auth
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
+38
-8
@@ -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);
|
||||||
|
|
||||||
@@ -82,14 +87,39 @@ 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 => {
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user