diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 241cf0d3..cf550461 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -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)
diff --git a/core/datastore/.gitignore b/core/datastore/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/datastore/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts
new file mode 100644
index 00000000..c76d6461
--- /dev/null
+++ b/core/datastore/build.gradle.kts
@@ -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)
+}
diff --git a/core/datastore/consumer-rules.pro b/core/datastore/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/datastore/proguard-rules.pro b/core/datastore/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/datastore/proguard-rules.pro
@@ -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
\ No newline at end of file
diff --git a/core/datastore/src/main/AndroidManifest.xml b/core/datastore/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..8bdb7e14
--- /dev/null
+++ b/core/datastore/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/core/datastore/src/main/java/com/twix/datastore/AuthConfigure.kt b/core/datastore/src/main/java/com/twix/datastore/AuthConfigure.kt
new file mode 100644
index 00000000..3406980c
--- /dev/null
+++ b/core/datastore/src/main/java/com/twix/datastore/AuthConfigure.kt
@@ -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 {
+ 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(),
+ )
+ }
+ }
+}
diff --git a/core/datastore/src/main/java/com/twix/datastore/AuthTokenProvider.kt b/core/datastore/src/main/java/com/twix/datastore/AuthTokenProvider.kt
new file mode 100644
index 00000000..17bbfbab
--- /dev/null
+++ b/core/datastore/src/main/java/com/twix/datastore/AuthTokenProvider.kt
@@ -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
+ 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 = "")
+ }
+ }
+}
diff --git a/core/datastore/src/main/java/com/twix/datastore/DataStore.kt b/core/datastore/src/main/java/com/twix/datastore/DataStore.kt
new file mode 100644
index 00000000..a98f6396
--- /dev/null
+++ b/core/datastore/src/main/java/com/twix/datastore/DataStore.kt
@@ -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 by dataStore(
+ fileName = "auth-configure.json",
+ serializer = AuthConfigureSerializer,
+)
diff --git a/core/datastore/src/main/java/com/twix/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/twix/datastore/di/DataStoreModule.kt
new file mode 100644
index 00000000..3c0dae11
--- /dev/null
+++ b/core/datastore/src/main/java/com/twix/datastore/di/DataStoreModule.kt
@@ -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 { AuthTokenProvider(get()) }
+ }
diff --git a/core/token/.gitignore b/core/token/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/token/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/token/build.gradle.kts b/core/token/build.gradle.kts
new file mode 100644
index 00000000..ba7b83cc
--- /dev/null
+++ b/core/token/build.gradle.kts
@@ -0,0 +1,3 @@
+plugins {
+ alias(libs.plugins.twix.java.library)
+}
diff --git a/core/token/consumer-rules.pro b/core/token/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/token/proguard-rules.pro b/core/token/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/token/proguard-rules.pro
@@ -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
\ No newline at end of file
diff --git a/core/token/src/main/java/com/twix/token/TokenProvider.kt b/core/token/src/main/java/com/twix/token/TokenProvider.kt
new file mode 100644
index 00000000..8d225b25
--- /dev/null
+++ b/core/token/src/main/java/com/twix/token/TokenProvider.kt
@@ -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()
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 57f800f0..e5f0d17e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -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"
# Google
material = "1.13.0"
@@ -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" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 05195c9d..718291c1 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -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")