Skip to main content

Token Persistence

To maintain user sessions across app restarts, Koani provides a TokenManager abstraction for loading, storing, and clearing OAuth2 tokens.

TokenManager Interface​

The TokenManager interface defines the contract for token storage:

public interface TokenManager {
public fun accessToken(): String?
public fun refreshToken(): String?
public fun storeTokens(accessToken: String, refreshToken: String?)
public fun clearTokens()
}

Default Storage: MemoryTokenManager​

By default, KoaniClient uses MemoryTokenManager(). This stores tokens in memory during the app's runtime. While useful for CLI tools and unit tests, stored tokens will be lost when the app process finishes.

Secure Storage with KSafe (koani-auth-persistence-ksafe)​

For production mobile and multiplatform apps, Koani provides the optional koani-auth-persistence-ksafe module. It uses KSafe to store tokens securely using platform-native encrypted storage (such as EncryptedSharedPreferences on Android and Keychain on iOS).

1. Add the Dependency​

Add koani-auth-persistence-ksafe to your build.gradle.kts:

build.gradle.kts
commonMain.dependencies {
implementation("io.github.rribeiro5:koani-auth-persistence-ksafe:<latest_version>")
}

2. Configure KoaniClient with KSafeTokenManager​

Pass KSafeTokenManager when initializing KoaniClient:

import io.github.rribeiro5.koani.KoaniClient
import io.github.rribeiro5.koani.auth.persistence.ksafe.KSafeTokenManager
import eu.anifantakis.lib.ksafe.KSafe

// Initialize KSafe storage instance
val kSafe = KSafe()
val persistentTokenManager = KSafeTokenManager(kSafe)

val client = KoaniClient(clientId = "YOUR_CLIENT_ID") {
tokenManager(persistentTokenManager)
}

Once configured, Koani automatically persists access and refresh tokens upon successful authentication or token refresh, restoring them on future app launches.

Custom TokenManager Implementation​

If your project uses Android DataStore, Room, SQLDelight, or another persistence mechanism, you can implement TokenManager:

import io.github.rribeiro5.koani.auth.TokenManager

class DataStoreTokenManager(
private val encryptedPreferences: EncryptedPreferencesStorage
) : TokenManager {

override fun accessToken(): String? {
return encryptedPreferences.getString("access_token")
}

override fun refreshToken(): String? {
return encryptedPreferences.getString("refresh_token")
}

override fun storeTokens(accessToken: String, refreshToken: String?) {
encryptedPreferences.putString("access_token", accessToken)
if (refreshToken != null) {
encryptedPreferences.putString("refresh_token", refreshToken)
} else {
encryptedPreferences.remove("refresh_token")
}
}

override fun clearTokens() {
encryptedPreferences.remove("access_token")
encryptedPreferences.remove("refresh_token")
}
}

Pass your custom implementation to the KoaniClient DSL:

val client = KoaniClient(clientId = "YOUR_CLIENT_ID") {
tokenManager(DataStoreTokenManager(encryptedStorage))
}