From e7f4ad50cc315dce6035456199e61a731fe5f8b7 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 12 Jan 2025 14:45:23 +0100 Subject: [PATCH 1/8] Update guidelines with instructions to run builds and tests after changes to backend and frontend --- .github/copilot-instructions.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 80f439b50..4b193a78a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,4 +1,4 @@ -When replying, list the sections from this rules file that guided your response. For example: +When replying, list *ALL* the sections from this guide that could be relevant to guide your response, and ensure to follow the guidance.For example: Sections used: - General Practices @@ -6,12 +6,14 @@ Sections used: - Backend Guidelines > API # General Practices (always include this section) -- AVOID unrelated code changes (e.g., don’t remove comments or alter types). -- Consistency is extremely important. Before implementing new code, always look for similar code in the existing code base, and do you utmost to follow conventions for naming, structure, patterns, formatting, styling, etc. -- SCS means self-contained system. -- Use long descriptive variable names (e.g commitMessage not commit). -- Never use acronyms (e.g. SharedAccessSignatureLink not SasLink). -- Use one-line imperative, sentence-case commit messages with no trailing dot. Don't prefix with "feat," "fix," etc. +- VERY IMPORTANT: + - Avoid making changes to code that is not relevant (e.g., don’t remove comments or alter types). + - Consistency is extremely important. Always look for similar code in the existing code base before adding new code, and do you utmost to follow conventions for naming, structure, patterns, formatting, styling, etc. + - Ask questions if guidance is unclear instead of making assumptions. +- General info: + - SCS means self-contained system. + - Use long descriptive variable names (e.g commitMessage not commit). + - Never use acronyms (e.g. SharedAccessSignatureLink not SasLink). # Project Overview PlatformPlatform is a multi-tenant SaaS foundation monorepo with: @@ -22,15 +24,17 @@ PlatformPlatform is a multi-tenant SaaS foundation monorepo with: # Backend Guidelines ## Over all -+ Always use new C# 8/9 language features like top-level namespaces, primary constructors and array initializers. ++ Always use new C# 8/9 language features like top-level namespaces, primary constructors, array initializers, and `is null`/`is not null` over `== null`/`!= null`. - Only throw exceptions for exceptional cases. - Prefer long lines, and break at 120-140 characters. - Use TimeProvider.System.GetUtcNow() to get current time. - All IDs on domain entities, commands, queries, API endpoints, etc. are strongly typed IDs. E.g. `TenantId Id` instead of `long Id`. +- IMPORTANT: After making backend changes, run `dotnet build` from `/application/` and `dotnet test --no-restore --no-build` to validate changes. ## API -- Always return Response DTOs -- Always use strongly TypedIDs in contract +- Implement in Endpoint namespace in the API project for the SCS. +- Always return Response DTOs. +- Always use strongly TypedIDs in contract. - Implement Minimal API endpoints in a single line, calling `mediator.Send()` and convert any path parameters using the ` with { Id = id }` like shown here: ```csharp @@ -115,16 +119,17 @@ PlatformPlatform is a multi-tenant SaaS foundation monorepo with: - Emphasize accessibility and type safety. - Leverage Tailwind variants for styling. - Global, reusable components live in Shared-Web. Change here only if it’s universally needed. +- IMPORTANT: After making frontend changes, run `npm run build` from `/application`, and if successful run `npm run format` and `npm run check`. ## React Aria Components - Build UI using components from `@application/shared-webapp/ui/components`. - Use `onPress` instead of `onClick`. ## API integration -- A strongly typed API Contract is generated by the .NET API in each SCS (look for @/WebApp/shared/lib/api/api.generated.d.ts) +- A strongly typed API Contract is generated by the .NET API in each SCS (look for @/WebApp/shared/lib/api/api.generated.d.ts). - When making API calls, don't use standard fetch, but instead use `@application/shared-webapp/infrastructure/api/PlatformApiClient.ts`, that contains methods for get, post, put, delete, options, head, patch, trace. -Here is an example of how to use the API client for a GET request +Here is an example of how to use the API client for a GET request: ```typescript await api.get("/api/account-management/users/{id}", { params: { path: { id: userId } } @@ -145,3 +150,6 @@ Here is an example of how to use the API client for a GET request - Avoid adding new dependencies to the root package.json. - If needed always Pin versions (no ^ or ~). - Use React Aria Components before adding anything new. + +# Git +- Use one-line imperative, sentence-case commit messages with no trailing dot; don't prefix with "feat," "fix," etc. From fb92e4c62cd4b395c4c3218844a199a2bc5836f9 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 12 Jan 2025 16:22:00 +0100 Subject: [PATCH 2/8] Add a new ApiResult extension to set an HTTP header that instructs AppGateway to refresh authentication tokens before returning the response --- .../Middleware/AuthenticationCookieMiddleware.cs | 15 +++++++++++---- .../SharedKernel/ApiResults/ApiResult.cs | 16 ++++++++++++++-- .../ApiResults/ApiResultExtensions.cs | 10 ++++++++++ .../AuthenticationTokenHttpKeys.cs | 2 ++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs b/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs index d86257aee..77e8108ec 100644 --- a/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs +++ b/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs @@ -26,8 +26,15 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) await next(context); - if (context.Response.Headers.TryGetValue(AuthenticationTokenHttpKeys.RefreshTokenHttpHeaderKey, out var refreshToken) && - context.Response.Headers.TryGetValue(AuthenticationTokenHttpKeys.AccessTokenHttpHeaderKey, out var accessToken)) + if (context.Response.Headers.TryGetValue(AuthenticationTokenHttpKeys.RefreshAuthenticationTokensHeaderKey, out _)) + { + logger.LogDebug("Refreshing authentication tokens as requested by endpoint."); + var (refreshToken, accessToken) = await RefreshAuthenticationTokensAsync(refreshTokenCookieValue!); + ReplaceAuthenticationHeaderWithCookie(context, refreshToken, accessToken); + context.Response.Headers.Remove(AuthenticationTokenHttpKeys.RefreshAuthenticationTokensHeaderKey); + } + else if (context.Response.Headers.TryGetValue(AuthenticationTokenHttpKeys.RefreshTokenHttpHeaderKey, out var refreshToken) && + context.Response.Headers.TryGetValue(AuthenticationTokenHttpKeys.AccessTokenHttpHeaderKey, out var accessToken)) { ReplaceAuthenticationHeaderWithCookie(context, refreshToken.Single()!, accessToken.Single()!); } @@ -54,6 +61,8 @@ private async Task ValidateAuthenticationCookieAndConvertToHttpBearerHeader(Http return; } + logger.LogDebug("The access-token has expired, attempting to refresh."); + (refreshToken, accessToken) = await RefreshAuthenticationTokensAsync(refreshToken); // Update the authentication token cookies with the new tokens @@ -74,8 +83,6 @@ private async Task ValidateAuthenticationCookieAndConvertToHttpBearerHeader(Http private async Task<(string newRefreshToken, string newAccessToken)> RefreshAuthenticationTokensAsync(string refreshToken) { - logger.LogDebug("The access-token has expired, attempting to refresh..."); - var request = new HttpRequestMessage(HttpMethod.Post, RefreshAuthenticationTokensEndpoint); // Use refresh Token as Bearer when refreshing Access Token diff --git a/application/shared-kernel/SharedKernel/ApiResults/ApiResult.cs b/application/shared-kernel/SharedKernel/ApiResults/ApiResult.cs index 359d3cb2f..b8571d4ee 100644 --- a/application/shared-kernel/SharedKernel/ApiResults/ApiResult.cs +++ b/application/shared-kernel/SharedKernel/ApiResults/ApiResult.cs @@ -6,12 +6,23 @@ namespace PlatformPlatform.SharedKernel.ApiResults; -public class ApiResult(ResultBase result, string? routePrefix = null) : IResult +public class ApiResult(ResultBase result, string? routePrefix = null, IDictionary? httpHeaders = null) + : IResult { protected string? RoutePrefix { get; } = routePrefix; + private IDictionary? HttpHeaders { get; } = httpHeaders; + public Task ExecuteAsync(HttpContext httpContext) { + if (HttpHeaders is not null) + { + foreach (var (key, value) in HttpHeaders) + { + httpContext.Response.Headers[key] = value; + } + } + return ConvertResult().ExecuteAsync(httpContext); } @@ -47,7 +58,8 @@ public static implicit operator ApiResult(Result result) } } -public sealed class ApiResult(Result result, string? routePrefix = null) : ApiResult(result, routePrefix) +public sealed class ApiResult(Result result, string? routePrefix = null, IDictionary? httpHeaders = null) + : ApiResult(result, routePrefix, httpHeaders) { protected override IResult ConvertResult() { diff --git a/application/shared-kernel/SharedKernel/ApiResults/ApiResultExtensions.cs b/application/shared-kernel/SharedKernel/ApiResults/ApiResultExtensions.cs index 62e03a2c1..aab9da571 100644 --- a/application/shared-kernel/SharedKernel/ApiResults/ApiResultExtensions.cs +++ b/application/shared-kernel/SharedKernel/ApiResults/ApiResultExtensions.cs @@ -1,3 +1,4 @@ +using PlatformPlatform.SharedKernel.Authentication; using PlatformPlatform.SharedKernel.Cqrs; namespace PlatformPlatform.SharedKernel.ApiResults; @@ -8,4 +9,13 @@ public static ApiResult AddResourceUri(this Result result, string routePre { return new ApiResult(result, routePrefix); } + + public static ApiResult AddRefreshAuthenticationTokens(this Result result) + { + if (!result.IsSuccess) return new ApiResult(result); + + return new ApiResult(result, httpHeaders: new Dictionary + { { AuthenticationTokenHttpKeys.RefreshAuthenticationTokensHeaderKey, "true" } } + ); + } } diff --git a/application/shared-kernel/SharedKernel/Authentication/AuthenticationTokenHttpKeys.cs b/application/shared-kernel/SharedKernel/Authentication/AuthenticationTokenHttpKeys.cs index 6f5036d3a..0af942540 100644 --- a/application/shared-kernel/SharedKernel/Authentication/AuthenticationTokenHttpKeys.cs +++ b/application/shared-kernel/SharedKernel/Authentication/AuthenticationTokenHttpKeys.cs @@ -9,4 +9,6 @@ public static class AuthenticationTokenHttpKeys public const string RefreshTokenHttpHeaderKey = "x-refresh-token"; public const string AccessTokenHttpHeaderKey = "x-access-token"; + + public const string RefreshAuthenticationTokensHeaderKey = "x-refresh-authentication-tokens-required"; } From 0f7b4152baa406050508ac18aa8be15394b2ffc4 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 12 Jan 2025 17:15:49 +0100 Subject: [PATCH 3/8] Update change-locale endpoint to enforce authentication token updates and eliminate the need to reload the SPA --- .../account-management/Api/Endpoints/UserEndpoints.cs | 2 +- .../infrastructure/translations/LocaleSwitcher.tsx | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/application/account-management/Api/Endpoints/UserEndpoints.cs b/application/account-management/Api/Endpoints/UserEndpoints.cs index f2ca00737..527e9c6ad 100644 --- a/application/account-management/Api/Endpoints/UserEndpoints.cs +++ b/application/account-management/Api/Endpoints/UserEndpoints.cs @@ -51,7 +51,7 @@ public void MapEndpoints(IEndpointRouteBuilder routes) ); group.MapPut("/change-locale", async Task (ChangeLocaleCommand command, IMediator mediator) - => await mediator.Send(command) + => (await mediator.Send(command)).AddRefreshAuthenticationTokens() ); group.MapGet("/summary", async Task> (IMediator mediator) diff --git a/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx b/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx index b1eddeafe..a66ac327a 100644 --- a/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx +++ b/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx @@ -33,15 +33,8 @@ export function LocaleSwitcher() { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ locale: newLocale }) }) - .then(() => { - // Refresh the authentication tokens to update the JWT locale claim - fetch("/api/account-management/authentication/refresh-authentication-tokens", { - method: "POST", - headers: { "Content-Type": "application/json" } - }).then(() => { - // Reload the page to - window.location.reload(); - }); + .then(async (_) => { + await setLocale(newLocale); }) .catch((error) => console.error("Failed to update locale:", error)); } else { From 15894c826b07e5b4b4e4b64b8ded66d9f0c3c671 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 12 Jan 2025 18:13:10 +0100 Subject: [PATCH 4/8] Update /admin/user PUT endpoint to utilize the logged-in user for updating user information instead of the {id} parameter --- .../Api/Endpoints/UserEndpoints.cs | 17 +- .../Features/Users/Commands/UpdateUser.cs | 8 +- .../Tests/Users/UpdateUserTests.cs | 27 +--- .../userModals/UserProfileModal.tsx | 2 +- .../shared/lib/api/AccountManagement.Api.json | 153 ++++++++---------- 5 files changed, 85 insertions(+), 122 deletions(-) diff --git a/application/account-management/Api/Endpoints/UserEndpoints.cs b/application/account-management/Api/Endpoints/UserEndpoints.cs index 527e9c6ad..8f2642593 100644 --- a/application/account-management/Api/Endpoints/UserEndpoints.cs +++ b/application/account-management/Api/Endpoints/UserEndpoints.cs @@ -18,6 +18,10 @@ public void MapEndpoints(IEndpointRouteBuilder routes) => await mediator.Send(query) ).Produces(); + group.MapGet("/summary", async Task> (IMediator mediator) + => await mediator.Send(new GetUserSummaryQuery()) + ).Produces(); + group.MapGet("/{id}", async Task> ([AsParameters] GetUserQuery query, IMediator mediator) => await mediator.Send(query) ).Produces(); @@ -26,10 +30,6 @@ public void MapEndpoints(IEndpointRouteBuilder routes) => (await mediator.Send(command)).AddResourceUri(RoutesPrefix) ); - group.MapPut("/{id}", async Task (UserId id, UpdateUserCommand command, IMediator mediator) - => await mediator.Send(command with { Id = id }) - ); - group.MapDelete("/{id}", async Task (UserId id, IMediator mediator) => await mediator.Send(new DeleteUserCommand(id)) ); @@ -42,6 +42,11 @@ public void MapEndpoints(IEndpointRouteBuilder routes) => await mediator.Send(command) ); + // The following endpoints are for the current user only + group.MapPut("/", async Task (UpdateUserCommand command, IMediator mediator) + => await mediator.Send(command) + ); + group.MapPost("/update-avatar", async Task (IFormFile file, IMediator mediator) => await mediator.Send(new UpdateAvatarCommand(file.OpenReadStream(), file.ContentType)) ).DisableAntiforgery(); // Disable anti-forgery until we implement it @@ -53,9 +58,5 @@ public void MapEndpoints(IEndpointRouteBuilder routes) group.MapPut("/change-locale", async Task (ChangeLocaleCommand command, IMediator mediator) => (await mediator.Send(command)).AddRefreshAuthenticationTokens() ); - - group.MapGet("/summary", async Task> (IMediator mediator) - => await mediator.Send(new GetUserSummaryQuery()) - ).Produces(); } } diff --git a/application/account-management/Core/Features/Users/Commands/UpdateUser.cs b/application/account-management/Core/Features/Users/Commands/UpdateUser.cs index 6bb9b0c64..d76b7d778 100644 --- a/application/account-management/Core/Features/Users/Commands/UpdateUser.cs +++ b/application/account-management/Core/Features/Users/Commands/UpdateUser.cs @@ -2,7 +2,6 @@ using JetBrains.Annotations; using PlatformPlatform.AccountManagement.Features.Users.Domain; using PlatformPlatform.SharedKernel.Cqrs; -using PlatformPlatform.SharedKernel.Domain; using PlatformPlatform.SharedKernel.Telemetry; using PlatformPlatform.SharedKernel.Validation; @@ -11,9 +10,6 @@ namespace PlatformPlatform.AccountManagement.Features.Users.Commands; [PublicAPI] public sealed record UpdateUserCommand : ICommand, IRequest { - [JsonIgnore] // Removes this property from the API contract - public UserId Id { get; init; } = null!; - public required string Email { get; init; } public required string FirstName { get; init; } @@ -39,8 +35,8 @@ public sealed class UpdateUserHandler(IUserRepository userRepository, ITelemetry { public async Task Handle(UpdateUserCommand command, CancellationToken cancellationToken) { - var user = await userRepository.GetByIdAsync(command.Id, cancellationToken); - if (user is null) return Result.NotFound($"User with id '{command.Id}' not found."); + var user = await userRepository.GetLoggedInUserAsync(cancellationToken); + if (user is null) return Result.BadRequest("User not found."); user.UpdateEmail(command.Email); user.Update(command.FirstName, command.LastName, command.Title); diff --git a/application/account-management/Tests/Users/UpdateUserTests.cs b/application/account-management/Tests/Users/UpdateUserTests.cs index 81dc822cc..54ac53b9f 100644 --- a/application/account-management/Tests/Users/UpdateUserTests.cs +++ b/application/account-management/Tests/Users/UpdateUserTests.cs @@ -2,7 +2,6 @@ using System.Net.Http.Json; using PlatformPlatform.AccountManagement.Database; using PlatformPlatform.AccountManagement.Features.Users.Commands; -using PlatformPlatform.SharedKernel.Domain; using PlatformPlatform.SharedKernel.Tests; using PlatformPlatform.SharedKernel.Validation; using Xunit; @@ -15,7 +14,6 @@ public sealed class UpdateUserTests : EndpointBaseTest Date: Sun, 12 Jan 2025 23:33:14 +0100 Subject: [PATCH 5/8] Update /admin/user PUT endpoint to enforce authentication token refresh and remove the need to reload the SPA --- .../Api/Endpoints/UserEndpoints.cs | 2 +- .../userModals/UserProfileModal.tsx | 62 +++++++++++++------ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/application/account-management/Api/Endpoints/UserEndpoints.cs b/application/account-management/Api/Endpoints/UserEndpoints.cs index 8f2642593..8b0414364 100644 --- a/application/account-management/Api/Endpoints/UserEndpoints.cs +++ b/application/account-management/Api/Endpoints/UserEndpoints.cs @@ -44,7 +44,7 @@ public void MapEndpoints(IEndpointRouteBuilder routes) // The following endpoints are for the current user only group.MapPut("/", async Task (UpdateUserCommand command, IMediator mediator) - => await mediator.Send(command) + => (await mediator.Send(command)).AddRefreshAuthenticationTokens() ); group.MapPost("/update-avatar", async Task (IFormFile file, IMediator mediator) diff --git a/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx b/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx index c915e7041..a55c1cb77 100644 --- a/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx +++ b/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx @@ -1,4 +1,4 @@ -import { useActionState, useCallback, useEffect, useRef, useState } from "react"; +import { useActionState, useCallback, useContext, useEffect, useRef, useState } from "react"; import { FileTrigger, Form, Heading, Label } from "react-aria-components"; import { Menu, MenuItem, MenuSeparator, MenuTrigger } from "@repo/ui/components/Menu"; import { CameraIcon, Trash2Icon, XIcon } from "lucide-react"; @@ -11,6 +11,7 @@ import type { Schemas } from "@/shared/lib/api/client"; import { api } from "@/shared/lib/api/client"; import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; +import { AuthenticationContext } from "@repo/infrastructure/auth/AuthenticationProvider"; const MAX_FILE_SIZE = 1024 * 1024; // 1MB in bytes const ALLOWED_FILE_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"]; // Align with backend @@ -29,9 +30,12 @@ export default function UserProfileModal({ isOpen, onOpenChange, userId }: Reado const [avatarPreviewUrl, setAvatarPreviewUrl] = useState(null); const [avatarMenuOpen, setAvatarMenuOpen] = useState(false); const [removeAvatarFlag, setRemoveAvatarFlag] = useState(false); + const [isSaving, setIsSaving] = useState(false); const avatarFileInputRef = useRef(null); + const { updateUserInfo } = useContext(AuthenticationContext); + // Fetch user data when modal opens useEffect(() => { if (isOpen) { @@ -42,6 +46,7 @@ export default function UserProfileModal({ isOpen, onOpenChange, userId }: Reado setAvatarPreviewUrl(null); setAvatarMenuOpen(false); setRemoveAvatarFlag(false); + setIsSaving(false); api .get("/api/account-management/users/{id}", { params: { path: { id: userId } } }) @@ -62,33 +67,52 @@ export default function UserProfileModal({ isOpen, onOpenChange, userId }: Reado }, [onOpenChange, avatarPreviewUrl]); // Handle form submission - let [{ success, errors, title, message }, action, isPending] = useActionState( + const [{ success, errors, title, message }, action, isPending] = useActionState( api.actionPut("/api/account-management/users"), { success: null } ); const handleFormSubmit = async (formData: FormData) => { - if (selectedAvatarFile) { - await api.uploadFile("/api/account-management/users/update-avatar", selectedAvatarFile); - } else if (removeAvatarFlag) { - await api.delete("/api/account-management/users/remove-avatar"); - setRemoveAvatarFlag(false); + if (isSaving) return; + setIsSaving(true); + + try { + if (selectedAvatarFile) { + await api.uploadFile("/api/account-management/users/update-avatar", selectedAvatarFile); + } else if (removeAvatarFlag) { + await api.delete("/api/account-management/users/remove-avatar"); + setRemoveAvatarFlag(false); + } + + action(formData); + } catch (error) { + console.error("Failed to update profile:", error); + setIsSaving(false); } - action(formData); }; + // Handle success state useEffect(() => { - if (isPending) { - success = undefined; - } - - if (success) { - closeDialog(); - api - .post("/api/account-management/authentication/refresh-authentication-tokens") - .then(() => window.location.reload()); + if (success && isSaving) { + // Add a small delay to ensure all requests have completed + setTimeout(async () => { + try { + const response = await api.get("/api/account-management/users/{id}", { params: { path: { id: userId } } }); + updateUserInfo({ + firstName: response.firstName, + lastName: response.lastName, + title: response.title, + avatarUrl: response.avatarUrl ?? undefined + }); + closeDialog(); + } catch (error) { + console.error("Failed to fetch updated user data:", error); + } finally { + setIsSaving(false); + } + }, 100); } - }, [success, isPending, closeDialog]); + }, [success, closeDialog, userId, updateUserInfo, isSaving]); // Handle file selection const onFileSelect = (files: FileList | null) => { @@ -232,7 +256,7 @@ export default function UserProfileModal({ isOpen, onOpenChange, userId }: Reado - From 26ca8eccd493f7d270ba3122dc43ac0bd202aad2 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Mon, 13 Jan 2025 00:48:52 +0100 Subject: [PATCH 6/8] Remove logic from AvatarButton that triggers UserProfileModal to open twice --- .../WebApp/shared/components/AvatarButton.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/application/account-management/WebApp/shared/components/AvatarButton.tsx b/application/account-management/WebApp/shared/components/AvatarButton.tsx index 373b58829..dca88c253 100644 --- a/application/account-management/WebApp/shared/components/AvatarButton.tsx +++ b/application/account-management/WebApp/shared/components/AvatarButton.tsx @@ -72,10 +72,6 @@ export default function AvatarButton() { /> - - {userInfo?.isAuthenticated && ( - - )} ); } From 97b0e00ddca6ea441728c6fb9f203b0ebf33ab0c Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Mon, 13 Jan 2025 10:04:08 +0100 Subject: [PATCH 7/8] Simplify the use of useActionState in InviteUserModal --- .../routes/admin/users/-components/InviteUserModal.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx b/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx index 4fff7b9b7..eddd1b58b 100644 --- a/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx +++ b/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx @@ -21,21 +21,17 @@ export default function InviteUserModal({ isOpen, onOpenChange }: Readonly { - if (isPending) { - success = undefined; - } - if (success) { closeDialog(); window.location.reload(); } - }, [success, isPending, closeDialog]); + }, [success, closeDialog]); return ( From 7b38657749acbfd1cb6ccf071747d5647b156005 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Mon, 13 Jan 2025 19:09:11 +0100 Subject: [PATCH 8/8] Make /refresh-authentication-tokens and internal-api endpoint --- .../Middleware/AuthenticationCookieMiddleware.cs | 2 +- .../Api/Endpoints/AuthenticationEndpoints.cs | 2 +- .../WebApp/shared/lib/api/AccountManagement.Api.json | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs b/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs index 77e8108ec..91d33457d 100644 --- a/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs +++ b/application/AppGateway/Middleware/AuthenticationCookieMiddleware.cs @@ -14,7 +14,7 @@ ILogger logger ) : IMiddleware { - private const string? RefreshAuthenticationTokensEndpoint = "/api/account-management/authentication/refresh-authentication-tokens"; + private const string? RefreshAuthenticationTokensEndpoint = "/internal-api/account-management/authentication/refresh-authentication-tokens"; public async Task InvokeAsync(HttpContext context, RequestDelegate next) { diff --git a/application/account-management/Api/Endpoints/AuthenticationEndpoints.cs b/application/account-management/Api/Endpoints/AuthenticationEndpoints.cs index c92d442bb..3e288ef86 100644 --- a/application/account-management/Api/Endpoints/AuthenticationEndpoints.cs +++ b/application/account-management/Api/Endpoints/AuthenticationEndpoints.cs @@ -30,7 +30,7 @@ public void MapEndpoints(IEndpointRouteBuilder routes) ); // Note: This endpoint must be called with the refresh token as Bearer token in the Authorization header - group.MapPost("refresh-authentication-tokens", async Task (IMediator mediator) + routes.MapPost("/internal-api/account-management/authentication/refresh-authentication-tokens", async Task (IMediator mediator) => await mediator.Send(new RefreshAuthenticationTokensCommand()) ); } diff --git a/application/account-management/WebApp/shared/lib/api/AccountManagement.Api.json b/application/account-management/WebApp/shared/lib/api/AccountManagement.Api.json index 062f827be..7b195c4cb 100644 --- a/application/account-management/WebApp/shared/lib/api/AccountManagement.Api.json +++ b/application/account-management/WebApp/shared/lib/api/AccountManagement.Api.json @@ -118,12 +118,9 @@ } } }, - "/api/account-management/authentication/refresh-authentication-tokens": { + "/internal-api/account-management/authentication/refresh-authentication-tokens": { "post": { - "tags": [ - "Authentication" - ], - "operationId": "PostApiAccountManagementAuthenticationRefreshAuthenticationTokens", + "operationId": "PostInternalApiAccountManagementAuthenticationRefreshAuthenticationTokens", "responses": { "200": { "description": ""