Fix team profile auth recovery
CI - Build & Verify / Build, Typecheck & Hygiene (push) Successful in 54s
CI - Build & Verify / Deploy to award.noveria.net (push) Successful in 1m5s

This commit is contained in:
AzuTear
2026-06-26 20:06:10 +02:00
parent 47fe59c42a
commit aeade132f9
15 changed files with 237 additions and 37 deletions
@@ -17,6 +17,14 @@ public static partial class AuthEndpoints
return Results.Unauthorized();
}
var teamMember = await FindTeamMemberForSessionAsync(db, session, context.RequestAborted);
if (teamMember is not null)
{
return Results.Json(
new { message = "Team-Accounts werden nicht ueber die automatische Teilnahme-Datenloeschung entfernt." },
statusCode: StatusCodes.Status403Forbidden);
}
var twitchUserId = session.TwitchUserId;
await using var transaction = await db.Database.BeginTransactionAsync(context.RequestAborted);
+5
View File
@@ -33,6 +33,11 @@ public static partial class AuthEndpoints
.WithName("StartTwitchAuthorization")
.WithOpenApi();
group.MapDelete("/twitch/binding", DisconnectTwitchBinding)
.RequireRateLimiting(ApplicationDefaults.AuthRateLimitPolicy)
.WithName("DisconnectTwitchBinding")
.WithOpenApi();
group.MapGet("/twitch/callback", CompleteTwitchAuthorization)
.RequireRateLimiting(ApplicationDefaults.AuthRateLimitPolicy)
.WithName("CompleteTwitchAuthorization")
@@ -212,6 +212,62 @@ public static partial class AuthEndpoints
return RedirectToTwitchCallback(oauthState, "connected");
}
private static async Task<IResult> DisconnectTwitchBinding(
HttpContext context,
AwardsDbContext db,
IUserSessionService userSessionService)
{
var session = await userSessionService.ResolveSessionAsync(context, context.RequestAborted);
if (session is null)
{
return Results.Unauthorized();
}
var member = await FindTeamMemberForSessionAsync(db, session, context.RequestAborted);
if (member is null || !member.IsActive)
{
return Results.BadRequest(new { message = "Dieser Account nutzt keinen aktiven Team-Login." });
}
var boundTwitchUserId = NormalizeTwitchUserId(member.BoundTwitchUserId);
if (string.IsNullOrWhiteSpace(boundTwitchUserId))
{
return Results.Ok(new TwitchBindingDisconnectResponse(
false,
false,
await ToAuthSessionDtoAsync(db, session, cancellationToken: context.RequestAborted)));
}
var currentTeamLogin = ReadTeamLoginFromSession(session.TwitchUserId);
var currentSessionUsesBoundTwitch = string.IsNullOrWhiteSpace(currentTeamLogin)
&& string.Equals(NormalizeTwitchUserId(session.TwitchUserId), boundTwitchUserId, StringComparison.OrdinalIgnoreCase);
foreach (var linkedSession in db.UserSessions.Where(item => item.TwitchUserId == boundTwitchUserId))
{
linkedSession.IsActive = false;
}
member.BoundTwitchUserId = null;
member.BoundTwitchDisplayName = null;
member.TwitchBoundAt = null;
member.UpdatedAt = DateTimeOffset.UtcNow;
member.UpdatedByTwitchId = session.TwitchUserId;
if (currentSessionUsesBoundTwitch)
{
session.IsActive = false;
}
await db.SaveChangesAsync(context.RequestAborted);
return Results.Ok(new TwitchBindingDisconnectResponse(
true,
currentSessionUsesBoundTwitch,
currentSessionUsesBoundTwitch
? null
: await ToAuthSessionDtoAsync(db, session, cancellationToken: context.RequestAborted)));
}
private static async Task<IResult> CompleteTwitchTeamLoginAsync(
HttpContext context,
TwitchOAuthState oauthState,