Skip to content
Closed
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
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ dependencies {
implementation(projects.core.network)
implementation(projects.core.navigation)
implementation(projects.core.ui)
implementation(projects.core.datastore)
implementation(projects.core.token)
implementation(projects.core.util)
implementation(projects.data)
implementation(projects.domain)
Expand Down
1 change: 1 addition & 0 deletions core/datastore/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
16 changes: 16 additions & 0 deletions core/datastore/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
alias(libs.plugins.twix.android.library)
alias(libs.plugins.twix.koin)
alias(libs.plugins.serialization)
}

android {
namespace = "com.twix.datastore"
}

dependencies {
implementation(projects.core.token)

implementation(libs.androidx.datastore)
implementation(libs.kotlinx.serialization.json)
}
Empty file.
21 changes: 21 additions & 0 deletions core/datastore/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
4 changes: 4 additions & 0 deletions core/datastore/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
52 changes: 52 additions & 0 deletions core/datastore/src/main/java/com/twix/datastore/AuthConfigure.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.twix.datastore

import androidx.datastore.core.Serializer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import java.io.InputStream
import java.io.OutputStream

@Serializable
internal data class AuthConfigure(
val accessToken: String =
"eyJhbGciOiJIUzM4NCJ9." +
"eyJzdWIiOiIxIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTc3MDI0NzM0NCwiZXhwIjoxNzcwODUyMTQ0fQ." +
"67rDscm8BeayYFA1gfcEMliEdEh8-HTUyE5TwmAT8Ef8ZvtaWczxpMNZqI5htiek",
val refreshToken: String =
"eyJhbGciOiJIUzM4NCJ9." +
"eyJzdWIiOiIxIiwidHlwZSI6InJlZnJlc2giLCJpYXQiOjE3NzAyNDczNDQsImV4cCI6MTc3MDg1MjE0NH0." +
"zgUYdR6onyeY5EaH2_pWLs1rjNLf8m8ZeXsY7Cbk99a_2tzR0rDBZO_hdGTnorRL",
)

internal object AuthConfigureSerializer : Serializer<AuthConfigure> {
override val defaultValue: AuthConfigure
get() = AuthConfigure()

override suspend fun readFrom(input: InputStream): AuthConfigure =
try {
Json.decodeFromString(
deserializer = AuthConfigure.serializer(),
string = input.readBytes().decodeToString(),
)
} catch (e: SerializationException) {
defaultValue
}

override suspend fun writeTo(
t: AuthConfigure,
output: OutputStream,
) {
withContext(Dispatchers.IO) {
output.write(
Json
.encodeToString(
serializer = AuthConfigure.serializer(),
value = t,
).encodeToByteArray(),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.twix.datastore

import android.content.Context
import androidx.datastore.core.DataStore
import com.twix.token.TokenProvider
import kotlinx.coroutines.flow.first

class AuthTokenProvider(
private val context: Context,
) : TokenProvider {
private val dataStore: DataStore<AuthConfigure>
get() = context.authDataStore

override suspend fun accessToken(): String = dataStore.data.first().accessToken

override suspend fun refreshToken(): String = dataStore.data.first().refreshToken

override suspend fun saveToken(
accessToken: String,
refreshToken: String,
) {
dataStore.updateData {
it.copy(accessToken = accessToken, refreshToken = refreshToken)
}
}

override suspend fun clear() {
dataStore.updateData {
it.copy(accessToken = "", refreshToken = "")
}
Comment on lines +11 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f -e kt | head -20

Repository: YAPP-Github/Twix-Android

Length of output: 1630


🏁 Script executed:

rg "authDataStore" --type kt -A 3 -B 3

Repository: YAPP-Github/Twix-Android

Length of output: 94


🏁 Script executed:

rg "context\.authDataStore\|authDataStore\s*=" --type kt -A 5

Repository: YAPP-Github/Twix-Android

Length of output: 94


🏁 Script executed:

find . -type f -name "*.kt" | xargs grep -l "DataStore\|authDataStore" | head -10

Repository: YAPP-Github/Twix-Android

Length of output: 201


🏁 Script executed:

cat -n core/datastore/src/main/java/com/twix/datastore/DataStore.kt

Repository: YAPP-Github/Twix-Android

Length of output: 431


🏁 Script executed:

cat -n core/datastore/src/main/java/com/twix/datastore/AuthTokenProvider.kt

Repository: YAPP-Github/Twix-Android

Length of output: 1175


🏁 Script executed:

find . -type f -name "*Serializer*" | grep -i auth

Repository: YAPP-Github/Twix-Android

Length of output: 50


🏁 Script executed:

rg "Serializer|serializer" --type-list

Repository: YAPP-Github/Twix-Android

Length of output: 457


🏁 Script executed:

find . -type f \( -name "*.kt" -o -name "*.java" \) | xargs grep -l "AuthConfigureSerializer" | head -5

Repository: YAPP-Github/Twix-Android

Length of output: 197


🏁 Script executed:

rg "object AuthConfigureSerializer|class AuthConfigureSerializer" -A 20

Repository: YAPP-Github/Twix-Android

Length of output: 2099


🏁 Script executed:

find . -type f -name "*.kt" | xargs grep -l "Serializer" | grep -i auth

Repository: YAPP-Github/Twix-Android

Length of output: 134


🏁 Script executed:

cat -n core/datastore/src/main/java/com/twix/datastore/AuthConfigure.kt

Repository: YAPP-Github/Twix-Android

Length of output: 2187


토큰이 평문으로 저장되어 보안 위험이 있습니다.

AuthConfigureSerializer의 readFrom/writeTo에서 Json.decodeFromString/encodeToString를 사용하고 있어, 토큰이 보호 없이 "auth-configure.json" 파일에 평문으로 저장됩니다. 루팅된 기기나 백업 환경에서 토큰이 노출될 수 있습니다.

개선 방안:

  1. 권장: Jetpack Security의 EncryptedDataStore + MasterKey를 사용하는 것이 가장 표준적입니다
  2. 대안: 현재 구조를 유지하면서 Serializer에서 암복호화를 적용할 수도 있습니다 (예: Tink나 BouncyCastle 라이브러리 활용)

EncryptedDataStore 적용이 어렵다면, Serializer에 암호화 로직을 추가하는 예시를 제공할 수 있습니다. 보안 요구사항상 암호화 적용이 필수인지 확인 부탁드립니다.

🤖 Prompt for AI Agents
In `@core/datastore/src/main/java/com/twix/datastore/AuthTokenProvider.kt` around
lines 11 - 30, Auth tokens are being saved in plaintext via the
DataStore/Serializer pipeline (see AuthTokenProvider -> dataStore and
AuthConfigureSerializer readFrom/writeTo writing "auth-configure.json"), so
replace plaintext storage with encryption: either migrate to Jetpack Security's
encrypted solution (create a MasterKey and use
EncryptedDataStore/EncryptedFile-backed DataStore for AuthConfigure so
AuthTokenProvider continues to read/write encrypted data), or implement
encryption inside AuthConfigureSerializer's readFrom/writeTo using a
MasterKey/Cipher to encrypt bytes before writing and decrypt after reading (use
AndroidX Security Crypto APIs or a vetted library like Tink), and update
DataStore usage accordingly so accessToken()/saveToken()/clear() operate on the
encrypted store.

}
}
10 changes: 10 additions & 0 deletions core/datastore/src/main/java/com/twix/datastore/DataStore.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.twix.datastore

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.dataStore

internal val Context.authDataStore: DataStore<AuthConfigure> by dataStore(
fileName = "auth-configure.json",
serializer = AuthConfigureSerializer,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.twix.datastore.di

import com.twix.datastore.AuthTokenProvider
import com.twix.token.TokenProvider
import org.koin.dsl.module

val dataStoreModule =
module {
single<TokenProvider> { AuthTokenProvider(get()) }
}
1 change: 1 addition & 0 deletions core/token/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
3 changes: 3 additions & 0 deletions core/token/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
alias(libs.plugins.twix.java.library)
}
Empty file added core/token/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions core/token/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
14 changes: 14 additions & 0 deletions core/token/src/main/java/com/twix/token/TokenProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.twix.token

interface TokenProvider {
suspend fun accessToken(): String

suspend fun refreshToken(): String

suspend fun saveToken(
accessToken: String,
refreshToken: String,
)

suspend fun clear()
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ kotlinx-serialization-json = "1.9.0"
# AndroidX
androidx-core-ktx = "1.17.0"
androidx-lifecycle-runtime-ktx = "2.10.0"
androidx-datastore = "1.2.0"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find gradle configuration files
git ls-files | grep -E "\.toml$|build\.gradle|settings\.gradle"

Repository: YAPP-Github/Twix-Android

Length of output: 646


🏁 Script executed:

# Check the actual artifact configuration in libs.versions.toml
cat gradle/libs.versions.toml

Repository: YAPP-Github/Twix-Android

Length of output: 9311


🏁 Script executed:

# Search for DataStore imports to understand which library is being used
rg "import.*datastore" --type kotlin -i

Repository: YAPP-Github/Twix-Android

Length of output: 597


🏁 Script executed:

# Look for DataStore API usage
rg "dataStore|DataStore|Preferences\|DataStoreFactory" --type kotlin -B 2 -A 3

Repository: YAPP-Github/Twix-Android

Length of output: 4271


🏁 Script executed:

cat core/datastore/build.gradle.kts

Repository: YAPP-Github/Twix-Android

Length of output: 401


🏁 Script executed:

# Check the full AuthConfigure and AuthConfigureSerializer implementation
cat core/datastore/src/main/java/com/twix/datastore/AuthConfigure.kt

Repository: YAPP-Github/Twix-Android

Length of output: 1823


🏁 Script executed:

cat core/datastore/src/main/java/com/twix/datastore/DataStore.kt

Repository: YAPP-Github/Twix-Android

Length of output: 361


DataStore 의존성을 수정해야 합니다

현재 datastore-preferences artifact를 선언하고 있지만, 코드에서는 typed DataStore API를 사용하고 있습니다. 코드 분석 결과:

  • AuthConfigureSerializer<T>로 직렬화하고 있음 (typed DataStore)
  • DataStore<AuthConfigure>dataStore 위임 함수를 사용 중
  • 이 모든 API는 androidx.datastore:datastore 라이브러리에만 포함됨

문제: datastore-preferences는 key-value 저장소용이며, 커스텀 Serializer<T> 인터페이스와 typed DataStore<T> API를 제공하지 않습니다. 현재 선언된 artifact로는 코드가 정상 동작할 수 없습니다.

🔧 수정 사항
-androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
+androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }
🤖 Prompt for AI Agents
In `@gradle/libs.versions.toml` at line 18, The project declares the wrong
DataStore artifact: replace the preferences-only artifact with the typed
DataStore artifact so the Serializer-based API used in code works; update the
dependency entry so the project depends on androidx.datastore:datastore (which
provides Serializer<T>, DataStore<T>, and the dataStore delegate) instead of the
datastore-preferences artifact referenced for androidx-datastore, so
classes/interfaces like AuthConfigure, Serializer<T>, DataStore<AuthConfigure>,
and the dataStore delegate resolve correctly.


# Google
material = "1.13.0"
Expand Down Expand Up @@ -71,6 +72,7 @@ appcompat = "1.7.1"
# AndroidX
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidx-lifecycle-runtime-ktx" }
androidx-datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }

# CameraX
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraX" }
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ include(":core:network")
include(":core:analytics")
include(":feature:main")
include(":feature:task-certification")
include(":core:datastore")
include(":core:token")
include(":core:result")