mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Implement proper token storage
This commit is contained in:
@@ -40,6 +40,7 @@ kotlin {
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(libs.jetbrains.kotlinx.coroutines.core)
|
||||
implementation(libs.navigation.compose)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
|
||||
androidMain.dependencies {
|
||||
@@ -49,10 +50,19 @@ kotlin {
|
||||
implementation(libs.biometric)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.security.crypto)
|
||||
}
|
||||
|
||||
iosMain.dependencies {
|
||||
// nothing
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.darwin)
|
||||
implementation(libs.ktor.client.content.negotiation)
|
||||
implementation(libs.ktor.client.serialization.kotlinx.json)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
// Multiplatform settings for iOS Keychain support
|
||||
implementation(libs.multiplatform.settings)
|
||||
implementation(libs.multiplatform.settings.coroutines)
|
||||
implementation(libs.multiplatform.settings.serialization)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.pr0gramm3r101.utils.settings
|
||||
|
||||
import androidx.core.content.edit
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import com.pr0gramm3r101.utils.UtilsLibrary.context
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AndroidSecureSettings : Settings {
|
||||
private val masterKey by lazy {
|
||||
MasterKey.Builder(context)
|
||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
.build()
|
||||
}
|
||||
|
||||
private val encryptedPrefs by lazy {
|
||||
EncryptedSharedPreferences.create(
|
||||
context,
|
||||
"secure_storage",
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun putString(key: String, value: String) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { putString(key, value) }
|
||||
}
|
||||
|
||||
override suspend fun getString(key: String, default: String) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.getString(key, default) ?: default
|
||||
}
|
||||
|
||||
override suspend fun putInt(key: String, value: Int) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { putInt(key, value) }
|
||||
}
|
||||
|
||||
override suspend fun getInt(key: String, default: Int) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.getInt(key, default)
|
||||
}
|
||||
|
||||
override suspend fun putLong(key: String, value: Long) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { putLong(key, value) }
|
||||
}
|
||||
|
||||
override suspend fun getLong(key: String, default: Long) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.getLong(key, default)
|
||||
}
|
||||
|
||||
override suspend fun putFloat(key: String, value: Float) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { putFloat(key, value) }
|
||||
}
|
||||
|
||||
override suspend fun getFloat(key: String, default: Float) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.getFloat(key, default)
|
||||
}
|
||||
|
||||
override suspend fun putBoolean(key: String, value: Boolean) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { putBoolean(key, value) }
|
||||
}
|
||||
|
||||
override suspend fun getBoolean(key: String, default: Boolean) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.getBoolean(key, default)
|
||||
}
|
||||
|
||||
override suspend fun putStringSet(key: String, value: Set<String>) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { putStringSet(key, value) }
|
||||
}
|
||||
|
||||
override suspend fun getStringSet(key: String, default: Set<String>) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.getStringSet(key, default) ?: default
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.edit { remove(key) }
|
||||
}
|
||||
|
||||
override suspend fun contains(key: String) = withContext(Dispatchers.IO) {
|
||||
encryptedPrefs.contains(key)
|
||||
}
|
||||
}
|
||||
|
||||
-4
@@ -79,10 +79,6 @@ class AndroidSettings(): Settings {
|
||||
preferences[stringSetPreferencesKey(key)] ?: default
|
||||
}.first()
|
||||
|
||||
override suspend fun putStringList(key: String, value: List<String>) = putStringSet(key, value.toSet())
|
||||
|
||||
override suspend fun getStringList(key: String, default: List<String>) = getStringSet(key, default.toSet()).toList()
|
||||
|
||||
override suspend fun remove(key: String) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences.remove(stringPreferencesKey(key))
|
||||
|
||||
+1
-2
@@ -12,6 +12,5 @@ import com.pr0gramm3r101.utils.UtilsLibrary
|
||||
object DataStoreSingleton {
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
val dataStore: DataStore<Preferences>
|
||||
get() = UtilsLibrary.context.dataStore
|
||||
val dataStore get() = UtilsLibrary.context.dataStore
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ package com.pr0gramm3r101.utils.settings
|
||||
|
||||
actual val settings: Settings get() = AndroidSettings()
|
||||
actual val secureSettings: Settings
|
||||
get() = TODO("Not yet implemented")
|
||||
get() = AndroidSecureSettings()
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
package com.pr0gramm3r101.utils.settings
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
interface Settings {
|
||||
companion object {
|
||||
inline fun get() = settings
|
||||
@@ -25,9 +29,24 @@ interface Settings {
|
||||
|
||||
suspend fun putStringSet(key: String, value: Set<String>)
|
||||
suspend fun getStringSet(key: String, default: Set<String> = emptySet()): Set<String>
|
||||
|
||||
suspend fun putStringList(key: String, value: List<String>)
|
||||
suspend fun getStringList(key: String, default: List<String> = emptyList()): List<String>
|
||||
|
||||
suspend fun putStringList(key: String, value: List<String>) = withContext(Dispatchers.Default) {
|
||||
putString(key, Json.encodeToString(value))
|
||||
}
|
||||
|
||||
suspend fun getStringList(key: String, default: List<String> = emptyList()) = withContext(Dispatchers.Default) {
|
||||
try {
|
||||
getString(key, "").let {
|
||||
if (it.isEmpty()) {
|
||||
default
|
||||
} else {
|
||||
Json.decodeFromString<List<String>>(it)
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun remove(key: String)
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.pr0gramm3r101.utils.settings
|
||||
|
||||
import com.russhwolf.settings.ExperimentalSettingsApi
|
||||
import com.russhwolf.settings.ExperimentalSettingsImplementation
|
||||
import com.russhwolf.settings.KeychainSettings
|
||||
import com.russhwolf.settings.coroutines.toSuspendSettings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@OptIn(ExperimentalSettingsApi::class, ExperimentalSettingsImplementation::class)
|
||||
class IosSecureSettings : Settings {
|
||||
private val suspendSettings = KeychainSettings(service = "ru.fromchat.secure").toSuspendSettings()
|
||||
|
||||
override suspend fun putString(key: String, value: String) = suspendSettings.putString(key, value)
|
||||
|
||||
override suspend fun getString(key: String, default: String) = suspendSettings.getString(key, default)
|
||||
|
||||
override suspend fun putInt(key: String, value: Int) = suspendSettings.putInt(key, value)
|
||||
|
||||
override suspend fun getInt(key: String, default: Int) = suspendSettings.getInt(key, default)
|
||||
|
||||
override suspend fun putLong(key: String, value: Long) = suspendSettings.putLong(key, value)
|
||||
|
||||
override suspend fun getLong(key: String, default: Long) = suspendSettings.getLong(key, default)
|
||||
|
||||
override suspend fun putFloat(key: String, value: Float) = suspendSettings.putFloat(key, value)
|
||||
|
||||
override suspend fun getFloat(key: String, default: Float) = suspendSettings.getFloat(key, default)
|
||||
|
||||
override suspend fun putBoolean(key: String, value: Boolean) = suspendSettings.putBoolean(key, value)
|
||||
|
||||
override suspend fun getBoolean(key: String, default: Boolean) = suspendSettings.getBoolean(key, default)
|
||||
|
||||
override suspend fun putStringSet(key: String, value: Set<String>) = withContext(Dispatchers.IO) {
|
||||
putStringList(key, value.toList())
|
||||
}
|
||||
|
||||
override suspend fun getStringSet(key: String, default: Set<String>) = withContext(Dispatchers.IO) {
|
||||
getStringList(key, default.toList()).toSet()
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) = suspendSettings.remove(key)
|
||||
|
||||
override suspend fun contains(key: String) = suspendSettings.hasKey(key)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.pr0gramm3r101.utils.settings
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
// TODO fix
|
||||
class IosSettings : Settings {
|
||||
private val defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults
|
||||
|
||||
@@ -45,7 +45,7 @@ class IosSettings : Settings {
|
||||
|
||||
override suspend fun remove(key: String) = defaults.removeObjectForKey(key)
|
||||
|
||||
override suspend fun contains(key: String): Boolean = withContext(Dispatchers.Default) {
|
||||
override suspend fun contains(key: String): Boolean = withContext(Dispatchers.IO) {
|
||||
defaults.objectForKey(key) != null
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@ package com.pr0gramm3r101.utils.settings
|
||||
|
||||
actual val settings: Settings get() = IosSettings()
|
||||
actual val secureSettings: Settings
|
||||
get() = TODO("Not yet implemented")
|
||||
get() = IosSecureSettings()
|
||||
Reference in New Issue
Block a user