148 lines
4.1 KiB
C#
148 lines
4.1 KiB
C#
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<IResult> 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<IResult> 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<IResult> 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<IResult> Logout(
|
|
RefreshRequest request,
|
|
AuthService auth)
|
|
{
|
|
await auth.LogoutAsync(request.RefreshToken);
|
|
return Results.NoContent();
|
|
}
|
|
|
|
private static async Task<IResult> 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);
|
|
}
|