Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ local.properties
.idea/deploymentTargetSelector.xml
.idea/misc.xml
.idea/gradle.xml
.idea/appInsightsSettings.xml
.idea/appInsightsSettings.xml
/app/google-services.json
Comment on lines +19 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

google-services.json 무시 추가는 적절. 단, google-services 플러그인이 남아 있으면 빌드 실패 가능성 있음

현재 app/build.gradle.ktsplugins 블록에 id("com.google.gms.google-services")가 남아 있습니다. Firebase 설정을 제거했다면 플러그인도 제거하세요. 파일이 없을 때 플러그인이 적용되면 빌드가 중단될 수 있습니다.

다음 스크립트로 플러그인 잔존 여부와 파일 유무를 확인하세요.


🏁 Script executed:

#!/bin/bash
echo "== google-services.json 존재 여부 =="
fd -H --type f 'google-services.json' || true

echo "== google-services 플러그인 적용 여부 =="
rg -n --glob '*gradle*' -S 'com.google.gms.google-services' || true

echo "== Firebase 종속성 잔존 여부 =="
rg -nP --glob '*gradle*' '(firebase-|platform\\(.+firebase-bom|firebase-auth)' || true

Length of output: 619


플러그인 선언 제거 필요
.gitignoregoogle-services.json 무시 처리는 적절하나,
root build.gradle.kts(7행)와 app/build.gradle.kts(9행)에 남아 있는
id("com.google.gms.google-services") 선언을 삭제하세요.

🤖 Prompt for AI Agents
In .gitignore around lines 19-20 and in the Gradle files: the project is already
ignoring /app/google-services.json, so remove the plugin declarations
referencing it by deleting id("com.google.gms.google-services") from root
build.gradle.kts at line 7 and from app/build.gradle.kts at line 9; update any
related pluginManagement or classpath entries if present so the build does not
try to apply the google-services plugin while the credentials file is
intentionally ignored.

3 changes: 0 additions & 3 deletions .idea/.gitignore

This file was deleted.

6 changes: 4 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ dependencies {
//implementation ("androidx.datastore:datastore-preferences:1.1.1")

// 구글 로그인
implementation(platform("com.google.firebase:firebase-bom:34.1.0"))
implementation("com.google.firebase:firebase-auth")
implementation("com.google.android.gms:play-services-auth:21.2.0")

implementation("androidx.credentials:credentials:1.2.2")
implementation("androidx.credentials:credentials-play-services-auth:1.2.2")

Comment on lines +113 to +115

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

AndroidX Credentials 도입 OK. google-services 플러그인 제거 필요(중요)

app/google-services.json을 삭제/무시했는데, 상단 pluginsid("com.google.gms.google-services")가 남아 있으면 빌드 에러가 발생할 수 있습니다. Firebase를 더 이상 사용하지 않으면 플러그인을 제거하세요.

다음으로 확인/정리하세요:

  • plugins 블록에서 com.google.gms.google-services 제거
  • 루트/모듈 어디에도 Firebase 관련 의존성/플러그인 잔존 여부 점검(아래 스크립트 사용 가능)

참고: Credentials(1.2.2) 추가는 적절하며 play-services-auth와 병행 사용 가능합니다. 단, 로그인/로그아웃 경로를 일관되게(Identity API 또는 Credentials 기반) 정리하면 유지보수성이 좋아집니다.


🏁 Script executed:

#!/bin/bash
rg -n --glob '*gradle*' -S 'com.google.gms.google-services|firebase' || true

Length of output: 222


com.google.gms.google-services 플러그인 제거

  • root build.gradle.kts (7행)에서 id("com.google.gms.google-services") version "4.4.3" apply false 삭제
  • app build.gradle.kts (9행)에서 id("com.google.gms.google-services") 삭제
  • firebase 키워드 기반 의존성/플러그인은 검색되지 않음

Credentials(1.2.2) 추가는 정상이며, play-services-auth 병행 사용 가능합니다.

}

kapt {
Expand Down
71 changes: 0 additions & 71 deletions app/google-services.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.texthip.thip.data.repository

import android.content.Context
import com.google.firebase.Firebase
import com.google.firebase.auth.GoogleAuthProvider
import com.google.firebase.auth.auth
import com.kakao.sdk.user.UserApiClient
import com.texthip.thip.data.model.auth.request.AuthRequest
import com.texthip.thip.data.model.auth.response.AuthResponse
import com.texthip.thip.data.model.base.handleBaseResponse
import com.texthip.thip.data.service.AuthService
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.tasks.await
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import java.util.Base64
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
Expand All @@ -36,13 +36,14 @@ class AuthRepository @Inject constructor(
}
suspend fun loginWithGoogle(idToken: String): Result<AuthResponse?> {
return runCatching {
//Firebase에 구글 ID 토큰으로 로그인
val credential = GoogleAuthProvider.getCredential(idToken, null)
val authResult = Firebase.auth.signInWithCredential(credential).await()
val googleUid = authResult.user?.uid ?: throw IllegalStateException("Google User UID is null")
val payload = idToken.split('.')[1]//ID 토큰을 .기준 분리
val decodedJson = String(Base64.getUrlDecoder().decode(payload))//디코딩 해서 JSON 문자열 반환

val jsonObject = Json.parseToJsonElement(decodedJson).jsonObject
val googleSubId = jsonObject["sub"]?.jsonPrimitive?.content ?: throw IllegalStateException("구글 userID (sub)값이 없습니다.")//sub 값 추출

Comment on lines +39 to 44

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

JWT 페이로드 파싱 안전성 부족: 형식 검증·Base64URL 패딩·문자셋 누락으로 크래시 위험

  • idToken.split('.')[1]은 잘못된 토큰에서 IndexOutOfBoundsException.
  • java.util.Base64 디코더는 JWT의 무패딩 Base64URL을 엄격하게 처리해 IllegalArgumentException이 발생할 수 있습니다.
  • String(...)이 플랫폼 기본 문자셋을 사용.

아래처럼 형식 검증, android.util.Base64 URL_SAFE + NO_PADDING/NO_WRAP, UTF-8 지정으로 교체해주세요.

-            val payload = idToken.split('.')[1]//ID 토큰을 .기준 분리
-            val decodedJson = String(Base64.getUrlDecoder().decode(payload))//디코딩 해서 JSON 문자열 반환
-
-            val jsonObject = Json.parseToJsonElement(decodedJson).jsonObject
-            val googleSubId = jsonObject["sub"]?.jsonPrimitive?.content ?: throw IllegalStateException("구글 userID (sub)값이 없습니다.")//sub 값 추출
+            val parts = idToken.split('.')
+            require(parts.size == 3) { "유효하지 않은 ID 토큰 형식입니다." }
+            val payloadB64 = parts[1]
+            val payloadBytes = android.util.Base64.decode(
+                payloadB64,
+                android.util.Base64.URL_SAFE or android.util.Base64.NO_PADDING or android.util.Base64.NO_WRAP
+            )
+            val decodedJson = payloadBytes.toString(Charsets.UTF_8)
+
+            val jsonObject = Json { ignoreUnknownKeys = true }
+                .parseToJsonElement(decodedJson).jsonObject
+            val googleSubId = jsonObject["sub"]?.jsonPrimitive?.content
+                ?: error("구글 userID(sub) 클레임이 없습니다.")
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/data/repository/AuthRepository.kt around
lines 39 to 44, the current payload extraction and decoding is unsafe (uses
idToken.split('.')[1], java.util.Base64 and platform default charset). Replace
with: validate the idToken has at least three dot-separated parts and throw a
clear exception if not; decode the payload using
android.util.Base64.decode(payload, Base64.URL_SAFE or-ed with Base64.NO_PADDING
| Base64.NO_WRAP) to accept JWT Base64URL without padding; convert bytes to
String using UTF-8; then parse the JSON and extract "sub", throwing a clear
IllegalStateException if missing. Ensure all potential decode/parse exceptions
are caught or allowed to surface with informative messages rather than letting
IndexOutOfBounds or IllegalArgumentException crash silently.

//받아온 UID로 신규/기존 유저인지 확인 요청
val request = AuthRequest(oauth2Id = "google_$googleUid")
val request = AuthRequest(oauth2Id = "google_$googleSubId")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

서버 측 ID 토큰 검증(aud/iss/exp/서명) 보장 여부 확인 필요

클라이언트에서 sub만 추출해 식별자를 구성하면, 서버가 ID 토큰 서명을 검증하지 않는 경우 위·변조 위험이 있습니다. 서버가 반드시 ID 토큰 원문을 받아 OIDC 규격대로 검증하는지 확인해 주세요(특히 aud가 default_web_client_id와 일치하는지).

서버가 검증하지 않는다면, 여기서 oauth2Id만 보내지 말고 ID 토큰 원문도 함께 전달하도록 API/클라이언트 계약을 갱신하는 것을 권장합니다.


🏁 Script executed:

#!/bin/bash
# idToken(혹은 Google 토큰) 서버 전송 및 검증 유무 탐색
rg -n -C3 '\bidToken\b|GoogleIdToken|Authorization: Bearer|checkNewUser\('

Length of output: 5292


ID 토큰 원문 전달 및 서버 측 검증 보장
현재 클라이언트에서 ID 토큰(idToken)을 파싱해 oauth2Id = "google_$sub" 형태로만 서버에 전송하고 있습니다.
서버가 OIDC 규격(aud/iss/exp/서명)대로 ID 토큰을 실제로 검증하는지 확인하고, 검증이 누락되어 있다면 AuthRequest에 raw idToken을 포함해 전송하도록 API/클라이언트 계약을 갱신하세요.

authService.checkNewUser(request)
.handleBaseResponse()
.getOrThrow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fun EditProfileContent(
WarningTextField(
containerColor = colors.DarkGrey02,
value = uiState.nickname,
onValueChange = onNicknameChange,
onValueChange = { newNickname -> onNicknameChange(newNickname.lowercase()) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

로케일 비의존 소문자 변환 사용

lowercase()는 현재 로케일에 따라 결과가 달라질 수 있습니다. 식별자/닉네임 정규화에는 Locale.ROOT를 사용하세요.

- onValueChange = { newNickname -> onNicknameChange(newNickname.lowercase()) },
+ onValueChange = { newNickname -> onNicknameChange(newNickname.lowercase(java.util.Locale.ROOT)) },

추가(파일 상단 import):

import java.util.Locale
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageEditScreen.kt
around line 117, the call using newNickname.lowercase() is locale-dependent;
change it to newNickname.lowercase(Locale.ROOT) to ensure consistent,
locale-independent normalization for identifiers/nicknames, and add the import
java.util.Locale at the top of the file.

hint = stringResource(R.string.nickname_condition),
showWarning = uiState.nicknameWarningMessageResId != null,
showIcon = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class MyPageViewModel @Inject constructor(
viewModelScope.launch {
tokenManager.clearTokens()
// 2. 카카오 SDK에서 로그아웃
UserApiClient.instance.unlink { error ->
UserApiClient.instance.logout { error ->
if (error != null) {
Log.e("MyPageViewModel", "카카오 로그아웃 실패", error)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fun SignupNicknameContent(
WarningTextField(
containerColor = colors.DarkGrey02,
value = nickname,
onValueChange = onNicknameChange,
onValueChange = { newNickname -> onNicknameChange(newNickname.lowercase()) },//소문자로 즉시 변경

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

로케일 비의존 소문자 변환 사용

동일 이유로 Locale.ROOT 사용을 권장합니다.

- onValueChange = { newNickname -> onNicknameChange(newNickname.lowercase()) },//소문자로 즉시 변경
+ onValueChange = { newNickname -> onNicknameChange(newNickname.lowercase(java.util.Locale.ROOT)) }, // 소문자로 즉시 변경(로케일 비의존)

추가(파일 상단 import):

import java.util.Locale
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/signin/screen/SignupNicknameScreen.kt
around line 103, the call newNickname.lowercase() is locale-dependent; change it
to newNickname.lowercase(Locale.ROOT) and add the import java.util.Locale at the
top of the file so the nickname is lowercased in a locale-independent manner.

hint = stringResource(R.string.nickname_condition),
showWarning = warningMessageResId != null,
showIcon = false,
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,6 @@
<string name="api_genre_science_it" translatable="false">과학/IT</string>

<!-- Google login constants -->
<string name="default_web_client_id" translatable="false">353417813537-ck9g1v0qprlb5nf4dvasinim403eng0f.apps.googleusercontent.com</string>
<string name="default_web_client_id" translatable="false">211189590640-a2ul4bnsvislm12ov7k8eu61mtketl8d.apps.googleusercontent.com</string>

</resources>