From 0ec7ee42fd43a6b0e5ee6c4a05cd45593a77c5f8 Mon Sep 17 00:00:00 2001 From: Paul James Date: Mon, 23 Oct 2023 00:55:52 -0400 Subject: [PATCH] Feat(AUTH-CPC-740): User Can Logout (#576) JIRA: link to jira ticket: https://champlainsaintlambert.atlassian.net/browse/CPC-740 ## Context: The logout functionality was incorrectly implemented, and it failed to remove the user account cookie upon logout. ## Changes Implemented a functional logout feature that clears the user account session cookie and removes associated local storage keys (e.g., email, roles, username, UUID). - Added logout methods in ApiGateway to facilitate logouts. - Enhanced the Frontend logout function to include the removal of the Bearer account session cookie. - Developed three comprehensive test cases to ensure the reliability of the new logout functionality. ## Before and After UI (Required for UI-impacting PRs) ### Before ![Logout_Cookie](https://github.com/cgerard321/champlain_petclinic/assets/77691550/0886d925-5232-43e3-b42c-aeef46ec7b65) ![logout_UnremovedCookie](https://github.com/cgerard321/champlain_petclinic/assets/77691550/c1a86078-da75-4abc-924a-6ba946fa9b73) ### After ![Logout_Cookie](https://github.com/cgerard321/champlain_petclinic/assets/77691550/8d324ca3-25f5-4ff5-bef7-5f76a9324f6c) ![Logout_Success](https://github.com/cgerard321/champlain_petclinic/assets/77691550/78e3ffa4-da0f-4828-81a1-f4edcdd14132) ![Logout_CookieRemoved](https://github.com/cgerard321/champlain_petclinic/assets/77691550/cd1ede7d-0eba-4138-8b77-a8e50874e595) ![logout_localStorage](https://github.com/cgerard321/champlain_petclinic/assets/77691550/155c2048-e2e5-426b-8572-0ef9c05b4cdc) ## Dev notes (Optional) None ## Linked pull requests (Optional) None --------- Co-authored-by: Dylan Brassard <71225455+DylanBrass@users.noreply.github.com> --- .../domainclientlayer/AuthServiceClient.java | 24 +++++++++++++ .../BFFApiGatewayController.java | 8 +++++ .../static/scripts/fragments/nav.html | 29 ++++++++++++---- .../AuthServiceClientIntegrationTest.java | 28 +++++++++++++++ .../ApiGatewayControllerTest.java | 34 +++++++++++++++++++ 5 files changed, 116 insertions(+), 7 deletions(-) diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/AuthServiceClient.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/AuthServiceClient.java index f01bac7b71..3046d212ca 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/AuthServiceClient.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/domainclientlayer/AuthServiceClient.java @@ -11,12 +11,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.time.Duration; +import java.util.List; import java.util.UUID; import static org.springframework.http.HttpStatus.BAD_REQUEST; @@ -242,6 +246,26 @@ public Mono> login(final Mono login) } } + public Mono> logout(ServerHttpRequest request, ServerHttpResponse response) { + log.info("Entered AuthServiceClient logout method"); + List cookies = request.getCookies().get("Bearer"); + if (cookies != null && !cookies.isEmpty()) { + ResponseCookie cookie = ResponseCookie.from("Bearer", "") + .httpOnly(true) + .secure(true) + .path("/api/gateway") + .domain("localhost") + .maxAge(Duration.ofSeconds(0)) + .sameSite("Lax").build(); + response.addCookie(cookie); + log.info("Logout Success: Account session ended"); + return Mono.just(ResponseEntity.noContent().build()); + } else { + log.warn("Logout Error: Problem removing account cookies, Session may have expired, redirecting to login page"); + return Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()); + } + } + public Mono> sendForgottenEmail(Mono emailRequestDTOMono) { diff --git a/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java b/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java index ca371a4c55..e79c9942f2 100644 --- a/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java +++ b/api-gateway/src/main/java/com/petclinic/bffapigateway/presentationlayer/BFFApiGatewayController.java @@ -24,6 +24,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.Resource; import org.springframework.http.*; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; @@ -777,6 +779,12 @@ public Mono> login(@RequestBody Mono } + @SecuredEndpoint(allowedRoles = {Roles.ANONYMOUS}) + @PostMapping("/users/logout") + public Mono> logout(ServerHttpRequest request, ServerHttpResponse response) { + return authServiceClient.logout(request, response); + } + @SecuredEndpoint(allowedRoles = {Roles.ANONYMOUS}) @PostMapping(value = "/users/forgot_password") diff --git a/api-gateway/src/main/resources/static/scripts/fragments/nav.html b/api-gateway/src/main/resources/static/scripts/fragments/nav.html index 829e169130..bed3466466 100644 --- a/api-gateway/src/main/resources/static/scripts/fragments/nav.html +++ b/api-gateway/src/main/resources/static/scripts/fragments/nav.html @@ -1,10 +1,25 @@