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

[YS-65] feat: 테스트용 토큰 강제 발급 API & AccessToken 갱신 API 구현 #16

Merged
merged 37 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ee8b4ec
style: add line for EOF
Ji-soo708 Jan 1, 2025
3560fb0
chore: update springdoc version
Ji-soo708 Jan 1, 2025
871ad82
feat: add generateTestTokenForTestMember method for test
Ji-soo708 Jan 1, 2025
225f15b
feat: add UseCase interface
Ji-soo708 Jan 1, 2025
c83fe2f
feat: add GenerateTestToken api for test member
Ji-soo708 Jan 1, 2025
e66f71f
test: change memberId type from String to Long in test code
Ji-soo708 Jan 1, 2025
ec088d0
chore: add mock dependency for test
Ji-soo708 Jan 1, 2025
e915406
test: add GenerateTestToken test code
Ji-soo708 Jan 1, 2025
2f42a22
test: refactor JwtTokenProviderTest to use Kotest style
Ji-soo708 Jan 1, 2025
5ae21ef
style: rename dto name
Ji-soo708 Jan 1, 2025
8bfa21a
feat: add @ComponentScan to include UseCase beans in application context
Ji-soo708 Jan 1, 2025
2387360
refact: refactor TokenProvider code
Ji-soo708 Jan 2, 2025
ef72cda
feat: add dto for SignIn logic
Ji-soo708 Jan 2, 2025
63d0863
feat: add GenerateTokenWithRefreshToken
Ji-soo708 Jan 2, 2025
83b41c1
test: add GenerateTokenWithRefreshToken test code
Ji-soo708 Jan 2, 2025
da69998
refact: rename entity
Ji-soo708 Jan 2, 2025
65ffa0c
refact: refactor member domain
Ji-soo708 Jan 2, 2025
6387bdb
test: fix test due to changed domain
Ji-soo708 Jan 2, 2025
c08fad6
feat: add MemberGateway
Ji-soo708 Jan 2, 2025
c207cf8
feat: add GetMemberById usecase
Ji-soo708 Jan 2, 2025
065c00b
refact: update MemberRefreshToken response
Ji-soo708 Jan 2, 2025
6db0c19
style: add line for eof
Ji-soo708 Jan 2, 2025
9abb0c3
Merge branch 'dev' into feature/YS-65
Ji-soo708 Jan 2, 2025
6a48fbf
style: update role example
Ji-soo708 Jan 2, 2025
eb68e03
[YS-31] feat: 구글 OAuth 로그인 구현 (#13)
chock-cho Jan 3, 2025
36a0d1c
feat: add GenerateTestToken api for test member
Ji-soo708 Jan 1, 2025
e26fdbf
test: refactor JwtTokenProviderTest to use Kotest style
Ji-soo708 Jan 1, 2025
f5c8cab
feat: add @ComponentScan to include UseCase beans in application context
Ji-soo708 Jan 1, 2025
170352d
feat: add GenerateTokenWithRefreshToken
Ji-soo708 Jan 2, 2025
0f6b999
refact: rename entity
Ji-soo708 Jan 2, 2025
aae3d23
test: fix test due to changed domain
Ji-soo708 Jan 2, 2025
a333982
refact: update MemberRefreshToken response
Ji-soo708 Jan 2, 2025
93550d8
fix: fix conflicts for merge
Ji-soo708 Jan 3, 2025
8538d2c
refact: move usecase file to application
Ji-soo708 Jan 3, 2025
edca1e9
refact: refactor OAuthLoginResponse response structure
Ji-soo708 Jan 3, 2025
7df78cd
fix: fix conflicts for merge
Ji-soo708 Jan 3, 2025
fedb6de
fix: fix conflicts for merge
Ji-soo708 Jan 3, 2025
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
4 changes: 3 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("com.github.f4b6a3:ulid-creator:5.2.3")
implementation("org.mariadb.jdbc:mariadb-java-client:2.7.3")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
compileOnly("org.projectlombok:lombok")
runtimeOnly("com.mysql:mysql-connector-j")
runtimeOnly("com.h2database:h2")
Expand All @@ -60,6 +60,8 @@ dependencies {
testImplementation("io.kotest:kotest-assertions-core:$koTestVersion")
testImplementation("io.kotest:kotest-property:$koTestVersion")
testImplementation("io.kotest.extensions:kotest-extensions-spring:1.1.2")

testImplementation("io.mockk:mockk:1.13.10")
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ import com.dobby.backend.domain.model.Member
interface TokenGateway {
fun generateAccessToken(member: Member): String
fun generateRefreshToken(member: Member): String
fun generateAccessTokenForTestMember(memberId: Long): String
fun generateRefreshTokenForTestMember(memberId: Long): String
fun extractMemberIdFromRefreshToken(token: String): String
}
7 changes: 3 additions & 4 deletions src/main/kotlin/com/dobby/backend/domain/model/Member.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package com.dobby.backend.domain.model

import com.dobby.backend.util.generateULID

data class Member(
val memberId: String,
val memberId: Long,
val name: String,
val email: String,
) {

companion object {
fun newMember(
memberId: Long,
name: String,
email: String,
) = Member(
memberId = generateULID(),
memberId = memberId,
name = name,
email = email,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.dobby.backend.domain.usecase

import com.dobby.backend.domain.gateway.TokenGateway
import org.springframework.stereotype.Component

@Component
class GenerateTestToken(
private val tokenGateway: TokenGateway
) : UseCase<GenerateTestToken.Input,
GenerateTestToken.Output> {
data class Input(
val memberId: Long
)

data class Output(
val accessToken: String,
val refreshToken: String,
)

override fun execute(input: Input): Output {
val memberId = input.memberId
return Output(
accessToken = tokenGateway.generateAccessTokenForTestMember(memberId),
refreshToken = tokenGateway.generateRefreshTokenForTestMember(memberId)
)
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/com/dobby/backend/domain/usecase/UseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.dobby.backend.domain.usecase

fun interface UseCase<I, O> {
fun execute(input: I): O
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ abstract class AuditingEntity {
@LastModifiedDate
@Column(name = "updated_at", nullable = false)
lateinit var updatedAt: LocalDateTime
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package com.dobby.backend.infrastructure.database.entity.enum

enum class RoleType {
RESEARCHER, PARTICIPANT
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ class TokenGatewayImpl(
return tokenProvider.generateRefreshToken(authentication)
}

override fun generateAccessTokenForTestMember(memberId: Long): String {
val authentication = UsernamePasswordAuthenticationToken(
memberId,
null,
)
return tokenProvider.generateAccessToken(authentication)
}

override fun generateRefreshTokenForTestMember(memberId: Long): String {
val authentication = UsernamePasswordAuthenticationToken(
memberId,
null,
)
return tokenProvider.generateRefreshToken(authentication)
}

override fun extractMemberIdFromRefreshToken(token: String): String {
return tokenProvider.getMemberIdFromRefreshToken(token)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.dobby.backend.presentation.api.controller

import com.dobby.backend.domain.usecase.GenerateTestToken
import com.dobby.backend.infrastructure.token.JwtTokenProvider
import com.dobby.backend.presentation.api.dto.response.TestMemberSignInResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.*

@Tag(name = "인증 관련 API")
@RestController
@RequestMapping("/v1/auth")
class AuthController(
private val generateTestToken: GenerateTestToken,
) {
@Operation(summary = "테스트용 토큰 강제 발급", description = "memberId로 테스트용 토큰을 발급합니다")
@PostMapping("/force-token")
fun forceToken(
@RequestParam memberId: Long
): TestMemberSignInResponse {
val result = generateTestToken.execute(
GenerateTestToken.Input(memberId)
)

return TestMemberSignInResponse(
accessToken = result.accessToken,
refreshToken = result.refreshToken
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.dobby.backend.presentation.api.dto.response

import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "테스트용 로그인 결과 DTO")
data class TestMemberSignInResponse(
@Schema(description = "엑세스 토큰")
val accessToken: String,

@Schema(description = "리프레쉬 토큰")
val refreshToken: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.dobby.backend.domain.usecase

import io.kotest.core.spec.style.BehaviorSpec
import com.dobby.backend.domain.gateway.TokenGateway
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk

class GenerateTestTokenTest: BehaviorSpec({
val tokenGateway = mockk<TokenGateway>()
val generateTestToken = GenerateTestToken(tokenGateway)

given("memberId가 주어졌을 때") {
val memberId = 123L
val accessToken = "testAccessToken"
val refreshToken = "testRefreshToken"

every { tokenGateway.generateAccessTokenForTestMember(memberId) } returns accessToken
every { tokenGateway.generateRefreshTokenForTestMember(memberId) } returns refreshToken

`when`("execute가 호출되면") {
val input = GenerateTestToken.Input(memberId)
val result = generateTestToken.execute(input)

then("생성된 accessToken과 refreshToken이 반환되어야 한다") {
result.accessToken shouldBe accessToken
result.refreshToken shouldBe refreshToken
}
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package com.dobby.backend.infrastructure.token
import com.dobby.backend.domain.exception.AuthenticationTokenNotValidException
import com.dobby.backend.domain.model.Member
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull

@SpringBootTest
class JwtTokenProviderTest : BehaviorSpec() {
Expand All @@ -18,30 +18,30 @@ class JwtTokenProviderTest : BehaviorSpec() {

init {
given("회원 정보가 주어지고") {
val member = Member(memberId = "1", email = "dlawotn3@naver.com", name = "dobby")
val member = Member(memberId = 1, email = "dlawotn3@naver.com", name = "dobby")
val authentication = UsernamePasswordAuthenticationToken(member.memberId, null)

`when`("해당 인증 정보로 JWT 토큰을 생성하면") {
val jwtToken = jwtTokenProvider.generateAccessToken(authentication)

then("JWT 토큰이 생성된다") {
assertNotNull(jwtToken)
jwtToken shouldNotBe null
}
}
}

given("유효한 JWT 토큰이 주어지고") {
val member = Member(memberId = "1", email = "dlawotn3@naver.com", name = "dobby")
val member = Member(memberId = 1, email = "dlawotn3@naver.com", name = "dobby")
val authentication = UsernamePasswordAuthenticationToken(member.memberId, null)
val validToken = jwtTokenProvider.generateAccessToken(authentication)

`when`("해당 토큰을 파싱하면") {
val parsedAuthentication = jwtTokenProvider.parseAuthentication(validToken)
val extractedMemberId = parsedAuthentication.principal as String
val extractedMemberId = parsedAuthentication.principal

then("파싱된 멤버의 ID는 원래 멤버의 ID와 같아야 한다") {
assertNotNull(extractedMemberId)
assertEquals(member.memberId, extractedMemberId)
extractedMemberId shouldNotBe null
extractedMemberId shouldBe member.memberId.toString()
}
}
}
Expand Down
Loading