diff --git a/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthProcessor.kt b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthProcessor.kt new file mode 100644 index 0000000..0708b6b --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthProcessor.kt @@ -0,0 +1,5 @@ +package org.grida.auth + +interface AuthProcessor { + fun process(code: String): AuthToken +} diff --git a/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthProcessorSelector.kt b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthProcessorSelector.kt new file mode 100644 index 0000000..498d714 --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthProcessorSelector.kt @@ -0,0 +1,16 @@ +package org.grida.auth + +import org.grida.error.GridaException +import org.grida.error.NotSupportedLoginPlatform +import org.springframework.stereotype.Component + +@Component +class AuthProcessorSelector( + private val authProcessors: Map +) { + + fun select(platform: String): AuthProcessor { + return authProcessors["${platform}AuthProcessor"] + ?: throw GridaException(NotSupportedLoginPlatform) + } +} diff --git a/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthToken.kt b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthToken.kt new file mode 100644 index 0000000..ac41a5f --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthToken.kt @@ -0,0 +1,6 @@ +package org.grida.auth + +data class AuthToken( + val accessToken: String, + val refreshToken: String +) diff --git a/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthTokenProvider.kt b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthTokenProvider.kt new file mode 100644 index 0000000..2358210 --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/auth/AuthTokenProvider.kt @@ -0,0 +1,22 @@ +package org.grida.auth + +import io.wwan13.wintersecurity.jwt.TokenGenerator +import org.grida.config.TokenPayload +import org.grida.domain.user.User +import org.springframework.stereotype.Component + +@Component +class AuthTokenProvider( + private val tokenGenerator: TokenGenerator +) { + + fun provide( + user: User + ): AuthToken { + val tokenPayload = TokenPayload(user.id, user.role) + return AuthToken( + accessToken = tokenGenerator.accessToken(tokenPayload), + refreshToken = tokenGenerator.refreshToken(tokenPayload) + ) + } +} diff --git a/grida-core/core-api/src/main/kotlin/org/grida/auth/InternalAuthProcessor.kt b/grida-core/core-api/src/main/kotlin/org/grida/auth/InternalAuthProcessor.kt new file mode 100644 index 0000000..ed21b85 --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/auth/InternalAuthProcessor.kt @@ -0,0 +1,36 @@ +package org.grida.auth + +import org.grida.domain.user.LoginOption +import org.grida.domain.user.LoginPlatform +import org.grida.domain.user.UserService +import org.grida.error.GridaException +import org.grida.error.NotSupportedLoginPlatform +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component + +@Component +class InternalAuthProcessor( + @Value("\${spring.profiles.active}") + private val activeProfile: String, + private val userService: UserService, + private val authTokenProvider: AuthTokenProvider +) : AuthProcessor { + + override fun process(code: String): AuthToken { + validateActiveProfile() + val loginOption = LoginOption(LoginPlatform.ADMIN, code) + val user = userService.read(loginOption.identifier.toLong()) + + return authTokenProvider.provide(user) + } + + private fun validateActiveProfile() { + if (!enableProfiles.contains(activeProfile)) { + throw throw GridaException(NotSupportedLoginPlatform) + } + } + + companion object { + val enableProfiles = listOf("dev", "stag") + } +} diff --git a/grida-core/core-api/src/main/kotlin/org/grida/auth/KakaoAuthProcessor.kt b/grida-core/core-api/src/main/kotlin/org/grida/auth/KakaoAuthProcessor.kt new file mode 100644 index 0000000..391e34c --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/auth/KakaoAuthProcessor.kt @@ -0,0 +1,26 @@ +package org.grida.auth + +import org.grida.domain.user.LoginOption +import org.grida.domain.user.LoginPlatform +import org.grida.domain.user.UserService +import org.grida.user.KakaoUserClient +import org.springframework.stereotype.Component + +@Component +class KakaoAuthProcessor( + private val kakaoAuthClient: KakaoAuthClient, + private val kakaoUserClient: KakaoUserClient, + private val userService: UserService, + private val authTokenProvider: AuthTokenProvider +) : AuthProcessor { + + override fun process(code: String): AuthToken { + val kakaoToken = kakaoAuthClient.provideAuthToken(code) + val kakaoProfile = kakaoUserClient.readUserProfile(kakaoToken.accessToken) + val loginOption = LoginOption(LoginPlatform.KAKAO, kakaoProfile.id) + + val user = userService.readUserByLoginOption(loginOption) + ?: userService.appendAndReturnNormalUser(kakaoProfile.name, loginOption) + return authTokenProvider.provide(user) + } +} diff --git a/grida-core/core-api/src/main/kotlin/org/grida/config/CoreApiSecurityConfig.kt b/grida-core/core-api/src/main/kotlin/org/grida/config/CoreApiSecurityConfig.kt index 8ceaa49..ebad6c1 100644 --- a/grida-core/core-api/src/main/kotlin/org/grida/config/CoreApiSecurityConfig.kt +++ b/grida-core/core-api/src/main/kotlin/org/grida/config/CoreApiSecurityConfig.kt @@ -31,6 +31,10 @@ class CoreApiSecurityConfig( override fun registerAuthPatterns(registry: AuthPatternsRegistry) { registry.apply { + uriPatterns("/api/health") + .httpMethodGet() + .permitAll() + uriPatterns("/api/v1/auth/**") .allHttpMethods() .permitAll() diff --git a/grida-core/core-api/src/main/kotlin/org/grida/error/CoreApiErrorType.kt b/grida-core/core-api/src/main/kotlin/org/grida/error/CoreApiErrorType.kt new file mode 100644 index 0000000..533643c --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/error/CoreApiErrorType.kt @@ -0,0 +1,12 @@ +package org.grida.error + +import org.grida.http.BAD_REQUEST + +sealed interface CoreApiErrorType : ErrorType + +data object NotSupportedLoginPlatform : CoreApiErrorType { + override val httpStatusCode: Int = BAD_REQUEST + override val errorCode: String = "AUTH_PLATFORM_400_1" + override val message: String = "지원하지 않는 로그인 플랫폼 입니다." + override val logLevel: LogLevel = INFO +} diff --git a/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/AuthController.kt b/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/AuthController.kt index 260d28f..6aa558e 100644 --- a/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/AuthController.kt +++ b/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/AuthController.kt @@ -1,14 +1,8 @@ package org.grida.presentation.v1.auth -import io.wwan13.wintersecurity.jwt.TokenGenerator import org.grida.api.ApiResponse -import org.grida.auth.KakaoAuthClient -import org.grida.config.TokenPayload -import org.grida.domain.user.LoginOption -import org.grida.domain.user.LoginPlatform -import org.grida.domain.user.UserService +import org.grida.auth.AuthProcessorSelector import org.grida.presentation.v1.auth.dto.LoginResponse -import org.grida.user.KakaoUserClient import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam @@ -17,28 +11,17 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api/v1/auth") class AuthController( - private val userService: UserService, - private val tokenGenerator: TokenGenerator, - private val kakaoAuthClient: KakaoAuthClient, - private val kakaoUserClient: KakaoUserClient + private val authProcessorSelector: AuthProcessorSelector ) { - @GetMapping("/kakao") - fun kakaoLogin( - @RequestParam("code") kakaoAuthCode: String + @GetMapping + fun provideAuthToken( + @RequestParam("platform") platform: String, + @RequestParam("code") code: String ): ApiResponse { - val kakaoToken = kakaoAuthClient.provideAuthToken(kakaoAuthCode) - val kakaoProfile = kakaoUserClient.readUserProfile(kakaoToken.accessToken) - - val loginOption = LoginOption(LoginPlatform.KAKAO, kakaoProfile.id) - val user = userService.readUserByLoginOption(loginOption) - ?: userService.appendAndReturnNormalUser(kakaoProfile.name, loginOption) - - val tokenPayload = TokenPayload(user.id, user.role) - val response = LoginResponse( - accessToken = tokenGenerator.accessToken(tokenPayload), - refreshToken = tokenGenerator.refreshToken(tokenPayload) - ) + val authProcessor = authProcessorSelector.select(platform) + val authToken = authProcessor.process(code) + val response = LoginResponse.from(authToken) return ApiResponse.success(response) } } diff --git a/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginRequest.kt b/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginRequest.kt new file mode 100644 index 0000000..61d740c --- /dev/null +++ b/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginRequest.kt @@ -0,0 +1,5 @@ +package org.grida.presentation.v1.auth.dto + +data class LoginRequest( + val code: String +) diff --git a/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginResponse.kt b/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginResponse.kt index 80d357b..47dfa56 100644 --- a/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginResponse.kt +++ b/grida-core/core-api/src/main/kotlin/org/grida/presentation/v1/auth/dto/LoginResponse.kt @@ -1,6 +1,14 @@ package org.grida.presentation.v1.auth.dto +import org.grida.auth.AuthToken + data class LoginResponse( val accessToken: String, val refreshToken: String, -) +) { + companion object { + fun from(authToken: AuthToken): LoginResponse { + return LoginResponse(authToken.accessToken, authToken.refreshToken) + } + } +} diff --git a/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/LoginPlatform.kt b/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/LoginPlatform.kt index 246309c..e2efa92 100644 --- a/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/LoginPlatform.kt +++ b/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/LoginPlatform.kt @@ -5,7 +5,6 @@ import org.grida.domain.base.ValueEnum enum class LoginPlatform( override val value: String ) : ValueEnum { - KAKAO("카카오"), - GOOGLE("구글"), - GITHUB("깃허브"); + KAKAO("kakao"), + ADMIN("admin") } diff --git a/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/UserService.kt b/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/UserService.kt index 786b8cc..4590ec1 100644 --- a/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/UserService.kt +++ b/grida-core/core-domain/src/main/kotlin/org/grida/domain/user/UserService.kt @@ -30,6 +30,10 @@ class UserService( return userAppender.appendAndReturnUser(user) } + fun read(id: Long): User { + return userReader.read(id) + } + fun readUserByLoginOption( loginOption: LoginOption ): User? {