using System.Security.Claims; using Vanguard.Server.Api; using Vanguard.Server.Auth; using Vanguard.Server.Meta; namespace Vanguard.Server.Auth; public sealed record RegisterRequest(string? UserName, string? Password); public sealed record LoginRequest(string? UserName, string? Password); public sealed record RefreshRequest(string? RefreshToken); public sealed record AuthResponse( Guid UserId, string UserName, string AccessToken, string RefreshToken, DateTimeOffset AccessExpiresAt, DateTimeOffset RefreshExpiresAt, bool OnboardingCompleted); public sealed record MeResponse( Guid UserId, string UserName, DateTimeOffset CreatedAt, bool OnboardingCompleted, string? SelectedTrialDeckId, int Gold, int Dust, int Tokens, int Xp, int RankedMmr, int CasualMmr, int Matches, int Wins); public static class AuthEndpoints { public static void MapAuthEndpoints(this WebApplication app) { var group = app.MapGroup("/api/auth"); group.MapPost("/register", Register).RequireRateLimiting("auth"); group.MapPost("/login", Login).RequireRateLimiting("auth"); group.MapPost("/refresh", Refresh).RequireRateLimiting("auth"); group.MapPost("/logout", Logout); group.MapGet("/me", Me).RequireAuthorization(); } private static async Task Register( RegisterRequest request, AuthService auth) { try { var result = await auth.RegisterAsync(request.UserName, request.Password); return Results.Ok(ToResponse(result)); } catch (AuthException exception) { return Results.Json( new ApiError(exception.Code, exception.Message), statusCode: exception.StatusCode); } } private static async Task Login( LoginRequest request, AuthService auth) { try { var result = await auth.LoginAsync(request.UserName, request.Password); return Results.Ok(ToResponse(result)); } catch (AuthException exception) { return Results.Json( new ApiError(exception.Code, exception.Message), statusCode: exception.StatusCode); } } private static async Task Refresh( RefreshRequest request, AuthService auth) { try { var result = await auth.RefreshAsync(request.RefreshToken); return Results.Ok(ToResponse(result)); } catch (AuthException exception) { return Results.Json( new ApiError(exception.Code, exception.Message), statusCode: exception.StatusCode); } } private static async Task Logout( RefreshRequest request, AuthService auth) { await auth.LogoutAsync(request.RefreshToken); return Results.NoContent(); } private static async Task Me( ClaimsPrincipal principal, AuthService auth, EconomyService economy) { var userId = principal.GetUserId(); if (userId is null) return Results.Unauthorized(); var user = await auth.GetUserAsync(userId.Value); if (user is null) return Results.Unauthorized(); var balances = await economy.GetBalancesAsync(user.Id); return Results.Ok(new MeResponse( user.Id, user.UserName, user.CreatedAt, user.OnboardingCompleted, user.SelectedTrialDeckId, balances.Gold, balances.Dust, balances.Tokens, user.Xp, user.RankedMmr, user.CasualMmr, user.Matches, user.Wins)); } private static AuthResponse ToResponse(AuthResult result) => new( result.User.Id, result.User.UserName, result.Tokens.AccessToken, result.Tokens.RefreshToken, result.Tokens.AccessExpiresAt, result.Tokens.RefreshExpiresAt, result.User.OnboardingCompleted); }