Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: logout endpoint #87

Merged
merged 7 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions api/src/main/java/lab/en2b/quizapi/auth/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
import lab.en2b.quizapi.auth.dtos.*;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
Expand All @@ -25,6 +24,12 @@ public ResponseEntity<JwtResponseDto> loginUser(@Valid @RequestBody LoginDto log
return ResponseEntity.ok(authService.login(loginRequest));
}

@GetMapping("/logout")
public ResponseEntity<Void> logoutUser(Authentication authentication){
authService.logOut(authentication);
return ResponseEntity.noContent().build();
}

@PostMapping("/refresh-token")
public ResponseEntity<RefreshTokenResponseDto> refreshToken(@Valid @RequestBody RefreshTokenDto refreshTokenRequest){
return ResponseEntity.ok(authService.refreshToken(refreshTokenRequest));
Expand Down
5 changes: 5 additions & 0 deletions api/src/main/java/lab/en2b/quizapi/auth/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ public RefreshTokenResponseDto refreshToken(RefreshTokenDto refreshTokenRequest)
"Refresh token is not in database!"));
return new RefreshTokenResponseDto(jwtUtils.generateTokenFromEmail(user.getEmail()), user.obtainRefreshIfValid());
}

public void logOut(Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
userService.deleteRefreshToken(userDetails.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, Authentication
.cors(Customizer.withDefaults())
.sessionManagement(configuration -> configuration.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(HttpMethod.GET,"/auth/logout").authenticated()
.requestMatchers(HttpMethod.POST,"/auth/**").permitAll()
.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.authenticationManager(authenticationManager)
.addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
//TODO: add exception handling
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ public String assignNewRefreshToken(Long id) {
userRepository.save(user);
return user.getRefreshToken();
}

public void deleteRefreshToken(Long id) {
User user = userRepository.findById(id).orElseThrow();
user.setRefreshToken(null);
user.setRefreshExpiration(null);
userRepository.save(user);
}
}
23 changes: 23 additions & 0 deletions api/src/test/java/lab/en2b/quizapi/auth/AuthControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(AuthController.class)
Expand Down Expand Up @@ -110,6 +112,19 @@ void refreshTokenEmptyTokenShouldReturn400() throws Exception {
testRefreshToken(asJsonString( new RefreshTokenDto("")), status().isBadRequest());
}

@Test
void logoutShouldReturn204() throws Exception {
testLogout(status().isNoContent());
}

@Test
void logoutNoAuthShouldReturn403() throws Exception {
mockMvc.perform(get("/auth/logout")
.contentType("application/json")
.with(csrf()))
.andExpect(status().isForbidden());
}

private void testRegister(String content, ResultMatcher code) throws Exception {
mockMvc.perform(post("/auth/register")
.content(content)
Expand All @@ -133,4 +148,12 @@ private void testRefreshToken(String content, ResultMatcher code) throws Excepti
.with(csrf()))
.andExpect(code);
}

private void testLogout(ResultMatcher code) throws Exception {
mockMvc.perform(get("/auth/logout")
.with(user("test").roles("user"))
.contentType("application/json")
.with(csrf()))
.andExpect(code);
}
}
12 changes: 12 additions & 0 deletions api/src/test/java/lab/en2b/quizapi/auth/AuthServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -97,4 +98,15 @@ void testRefreshToken(){
assertEquals(new RefreshTokenResponseDto("jwtToken","token"),actual);

}

@Test
void testLogout(){
Authentication authentication = mock(Authentication.class);
when(authentication.getPrincipal()).thenReturn(UserDetailsImpl.build(defaultUser));
when(userRepository.findById(any())).thenReturn(Optional.of(defaultUser));

authService.logOut(authentication);
assertNull(defaultUser.getRefreshToken());
assertNull(defaultUser.getRefreshExpiration());
}
}