100 lines
3.6 KiB
C#
100 lines
3.6 KiB
C#
|
|
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);
|