Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.oksusu.susu.domain.user.domain.UserWithdraw
import com.oksusu.susu.domain.user.domain.vo.AccountRole
import com.oksusu.susu.domain.user.domain.vo.UserStatusAssignmentType
import com.oksusu.susu.domain.user.domain.vo.UserStatusTypeInfo
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
Expand All @@ -47,6 +48,8 @@ class AuthFacade(
private val txTemplates: TransactionTemplates,
private val userStatusTypeService: UserStatusTypeService,
) {
val logger = KotlinLogging.logger { }

fun resolveAuthUser(token: Mono<AuthUserToken>): Mono<Any> {
return jwtTokenService.verifyTokenMono(token)
.map { payload ->
Expand All @@ -70,7 +73,7 @@ class AuthFacade(
}

private fun raiseIf(userStatus: UserStatus): UserStatusTypeModel {
val userStatusType = userStatusTypeService.getStatus(userStatus.id)
val userStatusType = userStatusTypeService.getStatus(userStatus.accountStatusId)

/** status type에 따른 처리 */
when (userStatusType.statusTypeInfo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.oksusu.susu.api.auth.model.AuthUser
import com.oksusu.susu.api.auth.model.TokenDto
import com.oksusu.susu.api.auth.model.request.OAuthLoginRequest
import com.oksusu.susu.api.auth.model.request.OAuthRegisterRequest
import com.oksusu.susu.api.auth.model.request.OidcLoginRequest
import com.oksusu.susu.api.auth.model.response.AbleRegisterResponse
import com.oksusu.susu.api.auth.model.response.UserOAuthInfoResponse
import com.oksusu.susu.api.event.model.CreateUserDeviceEvent
Expand All @@ -28,6 +29,7 @@ import com.oksusu.susu.domain.user.domain.UserStatus
import com.oksusu.susu.domain.user.domain.UserStatusHistory
import com.oksusu.susu.domain.user.domain.vo.AccountRole
import com.oksusu.susu.domain.user.domain.vo.OAuthProvider
import com.oksusu.susu.domain.user.domain.vo.OauthInfo
import com.oksusu.susu.domain.user.domain.vo.UserStatusAssignmentType
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.Dispatchers
Expand All @@ -54,17 +56,33 @@ class OAuthFacade(
) {
val logger = KotlinLogging.logger {}

/** 회원가입 가능 여부 체크. */
suspend fun checkRegisterValid(provider: OAuthProvider, accessToken: String): AbleRegisterResponse {
/** OAuth 회원가입 가능 여부 체크. */
suspend fun checkRegisterValidWithOAuth(provider: OAuthProvider, accessToken: String): AbleRegisterResponse {
val oauthInfo = oAuthService.getOAuthInfo(provider, accessToken)

return checkRegisterValid(oauthInfo)
}

/** Oidc 회원가입 가능 여부 체크. */
suspend fun checkRegisterValidWithOidc(provider: OAuthProvider, idToken: String): AbleRegisterResponse {
val oauthInfo = oAuthService.getOAuthInfoWithOidc(provider, idToken)

return checkRegisterValid(oauthInfo)
}

/**
* 회원가입 가능 여부 체크 공통 로직
*/
private suspend fun checkRegisterValid(oauthInfo: OauthInfo): AbleRegisterResponse {

val isExistUser = userService.existsByOAuthInfo(oauthInfo)

return AbleRegisterResponse(!isExistUser)

}

/** 회원가입 */
suspend fun register(
/** OAuth 회원가입 */
suspend fun registerWithOAuth(
provider: OAuthProvider,
accessToken: String,
request: OAuthRegisterRequest,
Expand All @@ -74,6 +92,31 @@ class OAuthFacade(

val oauthInfo = oAuthService.getOAuthInfo(provider, accessToken)

return register(oauthInfo, request, deviceContext)
}

/** Oidc 회원가입 */
suspend fun registerWithOidc(
provider: OAuthProvider,
idToken: String,
request: OAuthRegisterRequest,
deviceContext: UserDeviceContext,
): TokenDto {
authValidateService.validateRegisterRequest(request)

val oauthInfo = oAuthService.getOAuthInfoWithOidc(provider, idToken)

return register(oauthInfo, request, deviceContext)
}

/**
* 회원가입 공통 로직
*/
private suspend fun register(
oauthInfo: OauthInfo,
request: OAuthRegisterRequest,
deviceContext: UserDeviceContext,
): TokenDto {
coroutineScope {
val validateNotRegistered = async(Dispatchers.IO) {
userService.validateNotRegistered(
Expand Down Expand Up @@ -149,13 +192,32 @@ class OAuthFacade(
return generateTokenDto(user.id)
}

/** 로그인 */
suspend fun login(
/** OAuth 로그인 */
suspend fun loginWithOAuth(
provider: OAuthProvider,
request: OAuthLoginRequest,
deviceContext: UserDeviceContext,
): TokenDto {
val oauthInfo = oAuthService.getOAuthInfo(provider, request.accessToken)

return login(oauthInfo, deviceContext)
}

/** Oidc 로그인 */
suspend fun loginWithOidc(
provider: OAuthProvider,
request: OidcLoginRequest,
deviceContext: UserDeviceContext,
): TokenDto {
val oauthInfo = oAuthService.getOAuthInfoWithOidc(provider, request.idToken)

return login(oauthInfo, deviceContext)
}

/**
* 회원가입 공통 로직
*/
private suspend fun login(oauthInfo: OauthInfo, deviceContext: UserDeviceContext): TokenDto {
val user = userService.findByOAuthInfoOrThrow(oauthInfo)

val userDevice = UserDevice(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.oksusu.susu.api.auth.application.oauth.GoogleOAuthService
import com.oksusu.susu.api.auth.application.oauth.KakaoOAuthService
import com.oksusu.susu.api.auth.model.response.OAuthLoginLinkResponse
import com.oksusu.susu.api.auth.model.response.OAuthTokenResponse
import com.oksusu.susu.common.exception.ErrorCode
import com.oksusu.susu.common.exception.InvalidRequestException
import com.oksusu.susu.domain.user.domain.vo.OAuthProvider
import com.oksusu.susu.domain.user.domain.vo.OauthInfo
import io.github.oshai.kotlinlogging.KotlinLogging
Expand Down Expand Up @@ -51,6 +53,17 @@ class OAuthService(
}.oauthInfo
}

/** oauth info 가져오기 */
suspend fun getOAuthInfoWithOidc(
provider: OAuthProvider,
idToken: String,
): OauthInfo {
return when (provider) {
OAuthProvider.GOOGLE -> googleOAuthService.getOAuthInfoWithOidc(idToken)
else -> throw InvalidRequestException(ErrorCode.INVALID_OAUTH_PROVIDER)
}.oauthInfo
}

/** oauth 유저 회원 탈퇴하기 */
suspend fun withdraw(oauthInfo: OauthInfo, code: String?, accessToken: String?) {
when (oauthInfo.oAuthProvider) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.oksusu.susu.api.auth.application.oauth

import com.oksusu.susu.api.auth.model.OAuthUserInfoDto
import com.oksusu.susu.api.auth.model.OidcDecodePayload
import com.oksusu.susu.api.auth.model.response.OAuthLoginLinkResponse
import com.oksusu.susu.api.auth.model.response.OAuthTokenResponse
import com.oksusu.susu.api.config.OAuthSecretConfig
import com.oksusu.susu.client.config.OAuthUrlConfig
import com.oksusu.susu.client.oauth.google.GoogleClient
import com.oksusu.susu.common.extension.withMDCContext
import com.oksusu.susu.domain.user.domain.vo.OAuthProvider
import com.oksusu.susu.domain.user.domain.vo.OauthInfo
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.Dispatchers
import org.springframework.beans.factory.annotation.Value
Expand All @@ -17,6 +20,7 @@ class GoogleOAuthService(
val googleOAuthUrlConfig: OAuthUrlConfig.GoogleOAuthUrlConfig,
val googleOAuthSecretConfig: OAuthSecretConfig.GoogleOAuthSecretConfig,
val googleClient: GoogleClient,
val oidcService: OidcService,
@Value("\${server.domain-name}")
private val domainName: String,
) {
Expand Down Expand Up @@ -67,7 +71,10 @@ class GoogleOAuthService(
clientId = googleOAuthSecretConfig.clientId,
clientSecret = googleOAuthSecretConfig.clientSecret
)
}.run { OAuthTokenResponse.fromGoogle(this) }
}.run {
logger.info { this.idToken }
OAuthTokenResponse.fromGoogle(this)
}
}

/** 유저 정보를 가져옵니다. */
Expand All @@ -77,10 +84,36 @@ class GoogleOAuthService(
}.run { OAuthUserInfoDto.fromGoogle(this) }
}


/** 유저 정보를 가져옵니다. */
suspend fun getOAuthInfoWithOidc(idToken: String): OAuthUserInfoDto {
return withMDCContext(Dispatchers.IO) {
getOIDCDecodePayload(idToken)
}.run { OAuthUserInfoDto(
oauthInfo = OauthInfo(
oAuthProvider = OAuthProvider.GOOGLE,
oAuthId = this.sub
)
) }
}

/** 회원 탈퇴합니다 */
suspend fun withdraw(accessToken: String) {
withMDCContext(Dispatchers.IO) {
googleClient.withdraw(accessToken = accessToken)
}
}

/**
* oidc decode
*/
private suspend fun getOIDCDecodePayload(token: String): OidcDecodePayload {
val oidcPublicKeysResponse = oidcService.getOidcPublicKeys(OAuthProvider.GOOGLE)
return oidcService.getPayloadFromIdToken(
token,
googleOAuthUrlConfig.accountGoogleUrl,
googleOAuthSecretConfig.clientId,
oidcPublicKeysResponse
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import com.oksusu.susu.cache.model.OidcPublicKeysCacheModel
import com.oksusu.susu.cache.model.vo.OidcPublicKeyCacheModel
import com.oksusu.susu.cache.service.CacheService
import com.oksusu.susu.client.oauth.apple.AppleClient
import com.oksusu.susu.client.oauth.google.GoogleClient
import com.oksusu.susu.client.oauth.oidc.model.OidcPublicKeyModel
import com.oksusu.susu.client.oauth.oidc.model.OidcPublicKeysResponse
import com.oksusu.susu.common.exception.ErrorCode
import com.oksusu.susu.common.exception.InvalidRequestException
import com.oksusu.susu.common.exception.InvalidTokenException
import com.oksusu.susu.common.extension.withMDCContext
import com.oksusu.susu.domain.user.domain.vo.OAuthProvider
Expand All @@ -31,6 +33,7 @@ import java.util.Date
class OidcService(
private val cacheService: CacheService,
private val appleClient: AppleClient,
private val googleClient: GoogleClient,
private val eventPublisher: ApplicationEventPublisher,
) {
private val logger = KotlinLogging.logger { }
Expand All @@ -39,10 +42,11 @@ class OidcService(
withMDCContext(Dispatchers.IO) {
when (provider) {
OAuthProvider.APPLE -> cacheService.getOrNull(Cache.getAppleOidcPublicKeyCache())
else -> null
OAuthProvider.GOOGLE -> cacheService.getOrNull(Cache.getGoogleOidcPublicKeyCache())
else -> throw InvalidRequestException(ErrorCode.INVALID_OAUTH_PROVIDER)
}
}?.run {
logger.debug { "apple oidc public key cache hit" }
logger.debug { "oidc public key cache hit" }

val models = this.keys.map { key ->
OidcPublicKeyModel(
Expand All @@ -58,7 +62,11 @@ class OidcService(
}

return withMDCContext(Dispatchers.IO) {
appleClient.getOidcPublicKeys()
when (provider) {
OAuthProvider.APPLE -> appleClient.getOidcPublicKeys()
OAuthProvider.GOOGLE -> googleClient.getOidcPublicKeys()
else -> throw InvalidRequestException(ErrorCode.INVALID_OAUTH_PROVIDER)
}
}.run {
val cachekeys = this.keys.map { key ->
OidcPublicKeyCacheModel(
Expand Down Expand Up @@ -92,11 +100,13 @@ class OidcService(

verifyToken(jwt, iss, aud)

logger.info { jwt.claims }

return OidcDecodePayload(
iss = jwt.getClaim("iss").asString(),
aud = jwt.getClaim("aud").asString(),
sub = jwt.getClaim("sub").asString(),
email = jwt.getClaim("email").asString()
email = jwt.getClaim("email")?.asString() ?: ""
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface AdminUser {
fun isNotAuthorThrow(uid: Long)
}

class AdminUserImpl(
data class AdminUserImpl(
override val uid: Long,
) : AdminUser {
override fun isAuthor(uid: Long): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.oksusu.susu.domain.user.domain.vo.OAuthProvider
import com.oksusu.susu.domain.user.domain.vo.OauthInfo

/** oauth 정보 dto */
class OAuthUserInfoDto(
data class OAuthUserInfoDto(
/** oauth 정보 */
val oauthInfo: OauthInfo,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.oksusu.susu.api.auth.model

class OidcDecodePayload(
data class OidcDecodePayload(
/** issuer ex https://kauth.kakao.com */
val iss: String,
/** client id */
val aud: String,
/** oauth provider account unique id */
val sub: String,
val email: String,
// val profile:
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.oksusu.susu.api.auth.model
import java.time.LocalDateTime

/** 토큰 정보 dto */
class TokenDto(
data class TokenDto(
/** access token */
val accessToken: String,
/** access token 유효기간 */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.oksusu.susu.api.auth.model.request

class OAuthLoginRequest(
data class OAuthLoginRequest(
/** oauth access token */
val accessToken: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.oksusu.susu.api.auth.model.request
import com.oksusu.susu.domain.user.domain.vo.Gender
import java.time.LocalDate

class OAuthRegisterRequest(
data class OAuthRegisterRequest(
/** 유저 이름 */
val name: String,
/** 동의 약관 id */
Expand All @@ -14,6 +14,6 @@ class OAuthRegisterRequest(
val birth: Int?,
) {
fun getBirth(): LocalDate? {
return this.birth ?.let { LocalDate.of(it, 1, 1) }
return this.birth?.let { LocalDate.of(it, 1, 1) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.oksusu.susu.api.auth.model.request

data class OidcLoginRequest(
/** oauth idToken */
val idToken: String,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.oksusu.susu.api.auth.model.response

class AbleRegisterResponse(
data class AbleRegisterResponse(
/** 회원가입 가능 여부 / 가능 : 1, 불가능 : 0 */
val canRegister: Boolean,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.oksusu.susu.api.auth.model.response

class OAuthLoginLinkResponse(
data class OAuthLoginLinkResponse(
/** oauth 가입 url */
val link: String,
)
Loading