diff --git a/backend/src/main/kotlin/com/lightswitch/application/service/AuthService.kt b/backend/src/main/kotlin/com/lightswitch/application/service/AuthService.kt index 4246866..de5ae11 100644 --- a/backend/src/main/kotlin/com/lightswitch/application/service/AuthService.kt +++ b/backend/src/main/kotlin/com/lightswitch/application/service/AuthService.kt @@ -32,7 +32,10 @@ class AuthService( user.lastLoginAt = LocalDateTime.now() userRepository.save(user) - return jwtTokenProvider.generateJwtToken(user.id!!, user) + val jwtToken = jwtTokenProvider.generateJwtToken(user.id!!, user) + refreshTokenRepository.save(RefreshToken(user.id!!, jwtToken.refreshToken!!)) + + return jwtToken } @Transactional @@ -50,16 +53,11 @@ class AuthService( } @Transactional - fun reissue(jwtToken: JwtToken): JwtToken? { - if (!jwtTokenProvider.validateToken(jwtToken.refreshToken!!)) { - throw BusinessException("Refresh Token is Not Valid") - } - - val userId: Long = jwtTokenProvider.getRefreshTokenSubject(jwtToken.refreshToken) + fun reissue(jwtToken: String, userId: Long): JwtToken? { val refreshToken: RefreshToken = refreshTokenRepository.findById(userId) .orElseThrow { BusinessException("Log-out user") } - if (refreshToken.value != jwtToken.refreshToken) { + if (refreshToken.value != jwtToken) { throw BusinessException("Refresh Token is Not Valid") } @@ -70,11 +68,16 @@ class AuthService( jwtTokenProvider.isRefreshTokenRenewalRequired(refreshToken.value) -> { jwtTokenProvider.generateJwtToken(userId, user).also { refreshToken.value = it.refreshToken.toString() - refreshTokenRepository.save(refreshToken) } } - - else -> jwtTokenProvider.generateJwtAccessToken(userId, user, Date()) + else -> { + jwtTokenProvider.generateJwtAccessToken(userId, user, Date(), refreshToken.value) + } } } + + @Transactional + fun logout(userId: Long) { + refreshTokenRepository.deleteById(userId) + } } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/lightswitch/controller/UserController.kt b/backend/src/main/kotlin/com/lightswitch/controller/UserController.kt new file mode 100644 index 0000000..8dcbf7b --- /dev/null +++ b/backend/src/main/kotlin/com/lightswitch/controller/UserController.kt @@ -0,0 +1,71 @@ +package com.lightswitch.controller + +import com.lightswitch.application.service.AuthService +import com.lightswitch.controller.request.UserAccount +import com.lightswitch.infrastructure.security.JwtToken +import com.lightswitch.presentation.model.PayloadResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import jakarta.validation.Valid +import jakarta.validation.constraints.NotEmpty +import org.springframework.security.core.Authentication +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/api/v1/users") +class UserController(private val authService: AuthService) { + @Operation( + summary = "Login the user", + description = "Authenticates a user with their username and password, and returns a JWT token." + ) + @PostMapping("/login") + fun userLogin(@RequestBody @Valid body: UserAccount): PayloadResponse { + val token = authService.login(body.username, body.password) + return PayloadResponse( + "Success", + message = "User Login Success", + data = token + ) + } + + @Operation( + summary = "Initialize user account", + description = "Registers a new user with the provided username and password. Returns a confirmation message." + ) + @PostMapping("/initialize") + fun userInitialize(@RequestBody @Valid body: UserAccount): PayloadResponse { + val user = authService.signup(body.username, body.password) + return PayloadResponse( + "Success", + message = "Signup New User Success", + data = user.username + ) + } + + @Operation( + summary = "Refresh authentication token", + description = "Reissues a new JWT token using the provided refresh token and current user's identity." + ) + @PutMapping("/auth/refresh") + fun refreshUserToken(@RequestHeader("Authorization") @NotEmpty @Parameter(description = "The refresh token prefixed with 'Bearer '.") refreshToken: String): PayloadResponse { + val authentication: Authentication = SecurityContextHolder.getContext().authentication + val token = authService.reissue(refreshToken.removePrefix("Bearer "), authentication.name.toLong()) + return PayloadResponse("Success", "Refresh Token Success", token) + } + + @Operation( + summary = "Logout the user", + description = "Logs out the user by invalidating their access token. Requires the user to provide their token." + ) + @PostMapping("/logout") + fun userLogout( + @RequestHeader("Authorization") @NotEmpty @Parameter(description = "The access token prefixed with 'Bearer '.") accessToken: String, + @RequestBody @Valid body: UserAccount, + ): PayloadResponse { + val authentication: Authentication = SecurityContextHolder.getContext().authentication + val userId = authentication.name + authService.logout(userId.toLong()) + return PayloadResponse("Success", "Logout Success", body.username) + } +} diff --git a/backend/src/main/kotlin/com/lightswitch/controller/request/UserAccount.kt b/backend/src/main/kotlin/com/lightswitch/controller/request/UserAccount.kt new file mode 100644 index 0000000..60f18fd --- /dev/null +++ b/backend/src/main/kotlin/com/lightswitch/controller/request/UserAccount.kt @@ -0,0 +1,14 @@ +package com.lightswitch.controller.request + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank + +@Schema(description = "Represents a user's account credentials including username and password.") +data class UserAccount( + @Schema(description = "The username of the user.") + @field:NotBlank(message = "Username is required.") + val username: String, + @Schema(description = "The password of the user.") + @field:NotBlank(message = "Password is required.") + val password: String +) \ No newline at end of file diff --git a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtToken.kt b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtToken.kt index 9400071..22f65af 100644 --- a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtToken.kt +++ b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtToken.kt @@ -1,7 +1,13 @@ package com.lightswitch.infrastructure.security +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(description = "Represents the JWT token response containing both access and refresh tokens, as well as the expiration date of the access token.") data class JwtToken( + @Schema(description = "The access token used to authenticate the user in subsequent requests. This token is valid for 30 minutes.") val accessToken: String? = null, + @Schema(description = "The refresh token used to obtain a new access token once it expires. This token is valid for 7 days.") val refreshToken: String? = null, + @Schema(description = "The expiration date (timestamp in milliseconds) of the access token. The token becomes invalid after this time.") val accessTokenExpiredDate: Long? = null ) diff --git a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenFilter.kt b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenFilter.kt index 47f8d6d..b79d2d4 100644 --- a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenFilter.kt +++ b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenFilter.kt @@ -10,10 +10,14 @@ import org.springframework.web.filter.OncePerRequestFilter import java.io.IOException @Component -class JwtTokenFilter(private val jwtTokenProvider: JwtTokenProvider) : OncePerRequestFilter() { +class JwtTokenFilter( + private val jwtTokenProvider: JwtTokenProvider, +) : OncePerRequestFilter() { - private val HEADER_STRING = "Authorization" - private val TOKEN_PREFIX = "Bearer " + companion object { + const val HEADER_STRING = "Authorization" + const val TOKEN_PREFIX = "Bearer " + } @Throws(ServletException::class, IOException::class) public override fun doFilterInternal( diff --git a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenProvider.kt b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenProvider.kt index a3b9bee..cd09f95 100644 --- a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenProvider.kt +++ b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/JwtTokenProvider.kt @@ -18,14 +18,16 @@ import javax.crypto.SecretKey class JwtTokenProvider(@Value("\${jwt.secret}") private var secretKey: String) { private val logger: Logger = LoggerFactory.getLogger(JwtTokenProvider::class.java) - private val TYPE = "tokenType" - private val USER = "user" - private val accessValidTime = 30 * 60 * 1000L // 30 minutes - private val refreshValidTime = 7 * 24 * 60 * 60 * 1000L // 7 days - private val threeDays = 3 * 24 * 60 * 60 * 1000L // 3 days - fun generateJwtToken(userId: Long, user: User): JwtToken { + companion object { + const val TYPE = "tokenType" + const val USER = "user" + const val ACCESS_VALID_TIME = 30 * 60 * 1000L // 30 minutes + const val REFRESH_VALID_TIME = 7 * 24 * 60 * 60 * 1000L // 7 days + const val THREE_DAYS = 3 * 24 * 60 * 60 * 1000L // 3 days + } + fun generateJwtToken(userId: Long, user: User): JwtToken { val now = Date() val accessToken = createAccessToken(userId, user, now) @@ -35,14 +37,14 @@ class JwtTokenProvider(@Value("\${jwt.secret}") private var secretKey: String) { val refreshToken = Jwts.builder() .setClaims(refreshTokenClaims) .setIssuedAt(now) - .setExpiration(Date(now.time + refreshValidTime)) + .setExpiration(Date(now.time + REFRESH_VALID_TIME)) .signWith(getSigningKey(), SignatureAlgorithm.HS256) .compact() return JwtToken( accessToken = accessToken, refreshToken = refreshToken, - accessTokenExpiredDate = accessValidTime + accessTokenExpiredDate = ACCESS_VALID_TIME ) } @@ -59,7 +61,7 @@ class JwtTokenProvider(@Value("\${jwt.secret}") private var secretKey: String) { val accessToken = Jwts.builder() .setClaims(accessTokenClaims) .setIssuedAt(now) - .setExpiration(Date(now.time + accessValidTime)) + .setExpiration(Date(now.time + ACCESS_VALID_TIME)) .signWith(getSigningKey(), SignatureAlgorithm.HS256) .compact() return accessToken @@ -81,12 +83,12 @@ class JwtTokenProvider(@Value("\${jwt.secret}") private var secretKey: String) { return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey)) } - fun generateJwtAccessToken(userId: Long, user: User, now: Date): JwtToken { + fun generateJwtAccessToken(userId: Long, user: User, now: Date, refreshToken: String): JwtToken { val accessToken = createAccessToken(userId, user, now) return JwtToken( accessToken = accessToken, - refreshToken = null, - accessTokenExpiredDate = accessValidTime + refreshToken = refreshToken, + accessTokenExpiredDate = ACCESS_VALID_TIME ) } @@ -149,7 +151,7 @@ class JwtTokenProvider(@Value("\${jwt.secret}") private var secretKey: String) { val now = (Date()).time val refreshExpiredTime = claimsJws.body.expiration.time - return refreshExpiredTime - now > threeDays + return refreshExpiredTime - now <= THREE_DAYS } } \ No newline at end of file diff --git a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/SecurityConfig.kt b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/SecurityConfig.kt index ab5079f..00dc19d 100644 --- a/backend/src/main/kotlin/com/lightswitch/infrastructure/security/SecurityConfig.kt +++ b/backend/src/main/kotlin/com/lightswitch/infrastructure/security/SecurityConfig.kt @@ -31,11 +31,14 @@ class SecurityConfig( fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { http + .csrf { + it.disable() + } .authorizeHttpRequests { auth -> auth .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.REQUEST) .permitAll() - .requestMatchers("/login/**").permitAll() + .requestMatchers("/api/v1/users/**").permitAll() .anyRequest().authenticated() } @@ -46,7 +49,10 @@ class SecurityConfig( .formLogin { form -> form.disable() } - .addFilterBefore(JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter::class.java) + .addFilterBefore( + JwtTokenFilter(jwtTokenProvider), + UsernamePasswordAuthenticationFilter::class.java + ) return http.build() } diff --git a/backend/src/test/kotlin/com/lightswitch/application/service/AuthServiceTest.kt b/backend/src/test/kotlin/com/lightswitch/application/service/AuthServiceTest.kt index 19ddedc..58cd946 100644 --- a/backend/src/test/kotlin/com/lightswitch/application/service/AuthServiceTest.kt +++ b/backend/src/test/kotlin/com/lightswitch/application/service/AuthServiceTest.kt @@ -11,11 +11,13 @@ import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers -import org.mockito.kotlin.any -import org.mockito.kotlin.eq import org.mockito.Mock -import org.mockito.Mockito +import org.mockito.Mockito.* import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify +import org.springframework.security.core.Authentication import org.springframework.security.crypto.password.PasswordEncoder import java.time.LocalDateTime import java.util.* @@ -65,13 +67,13 @@ class AuthServiceTest { val password = "correctPassword" val jwtToken = JwtToken("accessToken", "refreshToken", 1800) - Mockito.`when`(userRepository.findByUsername(username)) + `when`(userRepository.findByUsername(username)) .thenReturn(user) - Mockito.`when`(passwordEncoder.matches(password, user.passwordHash)) + `when`(passwordEncoder.matches(password, user.passwordHash)) .thenReturn(true) - Mockito.`when`(jwtTokenProvider.generateJwtToken(user.id!!, user)) + `when`(jwtTokenProvider.generateJwtToken(user.id!!, user)) .thenReturn(jwtToken) val result = authService.login(username, password) @@ -79,6 +81,8 @@ class AuthServiceTest { assertNotNull(result) assertEquals(jwtToken.accessToken, result.accessToken) assertEquals(jwtToken.refreshToken, result.refreshToken) + verify(userRepository).save(user) + verify(refreshTokenRepository).save(any()) } @Test @@ -86,7 +90,7 @@ class AuthServiceTest { val username = "invalidUser" val password = "somePassword" - Mockito.`when`(userRepository.findByUsername(username)) + `when`(userRepository.findByUsername(username)) .thenReturn(null) val exception = assertThrows(BusinessException::class.java) { @@ -100,10 +104,10 @@ class AuthServiceTest { val username = "testUser" val password = "incorrectPassword" - Mockito.`when`(userRepository.findByUsername(username)) + `when`(userRepository.findByUsername(username)) .thenReturn(user) - Mockito.`when`(passwordEncoder.matches(password, user.passwordHash)) + `when`(passwordEncoder.matches(password, user.passwordHash)) .thenReturn(false) val exception = assertThrows(BusinessException::class.java) { @@ -122,19 +126,20 @@ class AuthServiceTest { passwordHash = passwordHash ) - Mockito.`when`(userRepository.existsByUsername(username)) + `when`(userRepository.existsByUsername(username)) .thenReturn(false) - Mockito.`when`(passwordEncoder.encode(password)) + `when`(passwordEncoder.encode(password)) .thenReturn(passwordHash) - Mockito.`when`(userRepository.save(ArgumentMatchers.any(User::class.java))) + `when`(userRepository.save(ArgumentMatchers.any(User::class.java))) .thenReturn(newUser) val result = authService.signup(username, password) assertNotNull(result) assertEquals(username, result.username) + verify(userRepository).save(any(User::class.java)) } @Test @@ -142,7 +147,7 @@ class AuthServiceTest { val username = "existingUser" val password = "somePassword" - Mockito.`when`(userRepository.existsByUsername(username)) + `when`(userRepository.existsByUsername(username)) .thenReturn(true) val exception = assertThrows(BusinessException::class.java) { @@ -156,80 +161,77 @@ class AuthServiceTest { val userId = 1L val token = JwtToken("accessToken", "refreshToken", 1800) val newToken = JwtToken("newAccessToken", "newRefreshToken", 1800) - val refreshTokenValue = "refreshToken" - - Mockito.`when`(jwtTokenProvider.validateToken(refreshTokenValue)) - .thenReturn(true) - - Mockito.`when`(jwtTokenProvider.getRefreshTokenSubject(refreshTokenValue)) - .thenReturn(userId) - Mockito.`when`(refreshTokenRepository.findById(userId)) + `when`(refreshTokenRepository.findById(userId)) .thenReturn(Optional.of(this.refreshToken)) - Mockito.`when`(userRepository.findById(userId)) + `when`(userRepository.findById(userId)) .thenReturn(Optional.of(user)) - Mockito.`when`(jwtTokenProvider.isRefreshTokenRenewalRequired(token.refreshToken)) + `when`(jwtTokenProvider.isRefreshTokenRenewalRequired(token.refreshToken)) .thenReturn(true) - Mockito.`when`(jwtTokenProvider.generateJwtToken(userId, user)) + `when`(jwtTokenProvider.generateJwtToken(userId, user)) .thenReturn(newToken) - val result = authService.reissue(token) + val result = authService.reissue(token.refreshToken.toString(), userId) assertNotNull(result) assertEquals(newToken.accessToken, result?.accessToken) assertEquals(newToken.refreshToken, result?.refreshToken) } - @Test - fun `reissue should throw BusinessException if refresh token is invalid`() { - val invalidRefreshToken = JwtToken("", "invalidToken", 1800) - - Mockito.`when`(jwtTokenProvider.validateToken(invalidRefreshToken.refreshToken!!)) - .thenReturn(false) - - val exception = assertThrows(BusinessException::class.java) { - authService.reissue(invalidRefreshToken) - } - assertEquals("Refresh Token is Not Valid", exception.message) - } @Test fun `reissue should return new JwtToken if refresh token is valid but no renewal is required`() { val userId = 1L val token = JwtToken("accessToken", "refreshToken", 1800) - val newToken = JwtToken("newAccessToken", null, 1800) + val newToken = JwtToken("newAccessToken", "refreshToken", 1800) - Mockito.`when`(jwtTokenProvider.validateToken(token.refreshToken!!)) + `when`(jwtTokenProvider.validateToken(token.refreshToken!!)) .thenReturn(true) - Mockito.`when`(jwtTokenProvider.getRefreshTokenSubject(token.refreshToken!!)) + `when`(jwtTokenProvider.getRefreshTokenSubject(token.refreshToken!!)) .thenReturn(userId) - Mockito.`when`(refreshTokenRepository.findById(userId)) + `when`(refreshTokenRepository.findById(userId)) .thenReturn(Optional.of(this.refreshToken)) - Mockito.`when`(userRepository.findById(userId)) + `when`(userRepository.findById(userId)) .thenReturn(Optional.of(user)) - Mockito.`when`(jwtTokenProvider.isRefreshTokenRenewalRequired(token.refreshToken)) + `when`(jwtTokenProvider.isRefreshTokenRenewalRequired(token.refreshToken)) .thenReturn(false) - Mockito.`when`( + `when`( jwtTokenProvider.generateJwtAccessToken( eq(userId), eq(user), - any() + any(), + eq( + token.refreshToken.toString() + ) ) - ) - .thenReturn(newToken) + ).thenReturn(newToken) - val result = authService.reissue(token) + val result = authService.reissue(token.refreshToken.toString(), userId) assertNotNull(result) assertEquals(newToken.accessToken, result?.accessToken) - assertNull(result?.refreshToken) + } + + @Test + fun `logout success test`() { + val accessToken = "validToken" + val authentication = mock(Authentication::class.java) + `when`(jwtTokenProvider.getAuthentication(accessToken)).thenReturn(authentication) + `when`(authentication.name).thenReturn("1") + + authService.logout(1L) + + verify( + refreshTokenRepository, + times(1) + ).deleteById(1L) } } diff --git a/backend/src/test/kotlin/com/lightswitch/controller/UserControllerTest.kt b/backend/src/test/kotlin/com/lightswitch/controller/UserControllerTest.kt new file mode 100644 index 0000000..880ab87 --- /dev/null +++ b/backend/src/test/kotlin/com/lightswitch/controller/UserControllerTest.kt @@ -0,0 +1,90 @@ +package com.lightswitch.controller + +import com.lightswitch.application.service.AuthService +import com.lightswitch.infrastructure.security.JwtToken +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.junit.jupiter.MockitoExtension +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.MediaType +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.Authentication +import org.springframework.security.core.context.SecurityContext +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import org.springframework.test.web.servlet.setup.MockMvcBuilders + +@ExtendWith(MockitoExtension::class) +class UserControllerTest { + + @Autowired + lateinit var mockMvc: MockMvc + + @Mock + lateinit var authService: AuthService + + @InjectMocks + lateinit var userController: UserController + + @BeforeEach + fun setup() { + mockMvc = MockMvcBuilders.standaloneSetup(userController).build() + val authentication: Authentication = UsernamePasswordAuthenticationToken("1", "password", emptyList()) + val securityContext: SecurityContext = SecurityContextHolder.createEmptyContext() + securityContext.authentication = authentication + SecurityContextHolder.setContext(securityContext) + } + + @Test + fun `should login user successfully`() { + val jwtToken = JwtToken("accessToken", "refreshToken") + + `when`(authService.login("testUser", "password")).thenReturn(jwtToken) + + mockMvc.perform( + MockMvcRequestBuilders.post("/api/v1/users/login") + .contentType(MediaType.APPLICATION_JSON) + .content("""{"username": "testUser", "password": "password"}""") + ) + .andExpect(MockMvcResultMatchers.status().isOk) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.accessToken").value("accessToken")) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.refreshToken").value("refreshToken")) + } + + + @Test + fun `should refresh user token successfully`() { + val refreshToken = "Bearer refreshToken" + val jwtToken = JwtToken("newAccessToken", "newRefreshToken") + + `when`(authService.reissue("refreshToken", 1L)).thenReturn(jwtToken) + + mockMvc.perform( + MockMvcRequestBuilders.put("/api/v1/users/auth/refresh") + .header("Authorization", refreshToken) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(MockMvcResultMatchers.status().isOk) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.accessToken").value("newAccessToken")) + .andExpect(MockMvcResultMatchers.jsonPath("$.data.refreshToken").value("newRefreshToken")) + } + + @Test + fun `should logout user successfully`() { + mockMvc.perform( + MockMvcRequestBuilders.post("/api/v1/users/logout") + .header("Authorization", "Bearer someAccessToken") + .contentType(MediaType.APPLICATION_JSON) + .content("""{"username": "testUser", "password": "password"}""") + ) + .andExpect(MockMvcResultMatchers.status().isOk) + .andExpect(MockMvcResultMatchers.jsonPath("$.data").value("testUser")) + + } +} diff --git a/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenFilterTest.kt b/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenFilterTest.kt index 56e3073..ea1c4e8 100644 --- a/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenFilterTest.kt +++ b/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenFilterTest.kt @@ -5,6 +5,7 @@ import com.lightswitch.infrastructure.security.JwtTokenProvider import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.Mock @@ -47,7 +48,7 @@ class JwtTokenFilterTest { jwtTokenFilter.doFilterInternal(request, response, filterChain) val context: SecurityContext = SecurityContextHolder.getContext() - assert(context.authentication == authentication) + assertThat(context.authentication == authentication) Mockito.verify(filterChain).doFilter(request, response) } @@ -60,7 +61,7 @@ class JwtTokenFilterTest { jwtTokenFilter.doFilterInternal(request, response, filterChain) val context: SecurityContext = SecurityContextHolder.getContext() - assert(context.authentication == null) + assertThat(context.authentication == null) Mockito.verify(filterChain).doFilter(request, response) } @@ -71,7 +72,7 @@ class JwtTokenFilterTest { jwtTokenFilter.doFilterInternal(request, response, filterChain) val context: SecurityContext = SecurityContextHolder.getContext() - assert(context.authentication == null) + assertThat(context.authentication == null) Mockito.verify(filterChain).doFilter(request, response) } } diff --git a/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenProviderTest.kt b/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenProviderTest.kt index 95499dd..58ecd52 100644 --- a/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenProviderTest.kt +++ b/backend/src/test/kotlin/com/lightswitch/infrastructue/security/JwtTokenProviderTest.kt @@ -42,10 +42,10 @@ class JwtTokenProviderTest { @Test fun `generateJwtAccessToken should return only access token`() { val now = Date() - val jwtToken = jwtTokenProvider.generateJwtAccessToken(user.id!!, user, now) + val jwtToken = jwtTokenProvider.generateJwtAccessToken(user.id!!, user, now, "testRefreshToken") assertNotNull(jwtToken.accessToken) - assertNull(jwtToken.refreshToken) + assertEquals("testRefreshToken",jwtToken.refreshToken) assertTrue(jwtToken.accessTokenExpiredDate!! > 0) } @@ -101,11 +101,11 @@ class JwtTokenProviderTest { val result = jwtTokenProvider.isRefreshTokenRenewalRequired(refreshToken) - assertTrue(result) + assertFalse(result) } @Test - fun `refreshTokenPeriodCheck should return false if refresh token expired less than 3 days`() { + fun `refreshTokenPeriodCheck should return true if refresh token expired less than 3 days`() { val now = Date() val issuedTime = Date(now.time - 5 * 24 * 60 * 60 * 1000L) val expiredTime = Date(now.time + 2 * 24 * 60 * 60 * 1000L) @@ -119,7 +119,7 @@ class JwtTokenProviderTest { val result = jwtTokenProvider.isRefreshTokenRenewalRequired(refreshToken) - assertFalse(result) + assertTrue(result) } }