Skip to content

Conversation

@Darren4641
Copy link
Contributor

@Darren4641 Darren4641 commented Dec 29, 2025

Description

  • 카카오 OpenID Connect(OIDC) 기반 소셜 로그인 및 회원가입 기능을 구현
  • 앱에서 카카오 SDK로 획득한 ID Token을 서버에서 검증하고, 사용자 정보를 추출하여 회원가입을 처리

1. 카카오 OIDC 인증 API 구현

  • 엔드포인트: POST /api/auth/kakao/oidc
  • 앱에서 전달받은 ID Token을 검증하고 회원가입처리 (현재는 테스트용으로 REST ID로 검증 중, 개발서버 검증 끝나면 APP_ID로 변경 예정)
  • 신규 사용자는 TB_USERS 저장, 기존 사용자는 Dynamic Update

2. OIDC ID Token 검증 로직 (KakaoOauthHelper)

  • 카카오 공식 가이드를 따라 7단계 검증 프로세스를 구현했습니다:
  • Step 1: ID Token 헤더에서 kid 추출 (Base64 디코딩)
  • Step 2: kid로 JWKS에서 공개키 조회
  • Step 3: iss 값 검증 (https://kauth.kakao.com)
  • Step 4: aud 값 검증 (앱 키 일치 여부)
  • Step 5: exp 값 검증 (토큰 만료 시간)
  • Step 6: nonce 검증 (선택사항, 현재 미구현)
  • Step 7: RSA 공개키로 서명 검증

주요 구현 내용:

  • JWT 라이브러리(io.jsonwebtoken)를 사용한 토큰 파싱 및 검증
  • JWK의 modulus(n)와 exponent(e)를 RSA 공개키로 변환
  • 검증 실패 시 적절한 예외 처리 (EXPIRED_TOKEN_ERROR, INVALID_TOKEN_ERROR)

3. 계층별 구조

API Layer (AuthController)

  • POST /api/auth/kakao/oidc: 카카오 OIDC 회원가입/로그인
  • GET /api/auth/test/kakao/redirect: 테스트용 ID Token 발급 엔드포인트 (Swagger Hidden)

Application Layer (KakaoAuthUseCase)

  fun execute(command: RegisterKakaoUserCommand): GetKakaoRegisterResult {
      // 1. 카카오 공개키 조회
      val publicKeys = kakaoOauthClient.getOIDCPublicKey()

      // 2. ID Token 검증 및 OAuth 정보 추출
      val oauthInfo = kakaoOauthHelper.getOauthInfoByIdToken(idToken, publicKeys)

      // 3. 사용자 조회 또는 생성
      val user = userRepositoryPort.findByOid(...) ?: User(...)

      // 4. 저장 및 결과 반환
      userRepositoryPort.save(user)
      return GetKakaoRegisterResult(...)
  }

Infrastructure Layer

  • KakaoOauthClient: 카카오 JWKS 엔드포인트 호출
  • KakaoOauthHelper: ID Token 검증 및 Claims 추출
  • OauthProperties: 카카오 OIDC 설정 관리 (issuer, clientId, jwksUri 등)
  • request/response 디렉토리를 생성해 OIDC 관련 DTO 클래스 정의

API Usage Example

Request

  POST /api/auth/kakao/oidc
  Content-Type: application/json

  {
    "idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
  }

Response

  {
    "code": "D-0",
    "message": "OK",
    "data": {
      "oid": 1234567890,
      "providerType": "KAKAO"
    }
  }

References

특이사항

OIDC 인증을 하기위해 access_token이 아닌 id_token을 이용하여 인증 진행, access_token을 client와 주고받으면, 탈취의 위험이 생김,

이때 id_token으로만 인증을 할 경우 사용자의 email을 가져올 수 없음. (biz 앱 신청을 해야 가져올 수 있음) 하지만 우리 서비스에서는 email이 별도 필요하지 않기때문에 (oid, providerType) 2개로 유저를 구분하도록 진행

email 혹은 name은 null값이 될 수 있기때문에 ? 키워드 추가

Darren4641 and others added 17 commits December 11, 2025 23:14
# Conflicts:
#	src/main/kotlin/com/yapp2app/auth/api/controller/AuthController.kt
#	src/main/kotlin/com/yapp2app/auth/infra/security/config/DocumentSecurityConfig.kt
#	src/main/kotlin/com/yapp2app/auth/infra/security/properties/AppProperties.kt
#	src/main/kotlin/com/yapp2app/common/config/JasyptConfig.kt
#	src/main/resources/application-local.yaml
#	src/main/resources/application-staging.yaml
#	src/main/resources/application.yaml
#	src/test/kotlin/com/yapp2app/JasyptTest.kt
# Conflicts:
#	src/main/kotlin/com/yapp2app/auth/api/dto/AuthDto.kt
#	src/main/kotlin/com/yapp2app/user/infra/persist/jpa/UserRepository.kt
@Darren4641 Darren4641 requested a review from koosco December 29, 2025 06:57
@Darren4641 Darren4641 self-assigned this Dec 29, 2025
@github-actions
Copy link

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

Code Format Check ❌ FAILED

Spotless Check: failure

⚠️ Code formatting issues detected!

Please run the following command to fix formatting issues:

./gradlew spotlessApply

Then commit the changes and push again.


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

Code Format Check ❌ FAILED

Spotless Check: failure

⚠️ Code formatting issues detected!

Please run the following command to fix formatting issues:

./gradlew spotlessApply

Then commit the changes and push again.


Pushed by: @Darren4641, Action: pull_request

@Darren4641 Darren4641 linked an issue Dec 29, 2025 that may be closed by this pull request
@github-actions
Copy link

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

Copy link
Member

@koosco koosco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다! 코멘트 남긴 부분 확인 부탁드립니다. 🙇‍♂️

* date : 2025. 12. 26. 18:05
* description : 인증/인가 관련 요청 body
*/
data class KakaoOIDCLoginRequest(@NotBlank(message = "ID 토큰은 필수입니다") val idToken: String)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

질문입니다.
@notblank로 사용해도 bean validation 정상적으로 적용되나요?
@field:NotBlank와 같이 명시적인 스코프 지정을 안하면 적용이 안된다는 내용이 있어 공유드립니다.

reference:
https://velog.io/@ldhbenecia/Spring-Boot-Kotlin-Bean-Validation%EC%9D%B4-%EC%9E%91%EB%8F%99%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영완료했습니다! String값의 경우만 NotBlank 처리했고, 나머지 Long값이나 enum타입의 경우는 ?인자가 붙은게 아니기떄문에 이미 null을 허용할 수 없어서 기본값이 0으로 들어가는 것으로 확인했습니다. 0으로 들어가기 때문에 가입된계정이 없습니다 이라는 BusinessException을 던지는데, 이부분을 그러면 Long? 으로 반환할지도 고민이되는데, 현상태도 괜찮을 것 같고 어떻게 하는 것이 좋을까요?

@@ -0,0 +1,43 @@
package com.yapp2app.common.util
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지난번에 삭제된 내용이 포함된 것 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제거완료

password: ${JASYPT_PASSWORD}

app:
version: "@version@"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공통 설정의 경우 application.yaml에서 관리해도 될 것 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다~

Dockerfile Outdated
@@ -0,0 +1,42 @@
# Build stage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DockerFile 내부에서 gradle build를 하는 것 같은데, multi stage build가 필요하지 않다면 외부에서 gradle build를 하고 빌드 결과물만 COPY해오는 방법이 어떨까요? 트레이드 오프가 있을 것 같은데, DockerFile 내부에서 gradle build를 하면 캐싱을 사실상 활용하기가 어려워 전체 빌드 시간이 증가하고 gradle build에만 필요한 파일들이 포함되어 이미지 크기가 증가할 것 같습니다.

reference:
https://www.linkedin.com/posts/sabaribalajip_should-i-build-my-jar-externally-and-copy-activity-7364497498779947009-GkIG

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀하신대로 GitAction 시점에 build한 jar를 이용하는 방식으로 진행했으며 Layerd Cache를 이용하여 이미지를 만들도록 변경했습니다!

@@ -0,0 +1,19 @@
package com.yapp2app.common.api.config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

외부 요청을 보내는 용도라면 infra/config가 적절할 것 같습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영완료했습니다!

val nickname: String?,
)

data class OIDCPublicKeysResponse(var keys: MutableList<OIDCPublicKeyDto>)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data class OIDCPublicKeysResponse(var keys: MutableList<OIDCPublicKeyDto>)
data class OIDCPublicKeysResponse(val keys: MutableList<OIDCPublicKeyDto>)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정완료했습니다!

}

private fun registerKakaoUserIfEmpty(oauthInfoResponse: OauthInfoResponse): User {
val existingUser = userRepositoryPort.findByOid(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조회 후 없는 경우 새로 인스턴스를 생성하는 것 같은데 existingUser 대신 user 같은 명수명은 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경완료했습니다!

@github-actions
Copy link

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ❌ FAILED

Spotless Check: failure

⚠️ Code formatting issues detected!

Please run the following command to fix formatting issues:

./gradlew spotlessApply

Then commit the changes and push again.


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 5, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 5, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 9, 2026

Code Format Check ❌ FAILED

Spotless Check: failure

⚠️ Code formatting issues detected!

Please run the following command to fix formatting issues:

./gradlew spotlessApply

Then commit the changes and push again.


Pushed by: @Darren4641, Action: pull_request

@github-actions
Copy link

github-actions bot commented Jan 9, 2026

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @Darren4641, Action: pull_request

@koosco koosco merged commit 820853f into staging Jan 9, 2026
1 check passed
@Darren4641 Darren4641 deleted the feat/#7 branch January 13, 2026 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 카카오 Oauth 로그인 구현

3 participants