using System; using System.Security.Claims; using System.IdentityModel.Tokens.Jwt; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using SNote.Models; using SNote.Server.Data; using SNote.Server.Security; namespace SNote.Server.Endpoints; public static class AuthEndpoints { public static void MapAuthEndpoints(this IEndpointRouteBuilder routes) { var group = routes.MapGroup("/api/auth"); group.MapPost("/register", async (RegisterRequest req, ServerDbContext db) => { if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.Password) || string.IsNullOrWhiteSpace(req.EncryptedKey)) { return Results.BadRequest("Username, password, and encrypted key are required."); } var existing = await db.Users.AnyAsync(u => u.Username.ToLower() == req.Username.ToLower()); if (existing) { return Results.Conflict("Username already exists."); } var user = new User { Username = req.Username, PasswordHash = PasswordHasher.HashPassword(req.Password), EncryptedKey = req.EncryptedKey }; db.Users.Add(user); await db.SaveChangesAsync(); return Results.Ok(new { message = "Registration successful." }); }); group.MapPost("/login", async (LoginRequest req, ServerDbContext db, CertificateManager certManager) => { if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.Password)) { return Results.BadRequest("Username and password are required."); } var user = await db.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == req.Username.ToLower()); if (user == null || !PasswordHasher.VerifyPassword(user.PasswordHash, req.Password)) { return Results.Unauthorized(); } // Generate JWT Token signed by shared certificate var cert = certManager.GetCertificate(); var privateKey = new X509SecurityKey(cert); var claims = new[] { new Claim(ClaimTypes.Name, user.Username), new Claim(ClaimTypes.Role, "User") }; var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256) }; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); return Results.Ok(new LoginResponse(tokenString, user.EncryptedKey)); }); group.MapGet("/encrypted-key/{username}", async (string username, ServerDbContext db) => { var user = await db.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()); if (user == null) { return Results.NotFound("User not found."); } return Results.Ok(new { encryptedKey = user.EncryptedKey }); }); } } public record RegisterRequest(string Username, string Password, string EncryptedKey); public record LoginRequest(string Username, string Password); public record LoginResponse(string Token, string EncryptedKey);