From cb78fad7cdf3c5d8cd99a80ff4ca6098d36ecef0 Mon Sep 17 00:00:00 2001 From: SeonghaeJo <87258768+SeonghaeJo@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:30:04 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=A0=ED=94=8C=20=EC=97=B0=EB=8F=99/?= =?UTF-8?q?=ED=95=B4=EC=A0=9C=20(#310)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/main/kotlin/handler/UserHandler.kt | 13 +++++++ api/src/main/kotlin/router/MainRouter.kt | 2 + api/src/main/kotlin/router/docs/UserDocs.kt | 39 +++++++++++++++++++ .../kotlin/users/dto/SocialLoginRequest.kt | 2 +- .../kotlin/users/repository/UserRepository.kt | 2 + .../main/kotlin/users/service/UserService.kt | 13 ++++++- 6 files changed, 69 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/handler/UserHandler.kt b/api/src/main/kotlin/handler/UserHandler.kt index 45304c25..9f95d62d 100644 --- a/api/src/main/kotlin/handler/UserHandler.kt +++ b/api/src/main/kotlin/handler/UserHandler.kt @@ -123,6 +123,13 @@ class UserHandler( userService.attachSocial(user, socialLoginRequest, AuthProvider.KAKAO) } + suspend fun attachApple(req: ServerRequest): ServerResponse = + handle(req) { + val user = req.getContext().user!! + val socialLoginRequest: SocialLoginRequest = req.awaitBody() + userService.attachSocial(user, socialLoginRequest, AuthProvider.APPLE) + } + suspend fun detachFacebook(req: ServerRequest): ServerResponse = handle(req) { val user = req.getContext().user!! @@ -141,6 +148,12 @@ class UserHandler( userService.detachSocial(user, AuthProvider.KAKAO) } + suspend fun detachApple(req: ServerRequest): ServerResponse = + handle(req) { + val user = req.getContext().user!! + userService.detachSocial(user, AuthProvider.APPLE) + } + suspend fun checkAuthProviders(req: ServerRequest): ServerResponse = handle(req) { val user = req.getContext().user!! diff --git a/api/src/main/kotlin/router/MainRouter.kt b/api/src/main/kotlin/router/MainRouter.kt index e9bd14d5..c69606fe 100644 --- a/api/src/main/kotlin/router/MainRouter.kt +++ b/api/src/main/kotlin/router/MainRouter.kt @@ -114,9 +114,11 @@ class MainRouter( POST("/facebook", userHandler::attachFacebook) POST("/google", userHandler::attachGoogle) POST("/kakao", userHandler::attachKakao) + POST("/apple", userHandler::attachApple) DELETE("/facebook", userHandler::detachFacebook) DELETE("/google", userHandler::detachGoogle) DELETE("/kakao", userHandler::detachKakao) + DELETE("/apple", userHandler::detachApple) } "/users".nest { GET("/me", userHandler::getUserMe) diff --git a/api/src/main/kotlin/router/docs/UserDocs.kt b/api/src/main/kotlin/router/docs/UserDocs.kt index 59801e29..b5b28cf0 100644 --- a/api/src/main/kotlin/router/docs/UserDocs.kt +++ b/api/src/main/kotlin/router/docs/UserDocs.kt @@ -319,6 +319,30 @@ import org.springframework.web.bind.annotation.RequestMethod ], ), ), + RouterOperation( + path = "/v1/user/apple", + method = [RequestMethod.POST], + produces = [MediaType.APPLICATION_JSON_VALUE], + operation = + Operation( + operationId = "attachApple", + requestBody = + RequestBody( + content = [ + Content( + schema = Schema(implementation = SocialLoginRequest::class), + mediaType = MediaType.APPLICATION_JSON_VALUE, + ), + ], + ), + responses = [ + ApiResponse( + responseCode = "200", + content = [Content(schema = Schema(implementation = TokenResponse::class))], + ), + ], + ), + ), RouterOperation( path = "/v1/user/facebook", method = [RequestMethod.DELETE], @@ -364,5 +388,20 @@ import org.springframework.web.bind.annotation.RequestMethod ], ), ), + RouterOperation( + path = "/v1/user/apple", + method = [RequestMethod.DELETE], + produces = [MediaType.APPLICATION_JSON_VALUE], + operation = + Operation( + operationId = "detachApple", + responses = [ + ApiResponse( + responseCode = "200", + content = [Content(schema = Schema(implementation = TokenResponse::class))], + ), + ], + ), + ), ) annotation class UserDocs diff --git a/core/src/main/kotlin/users/dto/SocialLoginRequest.kt b/core/src/main/kotlin/users/dto/SocialLoginRequest.kt index 8a4d2606..1f15f3b1 100644 --- a/core/src/main/kotlin/users/dto/SocialLoginRequest.kt +++ b/core/src/main/kotlin/users/dto/SocialLoginRequest.kt @@ -3,6 +3,6 @@ package com.wafflestudio.snu4t.users.dto import com.fasterxml.jackson.annotation.JsonAlias data class SocialLoginRequest( - @JsonAlias("fb_token") + @JsonAlias("fb_token", "apple_token") val token: String, ) diff --git a/core/src/main/kotlin/users/repository/UserRepository.kt b/core/src/main/kotlin/users/repository/UserRepository.kt index 5f71cecb..2d3c0509 100644 --- a/core/src/main/kotlin/users/repository/UserRepository.kt +++ b/core/src/main/kotlin/users/repository/UserRepository.kt @@ -40,6 +40,8 @@ interface UserRepository : CoroutineCrudRepository { suspend fun existsByCredentialKakaoSubAndActiveTrue(kakaoSub: String): Boolean + suspend fun existsByCredentialAppleSubAndActiveTrue(appleSub: String): Boolean + fun findAllByNicknameStartingWith(nickname: String): Flow suspend fun findByCredentialAppleTransferSubAndActiveTrue(appleTransferSub: String): User? diff --git a/core/src/main/kotlin/users/service/UserService.kt b/core/src/main/kotlin/users/service/UserService.kt index 4caf4486..6d603f61 100644 --- a/core/src/main/kotlin/users/service/UserService.kt +++ b/core/src/main/kotlin/users/service/UserService.kt @@ -485,7 +485,18 @@ class UserServiceImpl( kakaoEmail = kakaoCredential.kakaoEmail } } - AuthProvider.APPLE -> throw IllegalStateException("Apple login is not supported") + AuthProvider.APPLE -> { + if (user.credential.appleSub != null) throw AlreadySocialAccountException + if (userRepository.existsByCredentialAppleSubAndActiveTrue(oauth2UserResponse.socialId)) { + throw DuplicateSocialAccountException + } + val appleCredential = authService.buildAppleCredential(oauth2UserResponse) + user.credential.apply { + appleSub = appleCredential.appleSub + appleEmail = appleCredential.appleEmail + appleTransferSub = appleCredential.appleTransferSub + } + } AuthProvider.LOCAL -> throw IllegalStateException("Cannot attach local account") }