Skip to content

Commit 7d1822a

Browse files
authored
Implement Apple OAuth login using OIDC ID token (#11)
1 parent cf5af31 commit 7d1822a

File tree

11 files changed

+81
-14
lines changed

11 files changed

+81
-14
lines changed

src/main/kotlin/com/moa/common/exception/ErrorCode.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,5 @@ enum class ErrorCode(
1818
REQUIRED_TERMS_MUST_BE_AGREED("REQUIRED_TERMS_MUST_BE_AGREED", "필수 약관은 동의해야 합니다"),
1919

2020
INVALID_ID_TOKEN("INVALID_ID_TOKEN", "유효하지 않은 ID 토큰입니다"),
21-
INVALID_PROVIDER("INVALID_PROVIDER", "유효하지 않는 로그인 방식입니다."),
2221
EXPIRED_TOKEN("EXPIRED_TOKEN", "토큰이 만료되었습니다"),
2322
}

src/main/kotlin/com/moa/common/oidc/OidcIdTokenValidator.kt

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,27 @@ class OidcIdTokenValidator(
1515
private val objectMapper: ObjectMapper
1616
) {
1717
fun validate(provider: ProviderType, idToken: String): OidcUserInfo {
18-
val providerProperties = getProviderProperties(provider)
19-
20-
return validateToken(idToken, providerProperties, provider)
18+
return validateToken(idToken, provider)
2119
}
2220

23-
private fun getProviderProperties(provider: ProviderType): OidcProviderConfig.ProviderProperties {
21+
private fun getJwksConfig(provider: ProviderType): Pair<String, Long> {
2422
return when (provider) {
25-
ProviderType.KAKAO -> config.kakao
26-
else -> throw UnauthorizedException(ErrorCode.INVALID_PROVIDER)
23+
ProviderType.KAKAO -> config.kakao.jwksUri to config.kakao.cacheTtlSeconds
24+
ProviderType.APPLE -> config.apple.jwksUri to config.apple.cacheTtlSeconds
2725
}
2826
}
2927

3028
private fun validateToken(
3129
idToken: String,
32-
providerConfig: OidcProviderConfig.ProviderProperties,
3330
provider: ProviderType
3431
): OidcUserInfo {
32+
val (jwksUri, cacheTtlSeconds) = getJwksConfig(provider)
3533
val kid = extractKid(idToken)
3634

3735
val publicKey = publicKeyCache.getPublicKey(
38-
jwksUri = providerConfig.jwksUri,
36+
jwksUri = jwksUri,
3937
kid = kid,
40-
ttlSeconds = providerConfig.cacheTtlSeconds,
38+
ttlSeconds = cacheTtlSeconds,
4139
)
4240

4341
val claims = try {

src/main/kotlin/com/moa/common/oidc/OidcProviderConfig.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ import org.springframework.boot.context.properties.ConfigurationProperties
44

55
@ConfigurationProperties(prefix = "oidc")
66
data class OidcProviderConfig(
7-
val kakao: ProviderProperties,
7+
val kakao: KakaoProviderProperties,
8+
val apple: AppleProviderProperties,
89
) {
9-
data class ProviderProperties(
10+
data class KakaoProviderProperties(
11+
val jwksUri: String,
12+
val cacheTtlSeconds: Long = 3600,
13+
)
14+
15+
data class AppleProviderProperties(
1016
val jwksUri: String,
1117
val cacheTtlSeconds: Long = 3600,
1218
)

src/main/kotlin/com/moa/controller/AuthController.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.moa.controller
22

33
import com.moa.common.response.ApiResponse
44
import com.moa.service.AuthService
5+
import com.moa.service.dto.AppleSignInUpRequest
6+
import com.moa.service.dto.AppleSignInUpResponse
57
import com.moa.service.dto.KaKaoSignInUpRequest
68
import com.moa.service.dto.KakaoSignInUpResponse
79
import jakarta.validation.Valid
@@ -19,4 +21,9 @@ class AuthController(
1921
fun kakaoSignInUp(@RequestBody @Valid kaKaoSignInUpRequest: KaKaoSignInUpRequest): ResponseEntity<ApiResponse<KakaoSignInUpResponse>> {
2022
return ResponseEntity.ok(ApiResponse.success(authService.kakaoSignInUp(kaKaoSignInUpRequest)))
2123
}
24+
25+
@PostMapping("/api/v1/auth/apple")
26+
fun appleSignInUp(@RequestBody appleSignInUpRequest: AppleSignInUpRequest): ResponseEntity<ApiResponse<AppleSignInUpResponse>> {
27+
return ResponseEntity.ok(ApiResponse.success(authService.appleSignInUp(appleSignInUpRequest)))
28+
}
2229
}

src/main/kotlin/com/moa/service/AuthService.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import com.moa.common.oidc.OidcIdTokenValidator
55
import com.moa.entity.Member
66
import com.moa.entity.ProviderType
77
import com.moa.repository.MemberRepository
8+
import com.moa.service.dto.AppleSignInUpRequest
9+
import com.moa.service.dto.AppleSignInUpResponse
810
import com.moa.service.dto.KaKaoSignInUpRequest
911
import com.moa.service.dto.KakaoSignInUpResponse
1012
import org.springframework.stereotype.Service
@@ -48,4 +50,36 @@ class AuthService(
4850
registerToken,
4951
)
5052
}
53+
54+
@Transactional
55+
fun appleSignInUp(request: AppleSignInUpRequest): AppleSignInUpResponse {
56+
val userInfo = oidcIdTokenValidator.validate(ProviderType.APPLE, request.idToken)
57+
58+
val member = memberRepository.findByProviderAndProviderSubject(
59+
provider = userInfo.provider,
60+
providerSubject = userInfo.subject,
61+
)
62+
63+
member?.let {
64+
return AppleSignInUpResponse(
65+
jwtTokenProvider.createAccessToken(member.id)
66+
)
67+
}
68+
69+
val registeredMember = memberRepository.save(
70+
Member(
71+
provider = ProviderType.APPLE,
72+
providerSubject = userInfo.subject,
73+
profile = null,
74+
)
75+
)
76+
77+
val registerToken = jwtTokenProvider.createAccessToken(
78+
registeredMember.id
79+
)
80+
81+
return AppleSignInUpResponse(
82+
registerToken,
83+
)
84+
}
5185
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.moa.service.dto
2+
3+
import jakarta.validation.constraints.NotBlank
4+
5+
data class AppleSignInUpRequest(
6+
@field:NotBlank
7+
val idToken: String,
8+
val fcmDeviceToken: String? = null,
9+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.moa.service.dto
2+
3+
data class AppleSignInUpResponse(
4+
val accessToken: String? = null,
5+
)

src/main/resources/application-local.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ oidc:
3030
kakao:
3131
jwks-uri: ${secret.oauth.kakao.jwks-uri}
3232
cache-ttl-seconds: ${secret.oauth.cache-ttl-seconds}
33+
apple:
34+
jwks-uri: ${secret.oauth.apple.jwks-uri}
35+
cache-ttl-seconds: ${secret.oauth.cache-ttl-seconds}

src/main/resources/application-prod.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ spring:
88
jpa:
99
database-platform: org.hibernate.dialect.MySQLDialect
1010
hibernate:
11-
ddl-auto: update
11+
ddl-auto: create-drop
1212
properties:
1313
hibernate:
1414
format_sql: true
@@ -22,3 +22,6 @@ oidc:
2222
kakao:
2323
jwks-uri: ${secret.oauth.kakao.jwks-uri}
2424
cache-ttl-seconds: ${secret.oauth.cache-ttl-seconds}
25+
apple:
26+
jwks-uri: ${secret.oauth.apple.jwks-uri}
27+
cache-ttl-seconds: ${secret.oauth.cache-ttl-seconds}

src/main/resources/moa-secret

0 commit comments

Comments
 (0)