diff --git a/session-manager-android/src/androidTest/java/com/web3auth/session_manager_android/SessionManagerTest.kt b/session-manager-android/src/androidTest/java/com/web3auth/session_manager_android/SessionManagerTest.kt index 0931776..0586f1e 100644 --- a/session-manager-android/src/androidTest/java/com/web3auth/session_manager_android/SessionManagerTest.kt +++ b/session-manager-android/src/androidTest/java/com/web3auth/session_manager_android/SessionManagerTest.kt @@ -5,6 +5,7 @@ import androidx.test.platform.app.InstrumentationRegistry import com.web3auth.session_manager_android.keystore.KeyStoreManager import com.web3auth.session_manager_android.types.AES256CBC import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue import org.bouncycastle.util.encoders.Hex import org.json.JSONObject import org.junit.Test @@ -20,7 +21,8 @@ class SessionManagerTest { @Throws(ExecutionException::class, InterruptedException::class) fun test_createSession() { val context = InstrumentationRegistry.getInstrumentation().context - sessionManager = SessionManager(context, 86400, context.packageName) + val sessionId = SessionManager.generateRandomSessionKey() + sessionManager = SessionManager(context, 86400, context.packageName, sessionId) val json = JSONObject() json.put( "privateKey", @@ -38,18 +40,20 @@ class SessionManagerTest { @Throws(ExecutionException::class, InterruptedException::class) fun test_authorizeSession() { val context = InstrumentationRegistry.getInstrumentation().context - sessionManager = SessionManager(context, 86400, context.packageName) + val sessionId = SessionManager.generateRandomSessionKey() + sessionManager = SessionManager(context, 86400, context.packageName, sessionId) val json = JSONObject() json.put( "privateKey", "91714924788458331086143283967892938475657483928374623640418082526960471979197446884" ) json.put("publicAddress", "0x93475c78dv0jt80f2b6715a5c53838eC4aC96EF7") - sessionManager.createSession( + val created = sessionManager.createSession( json.toString(), - context, + context ).get() - sessionManager = SessionManager(context, 86400, "*") + SessionManager.saveSessionIdToStorage(created) + assertTrue(created.isNotEmpty()) val authResponse = sessionManager.authorizeSession( context.packageName, context @@ -63,20 +67,24 @@ class SessionManagerTest { @Throws(ExecutionException::class, InterruptedException::class) fun test_invalidateSession() { val context = InstrumentationRegistry.getInstrumentation().context - sessionManager = SessionManager(context, 86400, context.packageName) + val sessionId = SessionManager.generateRandomSessionKey() + sessionManager = SessionManager(context, 86400, context.packageName, sessionId) val json = JSONObject() json.put( "privateKey", "91714924788458331086143283967892938475657483928374623640418082526960471979197446884" ) json.put("publicAddress", "0x93475c78dv0jt80f2b6715a5c53838eC4aC96EF7") - sessionManager.createSession( + val created = sessionManager.createSession( json.toString(), context ).get() - sessionManager = SessionManager(context, 86400, context.packageName) + SessionManager.saveSessionIdToStorage(created) val invalidateRes = sessionManager.invalidateSession(context).get() assertEquals(invalidateRes, true) + SessionManager.deleteSessionIdFromStorage() + val res = SessionManager.getSessionIdFromStorage().isNotEmpty() + assertEquals(res, false) } @Test diff --git a/session-manager-android/src/main/java/com/web3auth/session_manager_android/SessionManager.kt b/session-manager-android/src/main/java/com/web3auth/session_manager_android/SessionManager.kt index 9beb408..4be27c7 100644 --- a/session-manager-android/src/main/java/com/web3auth/session_manager_android/SessionManager.kt +++ b/session-manager-android/src/main/java/com/web3auth/session_manager_android/SessionManager.kt @@ -23,46 +23,78 @@ import java.nio.charset.StandardCharsets import java.util.concurrent.CompletableFuture import kotlin.math.min -class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: String = "*") { +class SessionManager( + context: Context, + sessionTime: Int = 86400, + allowedOrigin: String = "*", + sessionId: String? = null +) { private val gson = GsonBuilder().disableHtmlEscaping().create() private val web3AuthApi = ApiHelper.getInstance().create(Web3AuthApi::class.java) private var sessionTime: Int private var allowedOrigin: String + private lateinit var sessionId: String companion object { fun generateRandomSessionKey(): String { return KeyStoreManager.generateRandomSessionKey() } + + fun getSessionIdFromStorage(): String { + return KeyStoreManager.getPreferencesData(KeyStoreManager.SESSION_ID_TAG).toString() + } + + fun deleteSessionIdFromStorage() { + KeyStoreManager.deletePreferencesData(KeyStoreManager.SESSION_ID_TAG) + } + + fun saveSessionIdToStorage(sessionId: String) { + if (sessionId.isNotEmpty() && sessionId.isNotBlank()) { + KeyStoreManager.savePreferenceData(KeyStoreManager.SESSION_ID_TAG, sessionId) + } + } } init { KeyStoreManager.initializePreferences(context.applicationContext) initiateKeyStoreManager() + if (sessionId != null && sessionId.isNotEmpty()) { + this.sessionId = sessionId + } this.sessionTime = sessionTime this.allowedOrigin = allowedOrigin } - private fun initiateKeyStoreManager() { - KeyStoreManager.getKeyGenerator() - } - - fun saveSessionId(sessionId: String) { + fun setSessionId(sessionId: String) { if (sessionId.isNotEmpty()) { - KeyStoreManager.savePreferenceData( - KeyStoreManager.SESSION_ID_TAG, sessionId - ) + this.sessionId = sessionId } } fun getSessionId(): String { - return KeyStoreManager.getPreferencesData(KeyStoreManager.SESSION_ID_TAG).toString() + return this.sessionId + } + + private fun initiateKeyStoreManager() { + KeyStoreManager.getKeyGenerator() } + /** - * Authorize User session in order to avoid re-login + * Authorizes a session for a given origin, performing any necessary authentication or token generation. + * This method operates asynchronously and returns a `CompletableFuture` that holds the result of the authorization. + * + * @param origin A string representing the origin or source of the session. This can be the app's package name or a specific domain. + * @param context The context in which the session authorization occurs. Typically used to access resources or perform operations within the application. + * + * @return A `CompletableFuture` that will contain the result of the session authorization. This will usually be a token or session ID upon successful authorization. + * + * Usage example: + * ``` + * authorizeSession("com.example.app", context) + * ``` */ - fun authorizeSession(origin: String, context: Context): CompletableFuture { return CompletableFuture.supplyAsync { if (!ApiHelper.isNetworkAvailable(context)) { @@ -73,10 +105,9 @@ class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: ) } - val sessionId = - getSessionId() + val sessionId = this.sessionId - if (sessionId.isEmpty()) { + if (sessionId.isNullOrEmpty()) { throw Exception( SessionManagerError.getError( ErrorCode.SESSIONID_NOT_FOUND @@ -140,6 +171,17 @@ class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: }.exceptionally { throw it } } + /** + * Invalidates the current session, effectively logging the user out or clearing session-related data. + * + * @param context The context in which the session invalidation occurs. Typically used to access resources + * or perform operations within the application (e.g., clearing shared preferences or cache). + * + * Usage example: + * ``` + * invalidateSession(context) + * ``` + */ fun invalidateSession( context: Context, ): CompletableFuture { @@ -152,7 +194,14 @@ class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: ) } - val sessionId = getSessionId() + val sessionId = this.sessionId + if (sessionId.isNullOrEmpty()) { + throw Exception( + SessionManagerError.getError( + ErrorCode.SESSIONID_NOT_FOUND + ) + ) + } val ephemKey = "04" + KeyStoreManager.getPubKey(sessionId).padStart(128, '0') val ivKey = KeyStoreManager.randomBytes(16) @@ -193,7 +242,6 @@ class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: } if (result.isSuccessful) { - KeyStoreManager.deletePreferencesData(KeyStoreManager.SESSION_ID_TAG) true } else { throw Exception( @@ -206,12 +254,29 @@ class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: }.exceptionally { throw it } } + /** + * Creates a new session with the provided data. + * + * @param data The session data as a string. This can include information such as tokens or user-specific identifiers. + * @param context The context in which the session is being created. Typically used to access resources or perform operations within the application. + * + * @return This function can be extended to return a result, such as a success or failure message. + * + * Usage example: + * ``` + * createSession("sessionData", context) + * ``` + */ fun createSession( data: String, context: Context, ): CompletableFuture { return CompletableFuture.supplyAsync { - val newSessionKey = generateRandomSessionKey() + val newSessionKey = this.sessionId + + if (newSessionKey.isNullOrEmpty()) { + throw Exception(SessionManagerError.getError(ErrorCode.SESSIONID_NOT_FOUND)) + } if (!ApiHelper.isNetworkAvailable(context)) { throw Exception( SessionManagerError.getError(ErrorCode.RUNTIME_ERROR) @@ -254,11 +319,7 @@ class SessionManager(context: Context, sessionTime: Int = 86400, allowedOrigin: } } - if (result.isSuccessful) { - KeyStoreManager.savePreferenceData( - KeyStoreManager.SESSION_ID_TAG, newSessionKey - ) - } else { + if (!result.isSuccessful) { throw Exception( SessionManagerError.getError( ErrorCode.SOMETHING_WENT_WRONG