Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/backend/Auth
diff options
context:
space:
mode:
authorAlfaPC112026-07-28 21:38:04 +0300
committerAlfaPC112026-07-28 21:38:04 +0300
commit3e726a87c9ae60aea3403e12c8a0aab3870942df (patch)
tree09a55fb7328e8ca7eb29f43a5e930de557104c13 /backend/Auth
parent0c9ea445a277c23aeb7156578c623ac502591499 (diff)
downloadwebsite-3e726a87c9ae60aea3403e12c8a0aab3870942df.tar.gz
require verification after Google auth and add account deletion
Diffstat (limited to 'backend/Auth')
-rw-r--r--backend/Auth/AuthEndpoints.cs60
1 files changed, 47 insertions, 13 deletions
diff --git a/backend/Auth/AuthEndpoints.cs b/backend/Auth/AuthEndpoints.cs
index 7ecb376..e4ea645 100644
--- a/backend/Auth/AuthEndpoints.cs
+++ b/backend/Auth/AuthEndpoints.cs
@@ -14,6 +14,7 @@ internal static class AuthEndpoints
{
private const string DashboardUrl = "https://repo.xsharp-lang.xyz/dashboard/";
private const string LoginUrl = "https://repo.xsharp-lang.xyz/login/";
+ private const string RegisterUrl = "https://repo.xsharp-lang.xyz/register/";
public static void MapAuthEndpoints(this WebApplication app)
{
@@ -23,6 +24,7 @@ internal static class AuthEndpoints
auth.MapPost("/verify-email/resend", ResendVerificationAsync);
auth.MapPost("/login", LoginAsync);
auth.MapPost("/logout", LogoutAsync).RequireAuthorization();
+ auth.MapDelete("/account", DeleteAccountAsync).RequireAuthorization();
auth.MapGet("/me", MeAsync).RequireAuthorization();
auth.MapGet("/providers", ProvidersAsync);
auth.MapGet("/google", BeginGoogleAsync);
@@ -152,15 +154,14 @@ internal static class AuthEndpoints
private static async Task<IResult> CompleteGoogleAsync(
SignInManager<ApplicationUser> signIn,
- UserManager<ApplicationUser> users)
+ UserManager<ApplicationUser> users,
+ AuthCodeService codes,
+ RegistryEmailSender emailSender,
+ CancellationToken cancellationToken)
{
ExternalLoginInfo? info = await signIn.GetExternalLoginInfoAsync();
if (info is null) return Results.Redirect($"{LoginUrl}?error=oauth");
- SignInResult existing = await signIn.ExternalLoginSignInAsync(
- info.LoginProvider, info.ProviderKey, isPersistent: true, bypassTwoFactor: false);
- if (existing.Succeeded) return Results.Redirect(DashboardUrl);
-
if (!TryNormalizeEmail(info.Principal.FindFirstValue(ClaimTypes.Email), out string email))
{
return Results.Redirect($"{LoginUrl}?error=missing_email");
@@ -168,20 +169,52 @@ internal static class AuthEndpoints
ApplicationUser? user = await users.FindByEmailAsync(email);
if (user is null)
{
- user = new ApplicationUser { UserName = email, Email = email, EmailConfirmed = true };
+ user = new ApplicationUser { UserName = email, Email = email };
IdentityResult created = await users.CreateAsync(user);
if (!created.Succeeded) return Results.Redirect($"{LoginUrl}?error=account");
}
- else if (!user.EmailConfirmed)
+
+ ApplicationUser? loginOwner = await users.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
+ if (loginOwner is null)
+ {
+ IdentityResult loginAdded = await users.AddLoginAsync(user, info);
+ if (!loginAdded.Succeeded) return Results.Redirect($"{LoginUrl}?error=oauth");
+ }
+ else if (loginOwner.Id != user.Id)
{
- user.EmailConfirmed = true;
- await users.UpdateAsync(user);
+ return Results.Redirect($"{LoginUrl}?error=oauth");
}
- IdentityResult loginAdded = await users.AddLoginAsync(user, info);
- if (!loginAdded.Succeeded) return Results.Redirect($"{LoginUrl}?error=oauth");
- await signIn.SignInAsync(user, isPersistent: true);
- return Results.Redirect(DashboardUrl);
+ string code = await codes.CreateAsync(user.Id, "signup", cancellationToken);
+ await emailSender.SendCodeAsync(email, code, "signup");
+ string destination = $"{RegisterUrl}?verify=google"
+ + $"&email={Uri.EscapeDataString(email)}";
+ return Results.Redirect(destination);
+ }
+
+ private static async Task<IResult> DeleteAccountAsync(
+ [Microsoft.AspNetCore.Mvc.FromBody] DeleteAccountRequest request,
+ ClaimsPrincipal principal,
+ UserManager<ApplicationUser> users,
+ SignInManager<ApplicationUser> signIn)
+ {
+ ApplicationUser? user = await users.GetUserAsync(principal);
+ if (user?.Email is null)
+ {
+ return Results.Unauthorized();
+ }
+ string confirmation = request.Confirmation?.Trim() ?? string.Empty;
+ if (!string.Equals(confirmation, user.Email, StringComparison.OrdinalIgnoreCase))
+ {
+ return Results.BadRequest(new ApiError(
+ "confirmation_mismatch",
+ "Enter the account email address to confirm deletion."));
+ }
+
+ IdentityResult result = await users.DeleteAsync(user);
+ if (!result.Succeeded) return IdentityErrors(result);
+ await signIn.SignOutAsync();
+ return Results.NoContent();
}
private static async Task<IResult> StartRecoveryAsync(
@@ -255,4 +288,5 @@ internal sealed record Credentials(string? Email, string? Password);
internal sealed record EmailRequest(string? Email);
internal sealed record VerificationRequest(string? Email, string? Code);
internal sealed record RecoveryRequest(string? Email, string? Code, string? Password);
+internal sealed record DeleteAccountRequest(string? Confirmation);
internal sealed record ApiError(string Error, string Message);