fix switch club and RefreshSign

This commit is contained in:
beo3000 2026-01-02 22:48:00 +01:00
parent 7a5580e57d
commit 3b65f9f1cd
6 changed files with 106 additions and 16 deletions

View File

@ -52,6 +52,15 @@ namespace Koogle.Application.DTOs
public string ConfirmPassword { get; set; } = ""; public string ConfirmPassword { get; set; } = "";
} }
/// <summary>
/// From DTO for switching session from one club to another
/// </summary>
public class SwitchClubFormDto
{
public Guid UserProfileId { get; set; }
public Guid ClubId { get; set; }
}
/// <summary> /// <summary>
/// DTO for user registration. /// DTO for user registration.
/// </summary> /// </summary>

View File

@ -709,6 +709,22 @@ public class UserService : IUserService
profile.CurrentClubId = clubId; profile.CurrentClubId = clubId;
await _appDb.SaveChangesAsync(ct); await _appDb.SaveChangesAsync(ct);
// Refresh signin to update claims with new club
var identityUser = await _userManager.FindByIdAsync(profile.IdentityUserId.ToString());
if (identityUser != null)
{
try
{
await _signInManager.RefreshSignInAsync(identityUser);
}
catch (Exception e)
{
throw;
}
}
return true; return true;
} }

View File

@ -1,10 +1,16 @@
@using System.Net
@using Fluxor @using Fluxor
@using Koogle.Application.DTOs
@using Koogle.Web.Store.AuthState @using Koogle.Web.Store.AuthState
@using Koogle.Application.Interfaces @using Koogle.Application.Interfaces
@inject IState<AuthState> AuthState @inject IState<AuthState> AuthState
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserService UserService @inject IUserService UserService
@inject HttpClient HttpClient;
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
@inject IHttpContextAccessor HttpContextAccessor
@inject IDispatcher Dispatcher
@inherits Fluxor.Blazor.Web.Components.FluxorComponent @inherits Fluxor.Blazor.Web.Components.FluxorComponent
@ -22,10 +28,10 @@
@foreach (var club in AuthState.Value.AvailableClubs) @foreach (var club in AuthState.Value.AvailableClubs)
{ {
<MudMenuItem OnClick="@(() => SwitchClubAsync(club.ClubId))" <MudMenuItem OnClick="@(() => SwitchClubAsync(club.ClubId))"
Disabled="@(club.ClubId == AuthState.Value.CurrentClub?.ClubId)"> Disabled="@(club.ClubId == AuthState.Value.CurrentClub?.ClubId)">
@if (club.ClubId == AuthState.Value.CurrentClub?.ClubId) @if (club.ClubId == AuthState.Value.CurrentClub?.ClubId)
{ {
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Class="mr-2" /> <MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Class="mr-2"/>
} }
@club.ClubName @club.ClubName
</MudMenuItem> </MudMenuItem>
@ -47,23 +53,64 @@ else if (AuthState.Value.IsAuthenticated && AuthState.Value.HasNoClub)
} }
@code { @code {
private string _antiToken;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
var http = HttpContextAccessor.HttpContext!;
var tokens = Antiforgery.GetAndStoreTokens(http);
_antiToken = tokens.RequestToken!;
}
}
private async Task SwitchClubAsync(Guid clubId) private async Task SwitchClubAsync(Guid clubId)
{ {
if (AuthState.Value.CurrentUser == null) if (AuthState.Value.CurrentUser == null)
return; return;
var model = new SwitchClubFormDto()
{
ClubId = clubId,
UserProfileId = AuthState.Value.CurrentUser.ProfileId
};
try try
{ {
var success = await UserService.SwitchClubAsync(AuthState.Value.CurrentUser.ProfileId, clubId); HttpClient.DefaultRequestHeaders.Remove("RequestVerificationToken");
if (success) HttpClient.DefaultRequestHeaders.Add(
{ "RequestVerificationToken",
// Force page reload to refresh claims _antiToken
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); );
}
var basepath = NavigationManager.BaseUri;
var url = $"{basepath}auth/switch-club";
await HttpClient.PostAsJsonAsync(url, model);
// Dispatcher.Dispatch(new AuthState.InitializeAuthSuccessAction(model.UserProfileId, model.ClubId, roles));
NavigationManager.NavigateTo("/dashboard", forceLoad: true);
} }
catch (Exception ex) catch (Exception e)
{ {
Console.WriteLine($"Club switch failed: {ex.Message}"); Console.WriteLine(e);
throw;
} }
// try
// {
// var success = await UserService.SwitchClubAsync(AuthState.Value.CurrentUser.ProfileId, clubId);
// if (success)
// {
// // Force page reload to refresh claims
// NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
// }
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Club switch failed: {ex.Message}");
// }
} }
} }

View File

@ -7,6 +7,7 @@ using Koogle.Web.Store.AuthState;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static QRCoder.PayloadGenerator;
namespace Koogle.Web.Controllers namespace Koogle.Web.Controllers
{ {
@ -18,7 +19,8 @@ namespace Koogle.Web.Controllers
/// Aus dem Grund werden Login/Logout Aktionen hier im MVC Controller mit einem "normalen" Post-Request abgewickelt. /// Aus dem Grund werden Login/Logout Aktionen hier im MVC Controller mit einem "normalen" Post-Request abgewickelt.
/// </remarks> /// </remarks>
[Microsoft.AspNetCore.Mvc.Route("auth")] [Microsoft.AspNetCore.Mvc.Route("auth")]
public class AuthController(IDispatcher dispatcher, IUserService userService, IEmailService emailService) : Controller public class AuthController(IDispatcher dispatcher, IUserService userService, IEmailService emailService)
: Controller
{ {
private readonly IDispatcher _dispatcher = dispatcher; private readonly IDispatcher _dispatcher = dispatcher;
private readonly IUserService _userService = userService; private readonly IUserService _userService = userService;
@ -37,6 +39,7 @@ namespace Koogle.Web.Controllers
// (club-setup if no clubs, dashboard otherwise) // (club-setup if no clubs, dashboard otherwise)
return LocalRedirect(result.RedirectUrl ?? "/dashboard"); return LocalRedirect(result.RedirectUrl ?? "/dashboard");
} }
return LocalRedirect("/account/login?error=true"); return LocalRedirect("/account/login?error=true");
} }
@ -67,8 +70,10 @@ namespace Koogle.Web.Controllers
// If invite token was provided, redirect to join page // If invite token was provided, redirect to join page
if (!string.IsNullOrWhiteSpace(input.InviteToken)) if (!string.IsNullOrWhiteSpace(input.InviteToken))
{ {
return LocalRedirect($"/account/login?registered=true&invite={Uri.EscapeDataString(input.InviteToken)}"); return LocalRedirect(
$"/account/login?registered=true&invite={Uri.EscapeDataString(input.InviteToken)}");
} }
return LocalRedirect("/account/login?registered=true"); return LocalRedirect("/account/login?registered=true");
} }
@ -93,7 +98,8 @@ namespace Koogle.Web.Controllers
if (token != null) if (token != null)
{ {
var baseUrl = $"{Request.Scheme}://{Request.Host}"; var baseUrl = $"{Request.Scheme}://{Request.Host}";
var resetUrl = $"{baseUrl}/account/reset-password?email={Uri.EscapeDataString(email)}&token={Uri.EscapeDataString(token)}"; var resetUrl =
$"{baseUrl}/account/reset-password?email={Uri.EscapeDataString(email)}&token={Uri.EscapeDataString(token)}";
await _emailService.SendPasswordResetEmailAsync(email, resetUrl); await _emailService.SendPasswordResetEmailAsync(email, resetUrl);
} }
@ -110,7 +116,8 @@ namespace Koogle.Web.Controllers
// Validate password confirmation // Validate password confirmation
if (input.NewPassword != input.ConfirmPassword) if (input.NewPassword != input.ConfirmPassword)
{ {
return LocalRedirect($"/account/reset-password?email={Uri.EscapeDataString(input.Email)}&token={Uri.EscapeDataString(input.Token)}&error=PasswordMismatch"); return LocalRedirect(
$"/account/reset-password?email={Uri.EscapeDataString(input.Email)}&token={Uri.EscapeDataString(input.Token)}&error=PasswordMismatch");
} }
var dto = new ResetPasswordDto var dto = new ResetPasswordDto
@ -127,9 +134,20 @@ namespace Koogle.Web.Controllers
} }
var errors = string.Join(",", result.Errors.Select(e => e.Code)); var errors = string.Join(",", result.Errors.Select(e => e.Code));
return LocalRedirect($"/account/reset-password?email={Uri.EscapeDataString(input.Email)}&token={Uri.EscapeDataString(input.Token)}&error={errors}"); return LocalRedirect(
$"/account/reset-password?email={Uri.EscapeDataString(input.Email)}&token={Uri.EscapeDataString(input.Token)}&error={errors}");
}
/// <summary>
/// Handles switch club.
/// </summary>
[HttpPost("switch-club")]
//[ValidateAntiForgeryToken]
public async Task<IActionResult> SwitchClub([FromBody] SwitchClubFormDto input)
{
await _userService.SwitchClubAsync(input.UserProfileId, input.ClubId);
return LocalRedirect($"/dashboard");
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 KiB