From f83312c89bfe9cda7804d73ac249f587612f9da0 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 27 Feb 2025 14:49:46 +0530 Subject: [PATCH 1/8] Renamed session token to web_sso_token and added experimental tag to web sso token apis --- .../authentication/AuthenticationAPIClient.kt | 10 +++++++--- .../storage/CredentialsManager.kt | 15 ++++++++++---- .../storage/SecureCredentialsManager.kt | 15 ++++++++++---- .../auth0/android/result/SSOCredentials.kt | 18 ++++++++--------- .../AuthenticationAPIClientTest.kt | 12 +++++------ .../storage/CredentialsManagerTest.kt | 18 ++++++++--------- .../storage/SecureCredentialsManagerTest.kt | 20 +++++++++---------- 7 files changed, 63 insertions(+), 45 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index a57bfd3d5..5b3003ea5 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -4,6 +4,7 @@ import androidx.annotation.VisibleForTesting import com.auth0.android.Auth0 import com.auth0.android.Auth0Exception import com.auth0.android.NetworkErrorException +import com.auth0.android.annotation.ExperimentalAuth0Api import com.auth0.android.request.* import com.auth0.android.request.internal.* import com.auth0.android.request.internal.GsonAdapter.Companion.forMap @@ -923,12 +924,15 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe } /** - * Creates a new request to fetch a session token in exchange for a refresh token. + * Creates a new request to fetch a web sso token in exchange for a refresh token. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. * * @param refreshToken A valid refresh token obtained as part of Auth0 authentication - * @return a request to fetch a session token + * @return a request to fetch a web sso token + * */ - public fun fetchSessionToken(refreshToken: String): Request { + @ExperimentalAuth0Api + public fun fetchWebSsoToken(refreshToken: String): Request { val params = ParameterBuilder.newBuilder() .setClientId(clientId) .setGrantType(ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt index 8f6b3e510..264d764f4 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt @@ -2,6 +2,7 @@ package com.auth0.android.authentication.storage import android.text.TextUtils import androidx.annotation.VisibleForTesting +import com.auth0.android.annotation.ExperimentalAuth0Api import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.Callback @@ -57,12 +58,14 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting /** * Stores the given [SSOCredentials] refresh token in the storage. - * This method must be called if the SSOCredentials are obtained by directly invoking [AuthenticationAPIClient.fetchSessionToken] api and + * This method must be called if the SSOCredentials are obtained by directly invoking [AuthenticationAPIClient.fetchWebSsoToken] api and * [rotating refresh token](https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation) are enabled for * the client. Method will silently return ,if the passed credentials has no refresh token. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. * * @param ssoCredentials the credentials to save in the storage. */ + @ExperimentalAuth0Api override fun saveSsoCredentials(ssoCredentials: SSOCredentials) { if (ssoCredentials.refreshToken.isNullOrEmpty()) return // No refresh token to save @@ -78,8 +81,10 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting /** * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued + * if a new one is issued. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. */ + @ExperimentalAuth0Api override fun getSsoCredentials(callback: Callback) { serialExecutor.execute { val refreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) @@ -90,7 +95,7 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting try { val sessionCredentials = - authenticationClient.fetchSessionToken(refreshToken) + authenticationClient.fetchWebSsoToken(refreshToken) .execute() saveSsoCredentials(sessionCredentials) callback.onSuccess(sessionCredentials) @@ -115,10 +120,12 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting /** * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued + * if a new one is issued. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) + @ExperimentalAuth0Api override suspend fun awaitSsoCredentials(): SSOCredentials { return suspendCancellableCoroutine { continuation -> getSsoCredentials(object : Callback { diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt index 955827f9a..455a27ce5 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt @@ -7,6 +7,7 @@ import android.util.Log import androidx.annotation.VisibleForTesting import androidx.fragment.app.FragmentActivity import com.auth0.android.Auth0 +import com.auth0.android.annotation.ExperimentalAuth0Api import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.Callback @@ -130,12 +131,14 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT /** * Stores the given [SSOCredentials] refresh token in the storage. - * This method must be called if the SSOCredentials are obtained by directly invoking [AuthenticationAPIClient.fetchSessionToken] api and + * This method must be called if the SSOCredentials are obtained by directly invoking [AuthenticationAPIClient.fetchWebSsoToken] api and * [rotating refresh token](https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation) are enabled for * the client. Method will silently return ,if the passed credentials has no refresh token. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. * * @param ssoCredentials the credentials to save in the storage. */ + @ExperimentalAuth0Api override fun saveSsoCredentials(ssoCredentials: SSOCredentials) { if (ssoCredentials.refreshToken.isNullOrEmpty()) return // No refresh token to save serialExecutor.execute { @@ -158,8 +161,10 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT /** * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued + * if a new one is issued. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. */ + @ExperimentalAuth0Api override fun getSsoCredentials(callback: Callback) { serialExecutor.execute { lateinit var existingCredentials:Credentials @@ -175,7 +180,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } try { val sessionCredentials = - authenticationClient.fetchSessionToken(existingCredentials.refreshToken!!) + authenticationClient.fetchWebSsoToken(existingCredentials.refreshToken!!) .execute() saveSsoCredentials(sessionCredentials) callback.onSuccess(sessionCredentials) @@ -205,10 +210,12 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT /** * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued + * if a new one is issued. + * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) + @ExperimentalAuth0Api override suspend fun awaitSsoCredentials(): SSOCredentials { return suspendCancellableCoroutine { continuation -> getSsoCredentials(object : Callback { diff --git a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt index bad5432fd..2e2bdd714 100644 --- a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt +++ b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt @@ -3,9 +3,9 @@ package com.auth0.android.result import com.google.gson.annotations.SerializedName /** - * Holds the session token credentials required for web SSO . + * Holds the token credentials required for web SSO . * - * * *sessionToken*: Session Token for web SSO + * * *webSsoToken*: Token for web SSO * * *refreshToken*: Refresh Token that can be used to request new tokens without signing in again * * *tokenType*: Contains information about how the token should be used. * * *expiresIn*: The token expiration duration. @@ -14,14 +14,14 @@ import com.google.gson.annotations.SerializedName */ public data class SSOCredentials( /** - * The Session Token used for web SSO . + * The token used for web SSO . * - * @return the Session Token. + * @return the web sso Token. */ - @field:SerializedName("access_token") public val sessionToken: String, + @field:SerializedName("access_token") public val webSsoToken: String, /** - * Type of the token issued.In this case, an Auth0 session token + * Type of the token issued.In this case, an Auth0 web sso token * * @return the issued token type. */ @@ -37,10 +37,10 @@ public data class SSOCredentials( @field:SerializedName("token_type") public val tokenType: String, /** - * Expiration duration of the session token in seconds. Session tokens are short-lived and expire after a few minutes. - * Once expired, the Session Token can no longer be used for SSO. + * Expiration duration of the web sso token in seconds. Web SSO tokens are short-lived and expire after a few minutes. + * Once expired, the web sso token can no longer be used for SSO. * - * @return the expiration duration of this Session Token + * @return the expiration duration of this web sso token */ @field:SerializedName("expires_in") public val expiresIn: Int, diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index 3ea781de0..b64e3ddce 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -2355,10 +2355,10 @@ public class AuthenticationAPIClientTest { } @Test - public fun shouldFetchSessionToken(){ + public fun shouldFetchWebSsoToken(){ mockAPI.willReturnSuccessfulLogin() val callback = MockAuthenticationCallback() - client.fetchSessionToken( "refresh-token") + client.fetchWebSsoToken( "refresh-token") .start(callback) ShadowLooper.idleMainLooper() val request = mockAPI.takeRequest() @@ -2385,9 +2385,9 @@ public class AuthenticationAPIClientTest { } @Test - public fun shouldFetchSessionTokenSync(){ + public fun shouldFetchWebSsoTokenSync(){ mockAPI.willReturnSuccessfulLogin() - val ssoCredentials= client.fetchSessionToken( "refresh-token") + val ssoCredentials= client.fetchWebSsoToken( "refresh-token") .execute() val request = mockAPI.takeRequest() assertThat( @@ -2410,10 +2410,10 @@ public class AuthenticationAPIClientTest { @Test @ExperimentalCoroutinesApi - public fun shouldAwaitFetchSessionToken(): Unit = runTest { + public fun shouldAwaitFetchWebSsoToken(): Unit = runTest { mockAPI.willReturnSuccessfulLogin() val ssoCredentials = client - .fetchSessionToken("refresh-token") + .fetchWebSsoToken("refresh-token") .await() val request = mockAPI.takeRequest() assertThat( diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt index 44b1353ce..30465ae65 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt @@ -277,11 +277,11 @@ public class CredentialsManagerTest { public fun shouldSaveTheNewRefreshTokenWhenGettingTheSSOCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token_old") - Mockito.`when`(client.fetchSessionToken("refresh_token_old")) + Mockito.`when`(client.fetchWebSsoToken("refresh_token_old")) .thenReturn(ssoCredentialsRequest) Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( SsoCredentialsMock.create( - "session-token", "issued-token-type", "token-type", "refresh-token", 60 + "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 ) ) manager.getSsoCredentials(ssoCallback) @@ -289,8 +289,8 @@ public class CredentialsManagerTest { ssoCredentialsCaptor.capture() ) val credentials = ssoCredentialsCaptor.firstValue - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`("session-token")) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) @@ -302,7 +302,7 @@ public class CredentialsManagerTest { public fun shouldFailOnGetNewSSOCredentialsWhenRefreshTokenExpired() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken") Mockito.`when`( - client.fetchSessionToken("refreshToken") + client.fetchWebSsoToken("refreshToken") ).thenReturn(ssoCredentialsRequest) //Trigger failure val authenticationException = AuthenticationException( @@ -343,16 +343,16 @@ public class CredentialsManagerTest { public fun shouldSaveNewRefreshingTokenOnAwaitSsoCredentials(): Unit = runTest { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token_old") - Mockito.`when`(client.fetchSessionToken("refresh_token_old")) + Mockito.`when`(client.fetchWebSsoToken("refresh_token_old")) .thenReturn(ssoCredentialsRequest) Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( SsoCredentialsMock.create( - "session-token", "issued-token-type", "token-type", "refresh-token", 60 + "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 ) ) val credentials = manager.awaitSsoCredentials() - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`("session-token")) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt index 2de21a2e8..0dc46897c 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt @@ -328,11 +328,11 @@ public class SecureCredentialsManagerTest { @Test public fun shouldFetchTheNewRefreshTokenOnGetSSoCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionToken("refreshToken")) + Mockito.`when`(client.fetchWebSsoToken("refreshToken")) .thenReturn(ssoCredentialsRequest) Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( SsoCredentialsMock.create( - "session-token", "issued-token-type", "token-type", "refresh-token", 60 + "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 ) ) insertTestCredentials( @@ -357,8 +357,8 @@ public class SecureCredentialsManagerTest { ssoCredentialsCaptor.capture() ) val credentials = ssoCredentialsCaptor.firstValue - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`("session-token")) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) @@ -375,7 +375,7 @@ public class SecureCredentialsManagerTest { @Test public fun shouldFailWhenRefreshTokenExpiredOnGetSsoCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionToken("refreshToken")) + Mockito.`when`(client.fetchWebSsoToken("refreshToken")) .thenReturn(ssoCredentialsRequest) insertTestCredentials( hasIdToken = true, @@ -473,11 +473,11 @@ public class SecureCredentialsManagerTest { @ExperimentalCoroutinesApi public fun shouldFetchTheNewRefreshTokenOnAwaitSSoCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionToken("refreshToken")) + Mockito.`when`(client.fetchWebSsoToken("refreshToken")) .thenReturn(ssoCredentialsRequest) Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( SsoCredentialsMock.create( - "session-token", "issued-token-type", "token-type", "refresh-token", 60 + "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 ) ) insertTestCredentials( @@ -500,8 +500,8 @@ public class SecureCredentialsManagerTest { val credentials = runBlocking { manager.awaitSsoCredentials() } - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.sessionToken, Is.`is`("session-token")) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) @@ -519,7 +519,7 @@ public class SecureCredentialsManagerTest { @ExperimentalCoroutinesApi public fun shouldFailWhenRefreshTokenExpiredOnAwaitSsoCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionToken("refreshToken")) + Mockito.`when`(client.fetchWebSsoToken("refreshToken")) .thenReturn(ssoCredentialsRequest) insertTestCredentials( hasIdToken = true, From 79d3b7a3dd6ca50891a3aec1848ecbf106f2717d Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 27 Feb 2025 15:06:39 +0530 Subject: [PATCH 2/8] Updated the examples.md file --- EXAMPLES.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 7f77b403f..0e8798f3e 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -18,6 +18,7 @@ - [Sign Up with a database connection](#sign-up-with-a-database-connection) - [Get user information](#get-user-information) - [Custom Token Exchange](#custom-token-exchange) + - [Native to Web SSO login](#native-to-web-sso-login) - [Credentials Manager](#credentials-manager) - [Secure Credentials Manager](#secure-credentials-manager) - [Usage](#usage) @@ -540,6 +541,64 @@ authentication +## Native to Web SSO login (Experimental) +> **Warning** +> +> Native to Web SSO login support in Auth0.Android is still experimental and can change in the future. + +This feature allows you to authenticate a user in a web session using the refresh token obtained from the native session without requiring the user to log in again. + +Call the api to fetch a webSsoToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` end point. + +```kotlin + authentication + .fetchWebSsoToken("refresh_token") + .start(object : Callback { + override fun onSuccess(result: SSOCredentials) { + // Use the web_sso token to authenticate the user in a web session in your app + } + + override fun onFailure(exception: AuthenticationException) { + // Handle error + } + + }) +``` + +
+ Using coroutines + +``` kotlin +try { + val ssoCredentials = authentication + .fetchWebSsoToken("refresh_token") + .await() +} catch (e: AuthenticationException) { + e.printStacktrace() +} +``` +
+ +
+ Using Java + +```java +authentication + .fetchWebSsoToken("refresh_token") + .start(new Callback() { + @Override + public void onSuccess(@Nullable SSOCredentials payload) { + // Handle success + } + @Override + public void onFailure(@NonNull AuthenticationException error) { + // Handle error + } + }); +``` +
+ + ## Credentials Manager ### Secure Credentials Manager From 22de15c04e8dd8967d6c846951ec345857168495 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Mon, 10 Mar 2025 14:19:51 +0530 Subject: [PATCH 3/8] made the store method of sso internal and added support to pass headers --- .../storage/BaseCredentialsManager.kt | 19 +++- .../storage/CredentialsManager.kt | 88 ++++++++------ .../storage/SecureCredentialsManager.kt | 107 +++++++++++------- .../storage/SecureCredentialsManagerTest.kt | 18 +-- 4 files changed, 148 insertions(+), 84 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt index 042f629b2..88dfcfc6a 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt @@ -30,9 +30,16 @@ public abstract class BaseCredentialsManager internal constructor( @Throws(CredentialsManagerException::class) public abstract fun saveCredentials(credentials: Credentials) - public abstract fun saveSsoCredentials(ssoCredentials: SSOCredentials) public abstract fun getCredentials(callback: Callback) - public abstract fun getSsoCredentials(callback: Callback) + public abstract fun getSsoCredentials( + headers: Map = emptyMap(), + callback: Callback + ) + + public abstract fun getSsoCredentials( + callback: Callback + ) + public abstract fun getCredentials( scope: String?, minTtl: Int, @@ -65,7 +72,13 @@ public abstract class BaseCredentialsManager internal constructor( @JvmSynthetic @Throws(CredentialsManagerException::class) - public abstract suspend fun awaitSsoCredentials(): SSOCredentials + public abstract suspend fun awaitSsoCredentials(headers: Map) + : SSOCredentials + + @JvmSynthetic + @Throws(CredentialsManagerException::class) + public abstract suspend fun awaitSsoCredentials() + : SSOCredentials @JvmSynthetic @Throws(CredentialsManagerException::class) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt index 264d764f4..26a5daca0 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt @@ -55,37 +55,28 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting storage.store(LEGACY_KEY_CACHE_EXPIRES_AT, credentials.expiresAt.time) } - /** - * Stores the given [SSOCredentials] refresh token in the storage. - * This method must be called if the SSOCredentials are obtained by directly invoking [AuthenticationAPIClient.fetchWebSsoToken] api and - * [rotating refresh token](https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation) are enabled for - * the client. Method will silently return ,if the passed credentials has no refresh token. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. - * - * @param ssoCredentials the credentials to save in the storage. + * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, + * if a new one is issued. + * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @ExperimentalAuth0Api - override fun saveSsoCredentials(ssoCredentials: SSOCredentials) { - if (ssoCredentials.refreshToken.isNullOrEmpty()) - return // No refresh token to save - serialExecutor.execute { - val existingRefreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) - // Checking if the existing one needs to be replaced with the new one - if (ssoCredentials.refreshToken == existingRefreshToken) - return@execute - storage.store(KEY_REFRESH_TOKEN, ssoCredentials.refreshToken) - } + override fun getSsoCredentials(callback: Callback) { + getSsoCredentials(emptyMap(), callback) } /** * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. + * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @ExperimentalAuth0Api - override fun getSsoCredentials(callback: Callback) { + override fun getSsoCredentials( + headers: Map, + callback: Callback + ) { serialExecutor.execute { val refreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) if (refreshToken.isNullOrEmpty()) { @@ -93,10 +84,12 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting return@execute } + val request = authenticationClient.fetchWebSsoToken(refreshToken) try { - val sessionCredentials = - authenticationClient.fetchWebSsoToken(refreshToken) - .execute() + for (header in headers) { + request.addHeader(header.key, header.value) + } + val sessionCredentials = request.execute() saveSsoCredentials(sessionCredentials) callback.onSuccess(sessionCredentials) } catch (error: AuthenticationException) { @@ -121,22 +114,36 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. + * This is still an experimental feature, test it thoroughly and OS variants and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) @ExperimentalAuth0Api override suspend fun awaitSsoCredentials(): SSOCredentials { + return awaitSsoCredentials(emptyMap()) + } + + /** + * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, + * if a new one is issued. + * This is still an experimental feature, test it thoroughly and OS variants and let us know your feedback. + */ + @JvmSynthetic + @Throws(CredentialsManagerException::class) + @ExperimentalAuth0Api + override suspend fun awaitSsoCredentials(headers: Map): SSOCredentials { return suspendCancellableCoroutine { continuation -> - getSsoCredentials(object : Callback { - override fun onSuccess(result: SSOCredentials) { - continuation.resume(result) - } + getSsoCredentials(headers, + object : Callback { + override fun onSuccess(result: SSOCredentials) { + continuation.resume(result) + } - override fun onFailure(error: CredentialsManagerException) { - continuation.resumeWithException(error) - } - }) + override fun onFailure(error: CredentialsManagerException) { + continuation.resumeWithException(error) + } + }) } } @@ -465,6 +472,23 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting storage.remove(LEGACY_KEY_CACHE_EXPIRES_AT) } + /** + * Helper method to store the given [SSOCredentials] refresh token in the storage. + * Method will silently return ,if the passed credentials has no refresh token. + * + * @param ssoCredentials the credentials to save in the storage. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun saveSsoCredentials(ssoCredentials: SSOCredentials) { + if (ssoCredentials.refreshToken.isNullOrEmpty()) + return // No refresh token to save + val existingRefreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) + // Checking if the existing one needs to be replaced with the new one + if (ssoCredentials.refreshToken == existingRefreshToken) + return // Same refresh token, no need to save + storage.store(KEY_REFRESH_TOKEN, ssoCredentials.refreshToken) + } + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal fun recreateCredentials( idToken: String, diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt index 455a27ce5..0b707f77e 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt @@ -130,47 +130,32 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } /** - * Stores the given [SSOCredentials] refresh token in the storage. - * This method must be called if the SSOCredentials are obtained by directly invoking [AuthenticationAPIClient.fetchWebSsoToken] api and - * [rotating refresh token](https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation) are enabled for - * the client. Method will silently return ,if the passed credentials has no refresh token. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. - * - * @param ssoCredentials the credentials to save in the storage. + * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, + * if a new one is issued. + * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @ExperimentalAuth0Api - override fun saveSsoCredentials(ssoCredentials: SSOCredentials) { - if (ssoCredentials.refreshToken.isNullOrEmpty()) return // No refresh token to save - serialExecutor.execute { - lateinit var existingCredentials: Credentials - try { - existingCredentials = getExistingCredentials() - } catch (exception: CredentialsManagerException) { - Log.e(TAG,"Error while fetching existing credentials", exception) - return@execute - } - // Checking if the existing one needs to be replaced with the new one - if (existingCredentials.refreshToken == ssoCredentials.refreshToken) - return@execute - val newCredentials = - existingCredentials.copy(refreshToken = ssoCredentials.refreshToken) - saveCredentials(newCredentials) - } + override fun getSsoCredentials(callback: Callback) { + getSsoCredentials(emptyMap(), callback) } /** * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. + * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @ExperimentalAuth0Api - override fun getSsoCredentials(callback: Callback) { + override fun getSsoCredentials( + headers: Map, + callback: Callback + ) { serialExecutor.execute { - lateinit var existingCredentials:Credentials - try{ + lateinit var existingCredentials: Credentials + try { existingCredentials = getExistingCredentials() - }catch (exception:CredentialsManagerException){ + } catch (exception: CredentialsManagerException) { callback.onFailure(exception) return@execute } @@ -178,10 +163,14 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT callback.onFailure(CredentialsManagerException.NO_REFRESH_TOKEN) return@execute } + + val request = authenticationClient.fetchWebSsoToken(existingCredentials.refreshToken!!) try { + for (header in headers) { + request.addHeader(header.key, header.value) + } val sessionCredentials = - authenticationClient.fetchWebSsoToken(existingCredentials.refreshToken!!) - .execute() + request.execute() saveSsoCredentials(sessionCredentials) callback.onSuccess(sessionCredentials) } catch (error: AuthenticationException) { @@ -211,22 +200,36 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. + * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) @ExperimentalAuth0Api override suspend fun awaitSsoCredentials(): SSOCredentials { + return awaitSsoCredentials(emptyMap()) + } + + /** + * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, + * if a new one is issued. + * This is still an experimental feature, test it thoroughly and let us know your feedback. + */ + @JvmSynthetic + @Throws(CredentialsManagerException::class) + @ExperimentalAuth0Api + override suspend fun awaitSsoCredentials(headers: Map): SSOCredentials { return suspendCancellableCoroutine { continuation -> - getSsoCredentials(object : Callback { - override fun onSuccess(result: SSOCredentials) { - continuation.resume(result) - } + getSsoCredentials(headers, + object : Callback { + override fun onSuccess(result: SSOCredentials) { + continuation.resume(result) + } - override fun onFailure(error: CredentialsManagerException) { - continuation.resumeWithException(error) - } - }) + override fun onFailure(error: CredentialsManagerException) { + continuation.resumeWithException(error) + } + }) } } @@ -763,6 +766,30 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT fragmentActivity!!.clear() } + /** + * Helper method to stores the given [SSOCredentials] refresh token in the storage. + * Method will silently return ,if the passed credentials has no refresh token. + * + * @param ssoCredentials the credentials to save in the storage. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun saveSsoCredentials(ssoCredentials: SSOCredentials) { + if (ssoCredentials.refreshToken.isNullOrEmpty()) return // No refresh token to save + lateinit var existingCredentials: Credentials + try { + existingCredentials = getExistingCredentials() + } catch (exception: CredentialsManagerException) { + Log.e(TAG, "Error while fetching existing credentials", exception) + return + } + // Checking if the existing one needs to be replaced with the new one + if (existingCredentials.refreshToken == ssoCredentials.refreshToken) + return + val newCredentials = + existingCredentials.copy(refreshToken = ssoCredentials.refreshToken) + saveCredentials(newCredentials) + } + internal companion object { private val TAG = SecureCredentialsManager::class.java.simpleName diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt index 0dc46897c..72a52e2ee 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt @@ -1859,32 +1859,32 @@ public class SecureCredentialsManagerTest { ) secureCredsManager.getCredentials(object : Callback { - override fun onFailure(exception: CredentialsManagerException) { - throw exception + override fun onFailure(error: CredentialsManagerException) { + throw error } - override fun onSuccess(credentials: Credentials) { + override fun onSuccess(result: Credentials) { // Verify all instances retrieved the same credentials MatcherAssert.assertThat( renewedCredentials.accessToken, - Is.`is`(credentials.accessToken) + Is.`is`(result.accessToken) ) MatcherAssert.assertThat( renewedCredentials.idToken, - Is.`is`(credentials.idToken) + Is.`is`(result.idToken) ) MatcherAssert.assertThat( renewedCredentials.refreshToken, - Is.`is`(credentials.refreshToken) + Is.`is`(result.refreshToken) ) - MatcherAssert.assertThat(renewedCredentials.type, Is.`is`(credentials.type)) + MatcherAssert.assertThat(renewedCredentials.type, Is.`is`(result.type)) MatcherAssert.assertThat( renewedCredentials.expiresAt, - Is.`is`(credentials.expiresAt) + Is.`is`(result.expiresAt) ) MatcherAssert.assertThat( renewedCredentials.scope, - Is.`is`(credentials.scope) + Is.`is`(result.scope) ) latch.countDown() } From 963874064fd44fa16a20fb06b2a449383aeb8790 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Mon, 10 Mar 2025 14:54:56 +0530 Subject: [PATCH 4/8] Minor change --- .../android/authentication/storage/BaseCredentialsManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt index 88dfcfc6a..e43c733a6 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt @@ -32,7 +32,7 @@ public abstract class BaseCredentialsManager internal constructor( public abstract fun saveCredentials(credentials: Credentials) public abstract fun getCredentials(callback: Callback) public abstract fun getSsoCredentials( - headers: Map = emptyMap(), + headers: Map, callback: Callback ) From bd87bebe55e461fad6ee8da6f5b6c3f800383b1e Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 13 Mar 2025 14:39:05 +0530 Subject: [PATCH 5/8] updated the session token type to the newly defined one --- .../android/authentication/AuthenticationAPIClient.kt | 2 +- .../auth0/android/authentication/ParameterBuilder.kt | 2 +- .../authentication/storage/BaseCredentialsManager.kt | 4 ++-- .../authentication/storage/CredentialsManager.kt | 10 +++++----- .../authentication/storage/SecureCredentialsManager.kt | 10 +++++----- .../java/com/auth0/android/result/SSOCredentials.kt | 7 ++++++- .../authentication/AuthenticationAPIClientTest.kt | 6 +++--- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index 5b3003ea5..2719f6232 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -938,7 +938,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe .setGrantType(ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) .set(SUBJECT_TOKEN_KEY, refreshToken) .set(SUBJECT_TOKEN_TYPE_KEY, ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN) - .set(REQUESTED_TOKEN_TYPE_KEY, ParameterBuilder.TOKEN_TYPE_SESSION_TOKEN) + .set(REQUESTED_TOKEN_TYPE_KEY, ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN) .asDictionary() return loginWithTokenGeneric(params) } diff --git a/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt b/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt index f92546895..1959f1ad4 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt @@ -161,7 +161,7 @@ public class ParameterBuilder private constructor(parameters: Map) public abstract fun getSsoCredentials( - headers: Map, + parameters: Map, callback: Callback ) @@ -72,7 +72,7 @@ public abstract class BaseCredentialsManager internal constructor( @JvmSynthetic @Throws(CredentialsManagerException::class) - public abstract suspend fun awaitSsoCredentials(headers: Map) + public abstract suspend fun awaitSsoCredentials(parameters: Map) : SSOCredentials @JvmSynthetic diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt index 26a5daca0..5877219c1 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt @@ -74,7 +74,7 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting */ @ExperimentalAuth0Api override fun getSsoCredentials( - headers: Map, + parameters: Map, callback: Callback ) { serialExecutor.execute { @@ -86,8 +86,8 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting val request = authenticationClient.fetchWebSsoToken(refreshToken) try { - for (header in headers) { - request.addHeader(header.key, header.value) + if (parameters.isNotEmpty()) { + request.addParameters(parameters) } val sessionCredentials = request.execute() saveSsoCredentials(sessionCredentials) @@ -132,9 +132,9 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting @JvmSynthetic @Throws(CredentialsManagerException::class) @ExperimentalAuth0Api - override suspend fun awaitSsoCredentials(headers: Map): SSOCredentials { + override suspend fun awaitSsoCredentials(parameters: Map): SSOCredentials { return suspendCancellableCoroutine { continuation -> - getSsoCredentials(headers, + getSsoCredentials(parameters, object : Callback { override fun onSuccess(result: SSOCredentials) { continuation.resume(result) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt index 0b707f77e..390077a6d 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt @@ -148,7 +148,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT */ @ExperimentalAuth0Api override fun getSsoCredentials( - headers: Map, + parameters: Map, callback: Callback ) { serialExecutor.execute { @@ -166,8 +166,8 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT val request = authenticationClient.fetchWebSsoToken(existingCredentials.refreshToken!!) try { - for (header in headers) { - request.addHeader(header.key, header.value) + if (parameters.isNotEmpty()) { + request.addParameters(parameters) } val sessionCredentials = request.execute() @@ -218,9 +218,9 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT @JvmSynthetic @Throws(CredentialsManagerException::class) @ExperimentalAuth0Api - override suspend fun awaitSsoCredentials(headers: Map): SSOCredentials { + override suspend fun awaitSsoCredentials(parameters: Map): SSOCredentials { return suspendCancellableCoroutine { continuation -> - getSsoCredentials(headers, + getSsoCredentials(parameters, object : Callback { override fun onSuccess(result: SSOCredentials) { continuation.resume(result) diff --git a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt index 2e2bdd714..1ae22723c 100644 --- a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt +++ b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt @@ -51,4 +51,9 @@ public data class SSOCredentials( * @return the Refresh Token. */ @field:SerializedName("refresh_token") public val refreshToken: String? = null -) \ No newline at end of file +) { + + override fun toString(): String { + return "SSOCredentials(webSsoToken = ****, issuedTokenType = $issuedTokenType, tokenType = $tokenType, expiresIn = $expiresIn, refreshToken = ****)" + } +} \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index b64e3ddce..89724fdce 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -2376,7 +2376,7 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasEntry("subject_token", "refresh-token")) assertThat(body, Matchers.hasEntry("subject_token_type", ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN)) - assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TOKEN)) + assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN)) assertThat( callback, AuthenticationCallbackMatcher.hasPayloadOfType( SSOCredentials::class.java @@ -2404,7 +2404,7 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasEntry("subject_token", "refresh-token")) assertThat(body, Matchers.hasEntry("subject_token_type", ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN)) - assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TOKEN)) + assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN)) assertThat(ssoCredentials, Matchers.`is`(Matchers.notNullValue())) } @@ -2430,7 +2430,7 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasEntry("subject_token", "refresh-token")) assertThat(body, Matchers.hasEntry("subject_token_type", ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN)) - assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TOKEN)) + assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN)) assertThat(ssoCredentials, Matchers.`is`(Matchers.notNullValue())) } From d98240c795e69819904edc08a89fc7dfa1ca6488 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 10 Apr 2025 14:24:31 +0530 Subject: [PATCH 6/8] Changes to align the api with the new server contract --- EXAMPLES.md | 23 ++- .../authentication/AuthenticationAPIClient.kt | 19 +- .../storage/BaseCredentialsManager.kt | 18 +- .../storage/CredentialsManager.kt | 66 ++++--- .../storage/SecureCredentialsManager.kt | 164 ++++++------------ .../android/provider/WebClientProvider.kt | 27 +++ ...tials.kt => SessionTransferCredentials.kt} | 14 +- .../AuthenticationAPIClientTest.kt | 49 +++--- .../storage/CredentialsManagerTest.kt | 110 ++++++------ .../storage/SecureCredentialsManagerTest.kt | 145 ++++++++-------- ...k.kt => SessionTransferCredentialsMock.kt} | 9 +- 11 files changed, 313 insertions(+), 331 deletions(-) create mode 100644 auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt rename auth0/src/main/java/com/auth0/android/result/{SSOCredentials.kt => SessionTransferCredentials.kt} (78%) rename auth0/src/test/java/com/auth0/android/result/{SsoCredentialsMock.kt => SessionTransferCredentialsMock.kt} (53%) diff --git a/EXAMPLES.md b/EXAMPLES.md index b2f0a55a3..c60c3717b 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -541,20 +541,17 @@ authentication -## Native to Web SSO login (Experimental) -> **Warning** -> -> Native to Web SSO login support in Auth0.Android is still experimental and can change in the future. +## Native to Web SSO login This feature allows you to authenticate a user in a web session using the refresh token obtained from the native session without requiring the user to log in again. -Call the api to fetch a webSsoToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` end point. +Call the api to fetch a webSessionTransferToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` end point. ```kotlin authentication - .fetchWebSsoToken("refresh_token") - .start(object : Callback { - override fun onSuccess(result: SSOCredentials) { + .fetchSessionTransferToken("refresh_token") + .start(object : Callback { + override fun onSuccess(result: SessionTransferCredentials) { // Use the web_sso token to authenticate the user in a web session in your app } @@ -570,8 +567,8 @@ Call the api to fetch a webSsoToken in exchange for a refresh token. Use the obt ``` kotlin try { - val ssoCredentials = authentication - .fetchWebSsoToken("refresh_token") + val sessionTransferCredentials = authentication + .fetchSessionTransferToken("refresh_token") .await() } catch (e: AuthenticationException) { e.printStacktrace() @@ -584,10 +581,10 @@ try { ```java authentication - .fetchWebSsoToken("refresh_token") - .start(new Callback() { + .fetchSessionTransferToken("refresh_token") + .start(new Callback() { @Override - public void onSuccess(@Nullable SSOCredentials payload) { + public void onSuccess(@Nullable SessionTransferCredentials result) { // Handle success } @Override diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index 2719f6232..a9fbfc554 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -4,7 +4,6 @@ import androidx.annotation.VisibleForTesting import com.auth0.android.Auth0 import com.auth0.android.Auth0Exception import com.auth0.android.NetworkErrorException -import com.auth0.android.annotation.ExperimentalAuth0Api import com.auth0.android.request.* import com.auth0.android.request.internal.* import com.auth0.android.request.internal.GsonAdapter.Companion.forMap @@ -15,7 +14,7 @@ import com.auth0.android.result.Credentials import com.auth0.android.result.DatabaseUser import com.auth0.android.result.PasskeyChallenge import com.auth0.android.result.PasskeyRegistrationChallenge -import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SessionTransferCredentials import com.auth0.android.result.UserProfile import com.google.gson.Gson import okhttp3.HttpUrl.Companion.toHttpUrl @@ -925,28 +924,24 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe /** * Creates a new request to fetch a web sso token in exchange for a refresh token. - * This is still an experimental feature, test it thoroughly in the targeted devices and OS variants and let us know your feedback. * * @param refreshToken A valid refresh token obtained as part of Auth0 authentication * @return a request to fetch a web sso token * */ - @ExperimentalAuth0Api - public fun fetchWebSsoToken(refreshToken: String): Request { + public fun fetchSessionTransferToken(refreshToken: String): Request { val params = ParameterBuilder.newBuilder() - .setClientId(clientId) - .setGrantType(ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) - .set(SUBJECT_TOKEN_KEY, refreshToken) - .set(SUBJECT_TOKEN_TYPE_KEY, ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN) - .set(REQUESTED_TOKEN_TYPE_KEY, ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN) + .setGrantType(ParameterBuilder.REFRESH_TOKEN_KEY) + .setAudience("urn:${auth0.domain}:session_transfer") + .set(ParameterBuilder.REFRESH_TOKEN_KEY, refreshToken) .asDictionary() - return loginWithTokenGeneric(params) + return loginWithTokenGeneric(params) } /** * Helper function to make a request to the /oauth/token endpoint with a custom response type. */ - private inline fun loginWithTokenGeneric(parameters: Map): Request { + private inline fun loginWithTokenGeneric(parameters: Map): Request { val url = auth0.getDomainUrl().toHttpUrl().newBuilder() .addPathSegment(OAUTH_PATH) .addPathSegment(TOKEN_PATH) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt index d82ed818d..0ef563a1b 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.kt @@ -4,7 +4,7 @@ import androidx.annotation.VisibleForTesting import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.callback.Callback import com.auth0.android.result.Credentials -import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SessionTransferCredentials import com.auth0.android.util.Clock import java.util.* @@ -31,13 +31,13 @@ public abstract class BaseCredentialsManager internal constructor( @Throws(CredentialsManagerException::class) public abstract fun saveCredentials(credentials: Credentials) public abstract fun getCredentials(callback: Callback) - public abstract fun getSsoCredentials( + public abstract fun getSessionTransferCredentials( parameters: Map, - callback: Callback + callback: Callback ) - public abstract fun getSsoCredentials( - callback: Callback + public abstract fun getSessionTransferCredentials( + callback: Callback ) public abstract fun getCredentials( @@ -72,13 +72,13 @@ public abstract class BaseCredentialsManager internal constructor( @JvmSynthetic @Throws(CredentialsManagerException::class) - public abstract suspend fun awaitSsoCredentials(parameters: Map) - : SSOCredentials + public abstract suspend fun awaitSessionTransferCredentials(parameters: Map) + : SessionTransferCredentials @JvmSynthetic @Throws(CredentialsManagerException::class) - public abstract suspend fun awaitSsoCredentials() - : SSOCredentials + public abstract suspend fun awaitSessionTransferCredentials() + : SessionTransferCredentials @JvmSynthetic @Throws(CredentialsManagerException::class) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt index 5877219c1..67cc3995a 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt @@ -2,12 +2,11 @@ package com.auth0.android.authentication.storage import android.text.TextUtils import androidx.annotation.VisibleForTesting -import com.auth0.android.annotation.ExperimentalAuth0Api import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.Callback import com.auth0.android.result.Credentials -import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SessionTransferCredentials import kotlinx.coroutines.suspendCancellableCoroutine import java.util.* import java.util.concurrent.Executor @@ -56,26 +55,22 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and let us know your feedback. */ - @ExperimentalAuth0Api - override fun getSsoCredentials(callback: Callback) { - getSsoCredentials(emptyMap(), callback) + override fun getSessionTransferCredentials(callback: Callback) { + getSessionTransferCredentials(emptyMap(), callback) } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and let us know your feedback. */ - @ExperimentalAuth0Api - override fun getSsoCredentials( + override fun getSessionTransferCredentials( parameters: Map, - callback: Callback + callback: Callback ) { serialExecutor.execute { val refreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) @@ -84,14 +79,14 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting return@execute } - val request = authenticationClient.fetchWebSsoToken(refreshToken) + val request = authenticationClient.fetchSessionTransferToken(refreshToken) try { if (parameters.isNotEmpty()) { request.addParameters(parameters) } - val sessionCredentials = request.execute() - saveSsoCredentials(sessionCredentials) - callback.onSuccess(sessionCredentials) + val sessionTransferCredentials = request.execute() + saveSessionTransferCredentials(sessionTransferCredentials) + callback.onSuccess(sessionTransferCredentials) } catch (error: AuthenticationException) { val exception = when { error.isRefreshTokenDeleted || @@ -111,32 +106,29 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and OS variants and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - @ExperimentalAuth0Api - override suspend fun awaitSsoCredentials(): SSOCredentials { - return awaitSsoCredentials(emptyMap()) + override suspend fun awaitSessionTransferCredentials(): SessionTransferCredentials { + return awaitSessionTransferCredentials(emptyMap()) } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and OS variants and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - @ExperimentalAuth0Api - override suspend fun awaitSsoCredentials(parameters: Map): SSOCredentials { + override suspend fun awaitSessionTransferCredentials(parameters: Map): SessionTransferCredentials { return suspendCancellableCoroutine { continuation -> - getSsoCredentials(parameters, - object : Callback { - override fun onSuccess(result: SSOCredentials) { + getSessionTransferCredentials( + parameters, + object : Callback { + override fun onSuccess(result: SessionTransferCredentials) { continuation.resume(result) } @@ -238,7 +230,8 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting forceRefresh: Boolean ): Credentials { return suspendCancellableCoroutine { continuation -> - getCredentials(scope, + getCredentials( + scope, minTtl, parameters, headers, @@ -473,20 +466,21 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } /** - * Helper method to store the given [SSOCredentials] refresh token in the storage. + * Helper method to store the given [SessionTransferCredentials] refresh token in the storage. * Method will silently return ,if the passed credentials has no refresh token. * - * @param ssoCredentials the credentials to save in the storage. + * @param sessionTransferCredentials the credentials to save in the storage. */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun saveSsoCredentials(ssoCredentials: SSOCredentials) { - if (ssoCredentials.refreshToken.isNullOrEmpty()) - return // No refresh token to save + internal fun saveSessionTransferCredentials(sessionTransferCredentials: SessionTransferCredentials) { + storage.store(KEY_ID_TOKEN, sessionTransferCredentials.idToken) val existingRefreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) // Checking if the existing one needs to be replaced with the new one - if (ssoCredentials.refreshToken == existingRefreshToken) + if (sessionTransferCredentials.refreshToken.isNullOrEmpty()) + return // No refresh token to save + if (sessionTransferCredentials.refreshToken == existingRefreshToken) return // Same refresh token, no need to save - storage.store(KEY_REFRESH_TOKEN, ssoCredentials.refreshToken) + storage.store(KEY_REFRESH_TOKEN, sessionTransferCredentials.refreshToken) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt index 390077a6d..9c69fedf9 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt @@ -7,14 +7,13 @@ import android.util.Log import androidx.annotation.VisibleForTesting import androidx.fragment.app.FragmentActivity import com.auth0.android.Auth0 -import com.auth0.android.annotation.ExperimentalAuth0Api import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.Callback import com.auth0.android.request.internal.GsonProvider import com.auth0.android.result.Credentials import com.auth0.android.result.OptionalCredentials -import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SessionTransferCredentials import com.google.gson.Gson import kotlinx.coroutines.suspendCancellableCoroutine import java.lang.ref.WeakReference @@ -112,44 +111,37 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT storage.store(KEY_CAN_REFRESH, canRefresh) } catch (e: IncompatibleDeviceException) { throw CredentialsManagerException( - CredentialsManagerException.Code.INCOMPATIBLE_DEVICE, - e + CredentialsManagerException.Code.INCOMPATIBLE_DEVICE, e ) - } catch (e: CryptoException) { - /* + } catch (e: CryptoException) {/* * If the keys were invalidated in the call above a good new pair is going to be available * to use on the next call. We clear any existing credentials so #hasValidCredentials returns * a true value. Retrying this operation will succeed. */ clearCredentials() throw CredentialsManagerException( - CredentialsManagerException.Code.CRYPTO_EXCEPTION, - e + CredentialsManagerException.Code.CRYPTO_EXCEPTION, e ) } } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and let us know your feedback. */ - @ExperimentalAuth0Api - override fun getSsoCredentials(callback: Callback) { - getSsoCredentials(emptyMap(), callback) + override fun getSessionTransferCredentials(callback: Callback) { + getSessionTransferCredentials(emptyMap(), callback) } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and let us know your feedback. */ - @ExperimentalAuth0Api - override fun getSsoCredentials( + override fun getSessionTransferCredentials( parameters: Map, - callback: Callback + callback: Callback ) { serialExecutor.execute { lateinit var existingCredentials: Credentials @@ -164,27 +156,25 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT return@execute } - val request = authenticationClient.fetchWebSsoToken(existingCredentials.refreshToken!!) + val request = + authenticationClient.fetchSessionTransferToken(existingCredentials.refreshToken!!) try { if (parameters.isNotEmpty()) { request.addParameters(parameters) } - val sessionCredentials = - request.execute() - saveSsoCredentials(sessionCredentials) + val sessionCredentials = request.execute() + saveSessionTransferCredentials(sessionCredentials) callback.onSuccess(sessionCredentials) } catch (error: AuthenticationException) { val exception = when { - error.isRefreshTokenDeleted || - error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED + error.isRefreshTokenDeleted || error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED error.isNetworkError -> CredentialsManagerException.Code.NO_NETWORK else -> CredentialsManagerException.Code.API_ERROR } callback.onFailure( CredentialsManagerException( - exception, - error + exception, error ) ) } catch (error: CredentialsManagerException) { @@ -197,32 +187,29 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - @ExperimentalAuth0Api - override suspend fun awaitSsoCredentials(): SSOCredentials { - return awaitSsoCredentials(emptyMap()) + override suspend fun awaitSessionTransferCredentials(): SessionTransferCredentials { + return awaitSessionTransferCredentials(emptyMap()) } /** - * Fetches a new [SSOCredentials] . It will fail with [CredentialsManagerException] + * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, * if a new one is issued. - * This is still an experimental feature, test it thoroughly and let us know your feedback. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - @ExperimentalAuth0Api - override suspend fun awaitSsoCredentials(parameters: Map): SSOCredentials { + override suspend fun awaitSessionTransferCredentials(parameters: Map): SessionTransferCredentials { return suspendCancellableCoroutine { continuation -> - getSsoCredentials(parameters, - object : Callback { - override fun onSuccess(result: SSOCredentials) { + getSessionTransferCredentials( + parameters, + object : Callback { + override fun onSuccess(result: SessionTransferCredentials) { continuation.resume(result) } @@ -264,8 +251,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT @JvmSynthetic @Throws(CredentialsManagerException::class) override suspend fun awaitCredentials( - scope: String?, - minTtl: Int + scope: String?, minTtl: Int ): Credentials { return awaitCredentials(scope, minTtl, emptyMap()) } @@ -286,15 +272,10 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT @JvmSynthetic @Throws(CredentialsManagerException::class) override suspend fun awaitCredentials( - scope: String?, - minTtl: Int, - parameters: Map + scope: String?, minTtl: Int, parameters: Map ): Credentials { return awaitCredentials( - scope, - minTtl, - parameters, - false + scope, minTtl, parameters, false ) } @@ -321,11 +302,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT forceRefresh: Boolean, ): Credentials { return awaitCredentials( - scope, - minTtl, - parameters, - mapOf(), - forceRefresh + scope, minTtl, parameters, mapOf(), forceRefresh ) } @@ -401,9 +378,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT * @param callback the callback to receive the result in. */ override fun getCredentials( - scope: String?, - minTtl: Int, - callback: Callback + scope: String?, minTtl: Int, callback: Callback ) { getCredentials(scope, minTtl, emptyMap(), callback) } @@ -428,11 +403,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT callback: Callback ) { getCredentials( - scope, - minTtl, - parameters, - false, - callback + scope, minTtl, parameters, false, callback ) } @@ -459,12 +430,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT callback: Callback ) { getCredentials( - scope, - minTtl, - parameters, - mapOf(), - forceRefresh, - callback + scope, minTtl, parameters, mapOf(), forceRefresh, callback ) } @@ -502,12 +468,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT activity = fragmentActivity, authenticationOptions = localAuthenticationOptions, resultCallback = localAuthenticationResultCallback( - scope, - minTtl, - parameters, - headers, - forceRefresh, - callback + scope, minTtl, parameters, headers, forceRefresh, callback ) ) localAuthenticationManager.authenticate() @@ -525,8 +486,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT object : Callback { override fun onSuccess(result: Boolean) { continueGetCredentials( - scope, minTtl, parameters, headers, forceRefresh, - callback + scope, minTtl, parameters, headers, forceRefresh, callback ) } @@ -572,10 +532,8 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT val canRefresh = storage.retrieveBoolean(KEY_CAN_REFRESH) val emptyCredentials = TextUtils.isEmpty(encryptedEncoded) return !(emptyCredentials || willExpire( - expiresAt, - minTtl - ) && - (canRefresh == null || !canRefresh)) + expiresAt, minTtl + ) && (canRefresh == null || !canRefresh)) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @@ -600,8 +558,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } catch (e: IncompatibleDeviceException) { callback.onFailure( CredentialsManagerException( - CredentialsManagerException.Code.INCOMPATIBLE_DEVICE, - e + CredentialsManagerException.Code.INCOMPATIBLE_DEVICE, e ) ) return@execute @@ -610,14 +567,12 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT clearCredentials() callback.onFailure( CredentialsManagerException( - CredentialsManagerException.Code.CRYPTO_EXCEPTION, - e + CredentialsManagerException.Code.CRYPTO_EXCEPTION, e ) ) return@execute } - val bridgeCredentials = gson.fromJson(json, OptionalCredentials::class.java) - /* OPTIONAL CREDENTIALS + val bridgeCredentials = gson.fromJson(json, OptionalCredentials::class.java)/* OPTIONAL CREDENTIALS * This bridge is required to prevent users from being logged out when * migrating from Credentials with optional Access Token and ID token */ @@ -668,8 +623,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT if (willAccessTokenExpire) { val tokenLifetime = (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 val wrongTtlException = CredentialsManagerException( - CredentialsManagerException.Code.LARGE_MIN_TTL, - String.format( + CredentialsManagerException.Code.LARGE_MIN_TTL, String.format( Locale.getDefault(), "The lifetime of the renewed Access Token (%d) is less than the minTTL requested (%d). Increase the 'Token Expiration' setting of your Auth0 API in the dashboard, or request a lower minTTL.", tokenLifetime, @@ -693,16 +647,14 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT ) } catch (error: AuthenticationException) { val exception = when { - error.isRefreshTokenDeleted || - error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED + error.isRefreshTokenDeleted || error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED error.isNetworkError -> CredentialsManagerException.Code.NO_NETWORK else -> CredentialsManagerException.Code.API_ERROR } callback.onFailure( CredentialsManagerException( - exception, - error + exception, error ) ) return@execute @@ -735,18 +687,15 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT throw CredentialsManagerException.NO_CREDENTIALS } val encrypted = Base64.decode(encryptedEncoded, Base64.DEFAULT) - val json: String - try { - json = String(crypto.decrypt(encrypted)) + val json: String = try { + String(crypto.decrypt(encrypted)) } catch (e: IncompatibleDeviceException) { throw CredentialsManagerException( - CredentialsManagerException.Code.INCOMPATIBLE_DEVICE, - e + CredentialsManagerException.Code.INCOMPATIBLE_DEVICE, e ) } catch (e: CryptoException) { throw CredentialsManagerException( - CredentialsManagerException.Code.CRYPTO_EXCEPTION, - e + CredentialsManagerException.Code.CRYPTO_EXCEPTION, e ) } val bridgeCredentials = gson.fromJson(json, OptionalCredentials::class.java) @@ -767,26 +716,25 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } /** - * Helper method to stores the given [SSOCredentials] refresh token in the storage. + * Helper method to stores the given [SessionTransferCredentials] refresh token in the storage. * Method will silently return ,if the passed credentials has no refresh token. * - * @param ssoCredentials the credentials to save in the storage. + * @param sessionTransferCredentials the credentials to save in the storage. */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun saveSsoCredentials(ssoCredentials: SSOCredentials) { - if (ssoCredentials.refreshToken.isNullOrEmpty()) return // No refresh token to save - lateinit var existingCredentials: Credentials - try { - existingCredentials = getExistingCredentials() + internal fun saveSessionTransferCredentials(sessionTransferCredentials: SessionTransferCredentials) { + val existingCredentials: Credentials = try { + getExistingCredentials() } catch (exception: CredentialsManagerException) { Log.e(TAG, "Error while fetching existing credentials", exception) return } // Checking if the existing one needs to be replaced with the new one - if (existingCredentials.refreshToken == ssoCredentials.refreshToken) - return - val newCredentials = - existingCredentials.copy(refreshToken = ssoCredentials.refreshToken) + if (existingCredentials.refreshToken == sessionTransferCredentials.refreshToken && existingCredentials.idToken == sessionTransferCredentials.idToken) return + val newCredentials = existingCredentials.copy( + refreshToken = sessionTransferCredentials.refreshToken + ?: existingCredentials.refreshToken, idToken = sessionTransferCredentials.idToken + ) saveCredentials(newCredentials) } diff --git a/auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt b/auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt new file mode 100644 index 000000000..5ae240d59 --- /dev/null +++ b/auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt @@ -0,0 +1,27 @@ +package com.auth0.android.provider + +import android.webkit.CookieManager +import android.webkit.WebView +import com.auth0.android.Auth0 + +/** + * Provider class to handle native to web sso + */ +public object WebClientProvider { + private const val TAG = "WebSSOProvider" + + public fun configureSSOWebView( + account: Auth0, + url: String, + webView: WebView, + sessionToken: String, + ): WebView { + val cookieManager = CookieManager.getInstance() + cookieManager.setAcceptCookie(true) + cookieManager.setCookie( + url, + "session_token=$sessionToken; path=/" + ) + return webView + } +} \ No newline at end of file diff --git a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt b/auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt similarity index 78% rename from auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt rename to auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt index 1ae22723c..905c246e8 100644 --- a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt +++ b/auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt @@ -12,13 +12,20 @@ import com.google.gson.annotations.SerializedName * * *issuedTokenType*: Type of the token issued. * */ -public data class SSOCredentials( +public data class SessionTransferCredentials( /** * The token used for web SSO . * * @return the web sso Token. */ - @field:SerializedName("access_token") public val webSsoToken: String, + @field:SerializedName("access_token") public val sessionTransferToken: String, + + /** + * Getter for the Identity Token with user information. + * + * @return the Identity Token. + */ + @field:SerializedName("id_token") public val idToken: String, /** * Type of the token issued.In this case, an Auth0 web sso token @@ -44,7 +51,6 @@ public data class SSOCredentials( */ @field:SerializedName("expires_in") public val expiresIn: Int, - /** * Refresh Token that can be used to request new tokens without signing in again. * @@ -54,6 +60,6 @@ public data class SSOCredentials( ) { override fun toString(): String { - return "SSOCredentials(webSsoToken = ****, issuedTokenType = $issuedTokenType, tokenType = $tokenType, expiresIn = $expiresIn, refreshToken = ****)" + return "SSOCredentials(sessionTransferToken = ****, issuedTokenType = $issuedTokenType, tokenType = $tokenType, expiresIn = $expiresIn, refreshToken = ****)" } } \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index 89724fdce..f65668b09 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -16,7 +16,7 @@ import com.auth0.android.result.Authentication import com.auth0.android.result.Challenge import com.auth0.android.result.Credentials import com.auth0.android.result.DatabaseUser -import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SessionTransferCredentials import com.auth0.android.result.UserProfile import com.auth0.android.util.Auth0UserAgent import com.auth0.android.util.AuthenticationAPIMockServer @@ -2277,7 +2277,7 @@ public class AuthenticationAPIClientTest { public fun shouldCustomTokenExchange() { mockAPI.willReturnSuccessfulLogin() val callback = MockAuthenticationCallback() - client.customTokenExchange( "subject-token-type","subject-token") + client.customTokenExchange("subject-token-type", "subject-token") .start(callback) ShadowLooper.idleMainLooper() val request = mockAPI.takeRequest() @@ -2355,10 +2355,10 @@ public class AuthenticationAPIClientTest { } @Test - public fun shouldFetchWebSsoToken(){ + public fun shouldFetchSessionTransferToken() { mockAPI.willReturnSuccessfulLogin() - val callback = MockAuthenticationCallback() - client.fetchWebSsoToken( "refresh-token") + val callback = MockAuthenticationCallback() + client.fetchSessionTransferToken("refresh-token") .start(callback) ShadowLooper.idleMainLooper() val request = mockAPI.takeRequest() @@ -2372,22 +2372,24 @@ public class AuthenticationAPIClientTest { assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID)) assertThat( body, - Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) + Matchers.hasEntry("grant_type", ParameterBuilder.REFRESH_TOKEN_KEY) + ) + assertThat(body, Matchers.hasEntry("audience", "urn:${auth0.domain}:session_transfer")) + assertThat( + body, + Matchers.hasEntry("refresh_token", "refresh-token") ) - assertThat(body, Matchers.hasEntry("subject_token", "refresh-token")) - assertThat(body, Matchers.hasEntry("subject_token_type", ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN)) - assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN)) assertThat( callback, AuthenticationCallbackMatcher.hasPayloadOfType( - SSOCredentials::class.java + SessionTransferCredentials::class.java ) ) } @Test - public fun shouldFetchWebSsoTokenSync(){ + public fun shouldFetchSessionTransferTokenSync() { mockAPI.willReturnSuccessfulLogin() - val ssoCredentials= client.fetchWebSsoToken( "refresh-token") + val sessionTransferCredentials = client.fetchSessionTransferToken("refresh-token") .execute() val request = mockAPI.takeRequest() assertThat( @@ -2400,20 +2402,19 @@ public class AuthenticationAPIClientTest { assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID)) assertThat( body, - Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) + Matchers.hasEntry("grant_type", ParameterBuilder.REFRESH_TOKEN_KEY) ) - assertThat(body, Matchers.hasEntry("subject_token", "refresh-token")) - assertThat(body, Matchers.hasEntry("subject_token_type", ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN)) - assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN)) - assertThat(ssoCredentials, Matchers.`is`(Matchers.notNullValue())) + assertThat(body, Matchers.hasEntry("audience", "urn:${auth0.domain}:session_transfer")) + assertThat(body, Matchers.hasEntry("refresh_token", "refresh-token")) + assertThat(sessionTransferCredentials, Matchers.`is`(Matchers.notNullValue())) } @Test @ExperimentalCoroutinesApi - public fun shouldAwaitFetchWebSsoToken(): Unit = runTest { + public fun shouldAwaitFetchSessionTransferToken(): Unit = runTest { mockAPI.willReturnSuccessfulLogin() val ssoCredentials = client - .fetchWebSsoToken("refresh-token") + .fetchSessionTransferToken("refresh-token") .await() val request = mockAPI.takeRequest() assertThat( @@ -2426,11 +2427,13 @@ public class AuthenticationAPIClientTest { assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID)) assertThat( body, - Matchers.hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE) + Matchers.hasEntry("grant_type", ParameterBuilder.REFRESH_TOKEN_KEY) + ) + assertThat(body, Matchers.hasEntry("refresh_token", "refresh-token")) + assertThat( + body, + Matchers.hasEntry("audience", "urn:${auth0.domain}:session_transfer") ) - assertThat(body, Matchers.hasEntry("subject_token", "refresh-token")) - assertThat(body, Matchers.hasEntry("subject_token_type", ParameterBuilder.TOKEN_TYPE_REFRESH_TOKEN)) - assertThat(body, Matchers.hasEntry("requested_token_type", ParameterBuilder.TOKEN_TYPE_SESSION_TRANSFER_TOKEN)) assertThat(ssoCredentials, Matchers.`is`(Matchers.notNullValue())) } diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt index 30465ae65..ddc3d737e 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt @@ -8,8 +8,8 @@ import com.auth0.android.request.Request import com.auth0.android.request.internal.Jwt import com.auth0.android.result.Credentials import com.auth0.android.result.CredentialsMock -import com.auth0.android.result.SSOCredentials -import com.auth0.android.result.SsoCredentialsMock +import com.auth0.android.result.SessionTransferCredentials +import com.auth0.android.result.SessionTransferCredentialsMock import com.auth0.android.util.Clock import com.nhaarman.mockitokotlin2.KArgumentCaptor import com.nhaarman.mockitokotlin2.any @@ -57,10 +57,10 @@ public class CredentialsManagerTest { private lateinit var request: Request @Mock - private lateinit var ssoCredentialsRequest: Request + private lateinit var sessionTransferCredentialsRequest: Request @Mock - private lateinit var ssoCallback: Callback + private lateinit var ssoCallback: Callback @Mock private lateinit var jwtDecoder: JWTDecoder @@ -71,7 +71,7 @@ public class CredentialsManagerTest { private val exceptionCaptor: KArgumentCaptor = argumentCaptor() - private val ssoCredentialsCaptor: KArgumentCaptor = argumentCaptor() + private val sessionTransferCredentialsCaptor: KArgumentCaptor = argumentCaptor() @get:Rule public var exception: ExpectedException = ExpectedException.none() @@ -222,46 +222,46 @@ public class CredentialsManagerTest { } @Test - public fun shouldNotSaveIfTheSsoCredentialsHasNoRefreshToken() { - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", - "issuedTokenType", "tokenType", null,60 - ) - manager.saveSsoCredentials(ssoCredentials) + public fun shouldNotSaveIfTheSessionTransferCredentialsHasNoRefreshToken() { verifyZeroInteractions(storage) + val ssoCredentials = SessionTransferCredentialsMock.create( + "accessToken", "identityToken", + "issuedTokenType", "tokenType", null, 60 + ) + manager.saveSessionTransferCredentials(ssoCredentials) } @Test - public fun shouldNotSaveIfTheNewSsoCredentialRefreshTokenIsSameAsTheExistingOne() { + public fun shouldNotSaveIfTheNewSessionTransferCredentialRefreshTokenIsSameAsTheExistingOne() { verifyNoMoreInteractions(storage) - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", - "issuedTokenType", "tokenType", "refresh_token",60 + val ssoCredentials = SessionTransferCredentialsMock.create( + "accessToken", "identityToken", + "issuedTokenType", "tokenType", "refresh_token", 60 ) Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token") - manager.saveSsoCredentials(ssoCredentials) + manager.saveSessionTransferCredentials(ssoCredentials) verify(storage, times(0)).store("com.auth0.refresh_token", "refresh_token") } @Test - public fun shouldSaveTheRefreshTokenIfTheNewSsoRefreshTokenIsNotSameAsTheOldOne() { + public fun shouldSaveTheRefreshTokenIfTheNewSessionTransferRefreshTokenIsNotSameAsTheOldOne() { verifyNoMoreInteractions(storage) - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", - "issuedTokenType", "tokenType", "refresh_token",60 + val ssoCredentials = SessionTransferCredentialsMock.create( + "accessToken", "identityToken", + "issuedTokenType", "tokenType", "refresh_token", 60 ) Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh-token") - manager.saveSsoCredentials(ssoCredentials) + manager.saveSessionTransferCredentials(ssoCredentials) verify(storage).store("com.auth0.refresh_token", "refresh_token") } @Test - public fun shouldThrowExceptionIfNoExistingRefreshTokenExistWhenGettingSSOCredentials() { + public fun shouldThrowExceptionIfNoExistingRefreshTokenExistWhenGettingSessionTransferCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn(null) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -274,23 +274,28 @@ public class CredentialsManagerTest { } @Test - public fun shouldSaveTheNewRefreshTokenWhenGettingTheSSOCredentials() { + public fun shouldSaveTheNewRefreshTokenWhenGettingTheSessionTransferCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token_old") - Mockito.`when`(client.fetchWebSsoToken("refresh_token_old")) - .thenReturn(ssoCredentialsRequest) - Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( - SsoCredentialsMock.create( - "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 + Mockito.`when`(client.fetchSessionTransferToken("refresh_token_old")) + .thenReturn(sessionTransferCredentialsRequest) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( + SessionTransferCredentialsMock.create( + "web-sso-token", + "identity-token", + "issued-token-type", + "token-type", + "refresh-token", + 60 ) ) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onSuccess( - ssoCredentialsCaptor.capture() + sessionTransferCredentialsCaptor.capture() ) - val credentials = ssoCredentialsCaptor.firstValue - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) + val credentials = sessionTransferCredentialsCaptor.firstValue + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) @@ -299,18 +304,18 @@ public class CredentialsManagerTest { } @Test - public fun shouldFailOnGetNewSSOCredentialsWhenRefreshTokenExpired() { + public fun shouldFailOnGetNewSessionTransferCredentialsWhenRefreshTokenExpired() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken") Mockito.`when`( - client.fetchWebSsoToken("refreshToken") - ).thenReturn(ssoCredentialsRequest) + client.fetchSessionTransferToken("refreshToken") + ).thenReturn(sessionTransferCredentialsRequest) //Trigger failure val authenticationException = AuthenticationException( "invalid_grant", "Unknown or invalid refresh token." ) - Mockito.`when`(ssoCredentialsRequest.execute()).thenThrow(authenticationException) - manager.getSsoCredentials(ssoCallback) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenThrow(authenticationException) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -325,11 +330,11 @@ public class CredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldFailOnAwaitSsoCredentialsWhenNoRefreshTokenWasSaved(): Unit = runTest { + public fun shouldFailOnAwaitSessionTransferCredentialsWhenNoRefreshTokenWasSaved(): Unit = runTest { verifyNoMoreInteractions(client) Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn(null) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSsoCredentials() } + runBlocking { manager.awaitSessionTransferCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -340,19 +345,24 @@ public class CredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldSaveNewRefreshingTokenOnAwaitSsoCredentials(): Unit = runTest { + public fun shouldSaveNewRefreshingTokenOnAwaitSessionTransferCredentials(): Unit = runTest { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token_old") - Mockito.`when`(client.fetchWebSsoToken("refresh_token_old")) - .thenReturn(ssoCredentialsRequest) - Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( - SsoCredentialsMock.create( - "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 + Mockito.`when`(client.fetchSessionTransferToken("refresh_token_old")) + .thenReturn(sessionTransferCredentialsRequest) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( + SessionTransferCredentialsMock.create( + "web-sso-token", + "identity-token", + "issued-token-type", + "token-type", + "refresh-token", + 60 ) ) - val credentials = manager.awaitSsoCredentials() - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) + val credentials = manager.awaitSessionTransferCredentials() + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt index 72a52e2ee..09e9a7762 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt @@ -15,8 +15,8 @@ import com.auth0.android.request.internal.GsonProvider import com.auth0.android.request.internal.Jwt import com.auth0.android.result.Credentials import com.auth0.android.result.CredentialsMock -import com.auth0.android.result.SSOCredentials -import com.auth0.android.result.SsoCredentialsMock +import com.auth0.android.result.SessionTransferCredentials +import com.auth0.android.result.SessionTransferCredentialsMock import com.auth0.android.util.Clock import com.google.gson.Gson import com.nhaarman.mockitokotlin2.KArgumentCaptor @@ -74,13 +74,13 @@ public class SecureCredentialsManagerTest { private lateinit var callback: Callback @Mock - private lateinit var ssoCallback: Callback + private lateinit var ssoCallback: Callback @Mock private lateinit var request: Request @Mock - private lateinit var ssoCredentialsRequest: Request + private lateinit var sessionTransferCredentialsRequest: Request @Mock private lateinit var crypto: CryptoUtil @@ -103,7 +103,7 @@ public class SecureCredentialsManagerTest { private val credentialsCaptor: KArgumentCaptor = argumentCaptor() private val exceptionCaptor: KArgumentCaptor = argumentCaptor() - private val ssoCredentialsCaptor: KArgumentCaptor = argumentCaptor() + private val sessionTransferCredentialsCaptor: KArgumentCaptor = argumentCaptor() private val stringCaptor: KArgumentCaptor = argumentCaptor() @@ -170,23 +170,14 @@ public class SecureCredentialsManagerTest { /* - * SAVE SSO credentials test + * SAVE SessionTransfer credentials test */ - @Test - public fun shouldNotSaveIfTheSsoCredentialsHasNoRefreshToken() { - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", - "issuedTokenType", "tokenType", null, 60 - ) - manager.saveSsoCredentials(ssoCredentials) - verifyZeroInteractions(storage) - } @Test public fun shouldNotSaveIfThereIsErrorInGettingTheExistingCredentials() { verifyNoMoreInteractions(storage) - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", + val ssoCredentials = SessionTransferCredentialsMock.create( + "accessToken", "identityToken", "issuedTokenType", "tokenType", "refresh_token", 60 ) val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) @@ -199,16 +190,16 @@ public class SecureCredentialsManagerTest { ) Mockito.`when`(crypto.decrypt(storedJson.toByteArray())) .thenThrow(CredentialsManagerException.NO_CREDENTIALS) - manager.saveSsoCredentials(ssoCredentials) + manager.saveSessionTransferCredentials(ssoCredentials) verify(storage, times(0)).store("com.auth0.credentials", storedJson) verify(storage, times(0)).store("com.auth0.credentials_can_refresh", true) } @Test - public fun shouldNotSaveIfTheNewSsoCredentialRefreshTokenIsSameAsTheExistingOne() { + public fun shouldNotSaveIfTheNewSessionTokenCredentialRefreshAndIdTokenAreSameAsTheExistingOne() { verifyNoMoreInteractions(storage) - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", + val sessionTransferCredentials = SessionTransferCredentialsMock.create( + "accessToken", "idToken", "issuedTokenType", "tokenType", "refreshToken", 60 ) val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) @@ -219,16 +210,16 @@ public class SecureCredentialsManagerTest { willExpireAt = expiresAt, scope = "scope" ) - manager.saveSsoCredentials(ssoCredentials) + manager.saveSessionTransferCredentials(sessionTransferCredentials) verify(storage, times(0)).store("com.auth0.credentials", storedJson) verify(storage, times(0)).store("com.auth0.credentials_can_refresh", true) } @Test - public fun shouldSaveIfTheNewSsoCredentialRefreshTokenIsNotSameAsTheExistingOne() { + public fun shouldSaveIfTheNewSessionTransferCredentialRefreshAndIdTokenIsNotSameAsTheExistingOne() { verifyNoMoreInteractions(storage) - val ssoCredentials = SsoCredentialsMock.create( - "accessToken", + val sessionTransferCredentials = SessionTransferCredentialsMock.create( + "accessToken", "identityToken", "issuedTokenType", "tokenType", "refresh_token", 60 ) val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) @@ -240,7 +231,7 @@ public class SecureCredentialsManagerTest { scope = "scope" ) val newCredentials = CredentialsMock.create( - "idToken", + "identityToken", "accessToken", "type", "refresh_token", @@ -249,25 +240,25 @@ public class SecureCredentialsManagerTest { ) val json = gson.toJson(newCredentials) Mockito.`when`(crypto.encrypt(any())).thenReturn(json.toByteArray()) - manager.saveSsoCredentials(ssoCredentials) + manager.saveSessionTransferCredentials(sessionTransferCredentials) verify(storage).store(eq("com.auth0.credentials"), stringCaptor.capture()) val encodedJson = stringCaptor.firstValue MatcherAssert.assertThat(encodedJson, Is.`is`(Matchers.notNullValue())) val decoded = Base64.decode(encodedJson, Base64.DEFAULT) val storedCredentials = gson.fromJson(String(decoded), Credentials::class.java) MatcherAssert.assertThat(storedCredentials.accessToken, Is.`is`("accessToken")) - MatcherAssert.assertThat(storedCredentials.idToken, Is.`is`("idToken")) MatcherAssert.assertThat(storedCredentials.refreshToken, Is.`is`("refresh_token")) + MatcherAssert.assertThat(storedCredentials.idToken, Is.`is`("identityToken")) } /* - * GET SSO credentials test + * GET Session Transfer credentials test */ @Test - public fun shouldThrowExceptionIfNoCredentialsExistOnGetSsoCredentials() { + public fun shouldThrowExceptionIfNoCredentialsExistOnGetSessionTransferCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.credentials")) .thenReturn(null) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -280,7 +271,7 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnGetSsoCredentials() { + public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnGetSessionTransferCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) val storedJson = insertTestCredentials( hasIdToken = true, @@ -291,7 +282,7 @@ public class SecureCredentialsManagerTest { ) Mockito.`when`(crypto.decrypt(storedJson.toByteArray())) .thenThrow(CredentialsManagerException.CRYPTO_EXCEPTION) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -304,7 +295,7 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnGetSsoCredentials() { + public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnGetSessionTransferCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) insertTestCredentials( hasIdToken = true, @@ -313,7 +304,7 @@ public class SecureCredentialsManagerTest { willExpireAt = expiresAt, scope = "scope" ) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -326,13 +317,18 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldFetchTheNewRefreshTokenOnGetSSoCredentials() { + public fun shouldFetchTheNewRefreshTokenOnGetSessionTransferCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchWebSsoToken("refreshToken")) - .thenReturn(ssoCredentialsRequest) - Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( - SsoCredentialsMock.create( - "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 + Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) + .thenReturn(sessionTransferCredentialsRequest) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( + SessionTransferCredentialsMock.create( + "web-sso-token", + "identity-token", + "issued-token-type", + "token-type", + "refresh-token", + 60 ) ) insertTestCredentials( @@ -352,13 +348,13 @@ public class SecureCredentialsManagerTest { ) val json = gson.toJson(newCredentials) Mockito.`when`(crypto.encrypt(any())).thenReturn(json.toByteArray()) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onSuccess( - ssoCredentialsCaptor.capture() + sessionTransferCredentialsCaptor.capture() ) - val credentials = ssoCredentialsCaptor.firstValue - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) + val credentials = sessionTransferCredentialsCaptor.firstValue + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) @@ -373,10 +369,10 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldFailWhenRefreshTokenExpiredOnGetSsoCredentials() { + public fun shouldFailWhenRefreshTokenExpiredOnGetSessionTransferCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchWebSsoToken("refreshToken")) - .thenReturn(ssoCredentialsRequest) + Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) + .thenReturn(sessionTransferCredentialsRequest) insertTestCredentials( hasIdToken = true, hasAccessToken = true, @@ -389,8 +385,8 @@ public class SecureCredentialsManagerTest { "invalid_grant", "Unknown or invalid refresh token." ) - Mockito.`when`(ssoCredentialsRequest.execute()).thenThrow(authenticationException) - manager.getSsoCredentials(ssoCallback) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenThrow(authenticationException) + manager.getSessionTransferCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -408,12 +404,12 @@ public class SecureCredentialsManagerTest { */ @Test @ExperimentalCoroutinesApi - public fun shouldFailWhenNoExistingCredentialsWasSavedOnAwaitSsoCredentials(): Unit = runTest { + public fun shouldFailWhenNoExistingCredentialsWasSavedOnAwaitSessionTransferCredentials(): Unit = runTest { verifyNoMoreInteractions(client) Mockito.`when`(storage.retrieveString("com.auth0.credentials")) .thenReturn(null) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSsoCredentials() } + runBlocking { manager.awaitSessionTransferCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -424,7 +420,7 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnAwaitSsoCredentials(): Unit = + public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnAwaitSessionTransferCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) val storedJson = insertTestCredentials( @@ -437,7 +433,7 @@ public class SecureCredentialsManagerTest { Mockito.`when`(crypto.decrypt(storedJson.toByteArray())) .thenThrow(CredentialsManagerException.CRYPTO_EXCEPTION) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSsoCredentials() } + runBlocking { manager.awaitSessionTransferCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -448,7 +444,7 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnAwaitSsoCredentials(): Unit = + public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnAwaitSessionTransferCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) insertTestCredentials( @@ -458,9 +454,9 @@ public class SecureCredentialsManagerTest { willExpireAt = expiresAt, scope = "scope" ) - manager.getSsoCredentials(ssoCallback) + manager.getSessionTransferCredentials(ssoCallback) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSsoCredentials() } + runBlocking { manager.awaitSessionTransferCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -471,13 +467,18 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldFetchTheNewRefreshTokenOnAwaitSSoCredentials(): Unit = runTest { + public fun shouldFetchTheNewRefreshTokenOnAwaitSessionTransferCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchWebSsoToken("refreshToken")) - .thenReturn(ssoCredentialsRequest) - Mockito.`when`(ssoCredentialsRequest.execute()).thenReturn( - SsoCredentialsMock.create( - "web-sso-token", "issued-token-type", "token-type", "refresh-token", 60 + Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) + .thenReturn(sessionTransferCredentialsRequest) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( + SessionTransferCredentialsMock.create( + "web-sso-token", + "identity-token", + "issued-token-type", + "token-type", + "refresh-token", + 60 ) ) insertTestCredentials( @@ -498,10 +499,10 @@ public class SecureCredentialsManagerTest { val json = gson.toJson(newCredentials) Mockito.`when`(crypto.encrypt(any())).thenReturn(json.toByteArray()) val credentials = runBlocking { - manager.awaitSsoCredentials() + manager.awaitSessionTransferCredentials() } - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`(Matchers.notNullValue())) - MatcherAssert.assertThat(credentials.webSsoToken, Is.`is`("web-sso-token")) + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) + MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) MatcherAssert.assertThat(credentials.issuedTokenType, Is.`is`("issued-token-type")) MatcherAssert.assertThat(credentials.refreshToken, Is.`is`("refresh-token")) @@ -517,10 +518,10 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldFailWhenRefreshTokenExpiredOnAwaitSsoCredentials(): Unit = runTest { + public fun shouldFailWhenRefreshTokenExpiredOnAwaitSessionTransferCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchWebSsoToken("refreshToken")) - .thenReturn(ssoCredentialsRequest) + Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) + .thenReturn(sessionTransferCredentialsRequest) insertTestCredentials( hasIdToken = true, hasAccessToken = true, @@ -533,9 +534,9 @@ public class SecureCredentialsManagerTest { "invalid_grant", "Unknown or invalid refresh token." ) - Mockito.`when`(ssoCredentialsRequest.execute()).thenThrow(authenticationException) + Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenThrow(authenticationException) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSsoCredentials() } + runBlocking { manager.awaitSessionTransferCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException)) diff --git a/auth0/src/test/java/com/auth0/android/result/SsoCredentialsMock.kt b/auth0/src/test/java/com/auth0/android/result/SessionTransferCredentialsMock.kt similarity index 53% rename from auth0/src/test/java/com/auth0/android/result/SsoCredentialsMock.kt rename to auth0/src/test/java/com/auth0/android/result/SessionTransferCredentialsMock.kt index 47290941b..8b6a6280c 100644 --- a/auth0/src/test/java/com/auth0/android/result/SsoCredentialsMock.kt +++ b/auth0/src/test/java/com/auth0/android/result/SessionTransferCredentialsMock.kt @@ -1,18 +1,19 @@ package com.auth0.android.result -public class SsoCredentialsMock { +public class SessionTransferCredentialsMock { public companion object { public fun create( accessToken: String, + idToken:String , issuedTokenType: String, type: String, refreshToken: String?, expiresIn: Int - ): SSOCredentials { - return SSOCredentials( - accessToken, issuedTokenType, type, expiresIn, refreshToken + ): SessionTransferCredentials { + return SessionTransferCredentials( + accessToken,idToken, issuedTokenType, type, expiresIn, refreshToken ) } } From cd55aa718f20e1404cba1384b0835c0306ecd876 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Wed, 16 Apr 2025 17:03:11 +0530 Subject: [PATCH 7/8] Review comments addressed --- EXAMPLES.md | 20 +-- .../authentication/AuthenticationAPIClient.kt | 16 ++- .../authentication/ParameterBuilder.kt | 3 - .../storage/BaseCredentialsManager.kt | 18 +-- .../storage/CredentialsManager.kt | 95 ++++++++------ .../storage/CredentialsManagerException.kt | 6 +- .../storage/SecureCredentialsManager.kt | 92 ++++++++----- .../auth0/android/result/SSOCredentials.kt | 72 ++++++++++ .../result/SessionTransferCredentials.kt | 65 --------- .../AuthenticationAPIClientTest.kt | 18 +-- .../storage/CredentialsManagerTest.kt | 76 +++++------ .../storage/SecureCredentialsManagerTest.kt | 123 ++++++++---------- ...edentialsMock.kt => SSOCredentialsMock.kt} | 6 +- 13 files changed, 322 insertions(+), 288 deletions(-) create mode 100644 auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt delete mode 100644 auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt rename auth0/src/test/java/com/auth0/android/result/{SessionTransferCredentialsMock.kt => SSOCredentialsMock.kt} (74%) diff --git a/EXAMPLES.md b/EXAMPLES.md index c60c3717b..0c3063280 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -545,14 +545,14 @@ authentication This feature allows you to authenticate a user in a web session using the refresh token obtained from the native session without requiring the user to log in again. -Call the api to fetch a webSessionTransferToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` end point. +Call the API to fetch a webSessionTransferToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` end point by passing as a query parameter or a cookie value. ```kotlin authentication - .fetchSessionTransferToken("refresh_token") - .start(object : Callback { - override fun onSuccess(result: SessionTransferCredentials) { - // Use the web_sso token to authenticate the user in a web session in your app + .ssoExchange("refresh_token") + .start(object : Callback { + override fun onSuccess(result: SSOCredentials) { + // Use the sessionTransferToken token to authenticate the user in a web session in your app } override fun onFailure(exception: AuthenticationException) { @@ -567,8 +567,8 @@ Call the api to fetch a webSessionTransferToken in exchange for a refresh token. ``` kotlin try { - val sessionTransferCredentials = authentication - .fetchSessionTransferToken("refresh_token") + val ssoCredentials = authentication + .ssoExchange("refresh_token") .await() } catch (e: AuthenticationException) { e.printStacktrace() @@ -581,10 +581,10 @@ try { ```java authentication - .fetchSessionTransferToken("refresh_token") - .start(new Callback() { + .ssoExchange("refresh_token") + .start(new Callback() { @Override - public void onSuccess(@Nullable SessionTransferCredentials result) { + public void onSuccess(@Nullable SSOCredentials result) { // Handle success } @Override diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index a9fbfc554..e8dcb63ae 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -14,7 +14,7 @@ import com.auth0.android.result.Credentials import com.auth0.android.result.DatabaseUser import com.auth0.android.result.PasskeyChallenge import com.auth0.android.result.PasskeyRegistrationChallenge -import com.auth0.android.result.SessionTransferCredentials +import com.auth0.android.result.SSOCredentials import com.auth0.android.result.UserProfile import com.google.gson.Gson import okhttp3.HttpUrl.Companion.toHttpUrl @@ -923,19 +923,25 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe } /** - * Creates a new request to fetch a web sso token in exchange for a refresh token. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * * * @param refreshToken A valid refresh token obtained as part of Auth0 authentication - * @return a request to fetch a web sso token + * @return a request to fetch a session transfer token * */ - public fun fetchSessionTransferToken(refreshToken: String): Request { + public fun ssoExchange(refreshToken: String): Request { val params = ParameterBuilder.newBuilder() .setGrantType(ParameterBuilder.REFRESH_TOKEN_KEY) .setAudience("urn:${auth0.domain}:session_transfer") .set(ParameterBuilder.REFRESH_TOKEN_KEY, refreshToken) .asDictionary() - return loginWithTokenGeneric(params) + return loginWithTokenGeneric(params) } /** diff --git a/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt b/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt index 1959f1ad4..a7d5d3532 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/ParameterBuilder.kt @@ -160,9 +160,6 @@ public class ParameterBuilder private constructor(parameters: Map) - public abstract fun getSessionTransferCredentials( + public abstract fun getSsoCredentials( parameters: Map, - callback: Callback + callback: Callback ) - public abstract fun getSessionTransferCredentials( - callback: Callback + public abstract fun getSsoCredentials( + callback: Callback ) public abstract fun getCredentials( @@ -72,13 +72,13 @@ public abstract class BaseCredentialsManager internal constructor( @JvmSynthetic @Throws(CredentialsManagerException::class) - public abstract suspend fun awaitSessionTransferCredentials(parameters: Map) - : SessionTransferCredentials + public abstract suspend fun awaitSsoCredentials(parameters: Map) + : SSOCredentials @JvmSynthetic @Throws(CredentialsManagerException::class) - public abstract suspend fun awaitSessionTransferCredentials() - : SessionTransferCredentials + public abstract suspend fun awaitSsoCredentials() + : SSOCredentials @JvmSynthetic @Throws(CredentialsManagerException::class) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt index 67cc3995a..3afaae7b1 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt @@ -6,7 +6,7 @@ import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.Callback import com.auth0.android.result.Credentials -import com.auth0.android.result.SessionTransferCredentials +import com.auth0.android.result.SSOCredentials import kotlinx.coroutines.suspendCancellableCoroutine import java.util.* import java.util.concurrent.Executor @@ -55,22 +55,34 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ - override fun getSessionTransferCredentials(callback: Callback) { - getSessionTransferCredentials(emptyMap(), callback) + override fun getSsoCredentials(callback: Callback) { + getSsoCredentials(emptyMap(), callback) } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ - override fun getSessionTransferCredentials( + override fun getSsoCredentials( parameters: Map, - callback: Callback + callback: Callback ) { serialExecutor.execute { val refreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) @@ -79,21 +91,18 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting return@execute } - val request = authenticationClient.fetchSessionTransferToken(refreshToken) + val request = authenticationClient.ssoExchange(refreshToken) try { if (parameters.isNotEmpty()) { request.addParameters(parameters) } val sessionTransferCredentials = request.execute() - saveSessionTransferCredentials(sessionTransferCredentials) + saveSsoCredentials(sessionTransferCredentials) callback.onSuccess(sessionTransferCredentials) } catch (error: AuthenticationException) { val exception = when { - error.isRefreshTokenDeleted || - error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED - error.isNetworkError -> CredentialsManagerException.Code.NO_NETWORK - else -> CredentialsManagerException.Code.API_ERROR + else -> CredentialsManagerException.Code.SSO_EXCHANGE_FAILED } callback.onFailure( CredentialsManagerException( @@ -106,29 +115,41 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - override suspend fun awaitSessionTransferCredentials(): SessionTransferCredentials { - return awaitSessionTransferCredentials(emptyMap()) + override suspend fun awaitSsoCredentials(): SSOCredentials { + return awaitSsoCredentials(emptyMap()) } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - override suspend fun awaitSessionTransferCredentials(parameters: Map): SessionTransferCredentials { + override suspend fun awaitSsoCredentials(parameters: Map): SSOCredentials { return suspendCancellableCoroutine { continuation -> - getSessionTransferCredentials( + getSsoCredentials( parameters, - object : Callback { - override fun onSuccess(result: SessionTransferCredentials) { + object : Callback { + override fun onSuccess(result: SSOCredentials) { continuation.resume(result) } @@ -466,21 +487,21 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting } /** - * Helper method to store the given [SessionTransferCredentials] refresh token in the storage. - * Method will silently return ,if the passed credentials has no refresh token. + * Helper method to store the given [SSOCredentials] refresh token in the storage. + * Method will silently return if the passed credentials have no refresh token. * - * @param sessionTransferCredentials the credentials to save in the storage. + * @param ssoCredentials the credentials to save in the storage. */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun saveSessionTransferCredentials(sessionTransferCredentials: SessionTransferCredentials) { - storage.store(KEY_ID_TOKEN, sessionTransferCredentials.idToken) + internal fun saveSsoCredentials(ssoCredentials: SSOCredentials) { + storage.store(KEY_ID_TOKEN, ssoCredentials.idToken) val existingRefreshToken = storage.retrieveString(KEY_REFRESH_TOKEN) // Checking if the existing one needs to be replaced with the new one - if (sessionTransferCredentials.refreshToken.isNullOrEmpty()) + if (ssoCredentials.refreshToken.isNullOrEmpty()) return // No refresh token to save - if (sessionTransferCredentials.refreshToken == existingRefreshToken) + if (ssoCredentials.refreshToken == existingRefreshToken) return // Same refresh token, no need to save - storage.store(KEY_REFRESH_TOKEN, sessionTransferCredentials.refreshToken) + storage.store(KEY_REFRESH_TOKEN, ssoCredentials.refreshToken) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManagerException.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManagerException.kt index 4367b9c02..10210e286 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManagerException.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManagerException.kt @@ -44,7 +44,8 @@ public class CredentialsManagerException : BIOMETRICS_INVALID_USER, BIOMETRIC_AUTHENTICATION_FAILED, NO_NETWORK, - API_ERROR + API_ERROR, + SSO_EXCHANGE_FAILED, } private var code: Code? @@ -142,6 +143,8 @@ public class CredentialsManagerException : CredentialsManagerException(Code.NO_NETWORK) public val API_ERROR: CredentialsManagerException = CredentialsManagerException(Code.API_ERROR) + public val SSO_EXCHANGE_FAILED: CredentialsManagerException = + CredentialsManagerException(Code.SSO_EXCHANGE_FAILED) private fun getMessage(code: Code): String { @@ -187,6 +190,7 @@ public class CredentialsManagerException : Code.BIOMETRIC_AUTHENTICATION_FAILED -> "Biometric authentication failed." Code.NO_NETWORK -> "Failed to execute the network request." Code.API_ERROR -> "An error occurred while processing the request." + Code.SSO_EXCHANGE_FAILED ->"The exchange of the refresh token for SSO credentials failed." } } } diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt index 9c69fedf9..feaf17296 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt @@ -13,7 +13,7 @@ import com.auth0.android.callback.Callback import com.auth0.android.request.internal.GsonProvider import com.auth0.android.result.Credentials import com.auth0.android.result.OptionalCredentials -import com.auth0.android.result.SessionTransferCredentials +import com.auth0.android.result.SSOCredentials import com.google.gson.Gson import kotlinx.coroutines.suspendCancellableCoroutine import java.lang.ref.WeakReference @@ -126,22 +126,34 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ - override fun getSessionTransferCredentials(callback: Callback) { - getSessionTransferCredentials(emptyMap(), callback) + override fun getSsoCredentials(callback: Callback) { + getSsoCredentials(emptyMap(), callback) } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ - override fun getSessionTransferCredentials( + override fun getSsoCredentials( parameters: Map, - callback: Callback + callback: Callback ) { serialExecutor.execute { lateinit var existingCredentials: Credentials @@ -157,20 +169,18 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } val request = - authenticationClient.fetchSessionTransferToken(existingCredentials.refreshToken!!) + authenticationClient.ssoExchange(existingCredentials.refreshToken!!) try { if (parameters.isNotEmpty()) { request.addParameters(parameters) } val sessionCredentials = request.execute() - saveSessionTransferCredentials(sessionCredentials) + saveSsoCredentials(sessionCredentials) callback.onSuccess(sessionCredentials) } catch (error: AuthenticationException) { val exception = when { - error.isRefreshTokenDeleted || error.isInvalidRefreshToken -> CredentialsManagerException.Code.RENEW_FAILED - error.isNetworkError -> CredentialsManagerException.Code.NO_NETWORK - else -> CredentialsManagerException.Code.API_ERROR + else -> CredentialsManagerException.Code.SSO_EXCHANGE_FAILED } callback.onFailure( CredentialsManagerException( @@ -187,29 +197,41 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - override suspend fun awaitSessionTransferCredentials(): SessionTransferCredentials { - return awaitSessionTransferCredentials(emptyMap()) + override suspend fun awaitSsoCredentials(): SSOCredentials { + return awaitSsoCredentials(emptyMap()) } /** - * Fetches a new [SessionTransferCredentials] . It will fail with [CredentialsManagerException] - * if the existing refresh_token is null or no longer valid. This method will handle saving the refresh_token, - * if a new one is issued. + * Creates a new request to exchange a refresh token for a session transfer token that can be used to perform web single sign-on. + * + * When opening your website on any browser or web view, add the session transfer token to the URL as a query + * parameter. Then your website can redirect the user to Auth0's `/authorize` endpoint, passing along the query + * parameter with the session transfer token. For example, + * `https://example.com/login?session_transfer_token=THE_TOKEN`. + * + * It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid. + * This method will handle saving the refresh_token, if a new one is issued. */ @JvmSynthetic @Throws(CredentialsManagerException::class) - override suspend fun awaitSessionTransferCredentials(parameters: Map): SessionTransferCredentials { + override suspend fun awaitSsoCredentials(parameters: Map): SSOCredentials { return suspendCancellableCoroutine { continuation -> - getSessionTransferCredentials( + getSsoCredentials( parameters, - object : Callback { - override fun onSuccess(result: SessionTransferCredentials) { + object : Callback { + override fun onSuccess(result: SSOCredentials) { continuation.resume(result) } @@ -716,24 +738,22 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT } /** - * Helper method to stores the given [SessionTransferCredentials] refresh token in the storage. - * Method will silently return ,if the passed credentials has no refresh token. + * Helper method to stores the given [ssoCredentials] refresh token in the storage. + * Method will silently return if the passed credentials have no refresh token. * - * @param sessionTransferCredentials the credentials to save in the storage. + * @param ssoCredentials the credentials to save in the storage. */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun saveSessionTransferCredentials(sessionTransferCredentials: SessionTransferCredentials) { + internal fun saveSsoCredentials(ssoCredentials: SSOCredentials) { val existingCredentials: Credentials = try { getExistingCredentials() } catch (exception: CredentialsManagerException) { Log.e(TAG, "Error while fetching existing credentials", exception) return } - // Checking if the existing one needs to be replaced with the new one - if (existingCredentials.refreshToken == sessionTransferCredentials.refreshToken && existingCredentials.idToken == sessionTransferCredentials.idToken) return val newCredentials = existingCredentials.copy( - refreshToken = sessionTransferCredentials.refreshToken - ?: existingCredentials.refreshToken, idToken = sessionTransferCredentials.idToken + refreshToken = ssoCredentials.refreshToken + ?: existingCredentials.refreshToken, idToken = ssoCredentials.idToken ) saveCredentials(newCredentials) } diff --git a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt new file mode 100644 index 000000000..8f8abda57 --- /dev/null +++ b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt @@ -0,0 +1,72 @@ +package com.auth0.android.result + +import com.google.gson.annotations.SerializedName + +/** + * Holds the token credentials required for web SSO . + */ +public data class SSOCredentials( + /** + * The token used for web SSO . + * + * @return the session transfer token. + */ + @field:SerializedName("access_token") public val sessionTransferToken: String, + + /** + * Identity Token with user information. + * + * - Important: You must [validate](https://auth0.com/docs/secure/tokens/id-tokens/validate-id-tokens) any ID + * tokens received from the Authentication API client before using the information they contain. + * + * ## See Also + * + * - [ID Tokens](https://auth0.com/docs/secure/tokens/id-tokens) + * - [JSON Web Tokens](https://auth0.com/docs/secure/tokens/json-web-tokens) + * - [jwt.io](https://jwt.io) + * + * @return the Identity Token. + */ + @field:SerializedName("id_token") public val idToken: String, + + /** + * Type of the token issued.In this case, an Auth0 session transfer token + * + * @return the issued token type. + */ + @field:SerializedName("issued_token_type") public val issuedTokenType: String, + + /** + * Contains information about how the token should be used. + * If the issued token is not an access token or usable as an access token, then the token_type + * value N_A is used to indicate that an OAuth 2.0 token_type identifier is not applicable in that context + * + * @return the token type. + */ + @field:SerializedName("token_type") public val tokenType: String, + + /** + * Expiration duration of the session transfer token in seconds. Session transfer tokens are short-lived and expire after a few minutes. + * Once expired, the session transfer tokens can no longer be used for web SSO. + * + * @return the expiration duration of this session transfer token + */ + @field:SerializedName("expires_in") public val expiresIn: Int, + + /** + * Rotated refresh token. Only available when Refresh Token Rotation is enabled. + * - Important: If you're using the Authentication API client directly to perform the SSO exchange, make sure to store this + * new refresh token replacing the previous one. + * + * ## See Also + * - [Refresh Token Rotation](https://auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation) + * + * @return the Refresh Token. + */ + @field:SerializedName("refresh_token") public val refreshToken: String? = null +) { + + override fun toString(): String { + return "SSOCredentials(sessionTransferToken = ****, idToken = ****,issuedTokenType = $issuedTokenType, tokenType = $tokenType, expiresIn = $expiresIn, refreshToken = ****)" + } +} \ No newline at end of file diff --git a/auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt b/auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt deleted file mode 100644 index 905c246e8..000000000 --- a/auth0/src/main/java/com/auth0/android/result/SessionTransferCredentials.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.auth0.android.result - -import com.google.gson.annotations.SerializedName - -/** - * Holds the token credentials required for web SSO . - * - * * *webSsoToken*: Token for web SSO - * * *refreshToken*: Refresh Token that can be used to request new tokens without signing in again - * * *tokenType*: Contains information about how the token should be used. - * * *expiresIn*: The token expiration duration. - * * *issuedTokenType*: Type of the token issued. - * - */ -public data class SessionTransferCredentials( - /** - * The token used for web SSO . - * - * @return the web sso Token. - */ - @field:SerializedName("access_token") public val sessionTransferToken: String, - - /** - * Getter for the Identity Token with user information. - * - * @return the Identity Token. - */ - @field:SerializedName("id_token") public val idToken: String, - - /** - * Type of the token issued.In this case, an Auth0 web sso token - * - * @return the issued token type. - */ - @field:SerializedName("issued_token_type") public val issuedTokenType: String, - - /** - * Contains information about how the token should be used. - * If the issued token is not an access token or usable as an access token, then the token_type - * value N_A is used to indicate that an OAuth 2.0 token_type identifier is not applicable in that context - * - * @return the token type. - */ - @field:SerializedName("token_type") public val tokenType: String, - - /** - * Expiration duration of the web sso token in seconds. Web SSO tokens are short-lived and expire after a few minutes. - * Once expired, the web sso token can no longer be used for SSO. - * - * @return the expiration duration of this web sso token - */ - @field:SerializedName("expires_in") public val expiresIn: Int, - - /** - * Refresh Token that can be used to request new tokens without signing in again. - * - * @return the Refresh Token. - */ - @field:SerializedName("refresh_token") public val refreshToken: String? = null -) { - - override fun toString(): String { - return "SSOCredentials(sessionTransferToken = ****, issuedTokenType = $issuedTokenType, tokenType = $tokenType, expiresIn = $expiresIn, refreshToken = ****)" - } -} \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index f65668b09..ffc78cf66 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -16,7 +16,7 @@ import com.auth0.android.result.Authentication import com.auth0.android.result.Challenge import com.auth0.android.result.Credentials import com.auth0.android.result.DatabaseUser -import com.auth0.android.result.SessionTransferCredentials +import com.auth0.android.result.SSOCredentials import com.auth0.android.result.UserProfile import com.auth0.android.util.Auth0UserAgent import com.auth0.android.util.AuthenticationAPIMockServer @@ -2355,10 +2355,10 @@ public class AuthenticationAPIClientTest { } @Test - public fun shouldFetchSessionTransferToken() { + public fun shouldSsoExchange() { mockAPI.willReturnSuccessfulLogin() - val callback = MockAuthenticationCallback() - client.fetchSessionTransferToken("refresh-token") + val callback = MockAuthenticationCallback() + client.ssoExchange("refresh-token") .start(callback) ShadowLooper.idleMainLooper() val request = mockAPI.takeRequest() @@ -2381,15 +2381,15 @@ public class AuthenticationAPIClientTest { ) assertThat( callback, AuthenticationCallbackMatcher.hasPayloadOfType( - SessionTransferCredentials::class.java + SSOCredentials::class.java ) ) } @Test - public fun shouldFetchSessionTransferTokenSync() { + public fun shouldSsoExchangeSync() { mockAPI.willReturnSuccessfulLogin() - val sessionTransferCredentials = client.fetchSessionTransferToken("refresh-token") + val sessionTransferCredentials = client.ssoExchange("refresh-token") .execute() val request = mockAPI.takeRequest() assertThat( @@ -2411,10 +2411,10 @@ public class AuthenticationAPIClientTest { @Test @ExperimentalCoroutinesApi - public fun shouldAwaitFetchSessionTransferToken(): Unit = runTest { + public fun shouldAwaitSsoExchange(): Unit = runTest { mockAPI.willReturnSuccessfulLogin() val ssoCredentials = client - .fetchSessionTransferToken("refresh-token") + .ssoExchange("refresh-token") .await() val request = mockAPI.takeRequest() assertThat( diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt index ddc3d737e..b12b23601 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt @@ -8,8 +8,8 @@ import com.auth0.android.request.Request import com.auth0.android.request.internal.Jwt import com.auth0.android.result.Credentials import com.auth0.android.result.CredentialsMock -import com.auth0.android.result.SessionTransferCredentials -import com.auth0.android.result.SessionTransferCredentialsMock +import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SSOCredentialsMock import com.auth0.android.util.Clock import com.nhaarman.mockitokotlin2.KArgumentCaptor import com.nhaarman.mockitokotlin2.any @@ -57,10 +57,10 @@ public class CredentialsManagerTest { private lateinit var request: Request @Mock - private lateinit var sessionTransferCredentialsRequest: Request + private lateinit var SSOCredentialsRequest: Request @Mock - private lateinit var ssoCallback: Callback + private lateinit var ssoCallback: Callback @Mock private lateinit var jwtDecoder: JWTDecoder @@ -71,7 +71,7 @@ public class CredentialsManagerTest { private val exceptionCaptor: KArgumentCaptor = argumentCaptor() - private val sessionTransferCredentialsCaptor: KArgumentCaptor = argumentCaptor() + private val SSOCredentialsCaptor: KArgumentCaptor = argumentCaptor() @get:Rule public var exception: ExpectedException = ExpectedException.none() @@ -222,46 +222,46 @@ public class CredentialsManagerTest { } @Test - public fun shouldNotSaveIfTheSessionTransferCredentialsHasNoRefreshToken() { + public fun shouldNotSaveIfTheSSOCredentialsHasNoRefreshToken() { verifyZeroInteractions(storage) - val ssoCredentials = SessionTransferCredentialsMock.create( + val ssoCredentials = SSOCredentialsMock.create( "accessToken", "identityToken", "issuedTokenType", "tokenType", null, 60 ) - manager.saveSessionTransferCredentials(ssoCredentials) + manager.saveSsoCredentials(ssoCredentials) } @Test - public fun shouldNotSaveIfTheNewSessionTransferCredentialRefreshTokenIsSameAsTheExistingOne() { + public fun shouldNotSaveIfTheNewSSOCredentialRefreshTokenIsSameAsTheExistingOne() { verifyNoMoreInteractions(storage) - val ssoCredentials = SessionTransferCredentialsMock.create( + val ssoCredentials = SSOCredentialsMock.create( "accessToken", "identityToken", "issuedTokenType", "tokenType", "refresh_token", 60 ) Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token") - manager.saveSessionTransferCredentials(ssoCredentials) + manager.saveSsoCredentials(ssoCredentials) verify(storage, times(0)).store("com.auth0.refresh_token", "refresh_token") } @Test - public fun shouldSaveTheRefreshTokenIfTheNewSessionTransferRefreshTokenIsNotSameAsTheOldOne() { + public fun shouldSaveTheRefreshTokenIfTheNewSSOCredentialsRefreshTokenIsNotSameAsTheOldOne() { verifyNoMoreInteractions(storage) - val ssoCredentials = SessionTransferCredentialsMock.create( + val ssoCredentials = SSOCredentialsMock.create( "accessToken", "identityToken", "issuedTokenType", "tokenType", "refresh_token", 60 ) Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh-token") - manager.saveSessionTransferCredentials(ssoCredentials) + manager.saveSsoCredentials(ssoCredentials) verify(storage).store("com.auth0.refresh_token", "refresh_token") } @Test - public fun shouldThrowExceptionIfNoExistingRefreshTokenExistWhenGettingSessionTransferCredentials() { + public fun shouldThrowExceptionIfNoExistingRefreshTokenExistWhenGettingSSOCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn(null) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -274,13 +274,13 @@ public class CredentialsManagerTest { } @Test - public fun shouldSaveTheNewRefreshTokenWhenGettingTheSessionTransferCredentials() { + public fun shouldSaveTheNewRefreshTokenWhenGettingTheSSOCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token_old") - Mockito.`when`(client.fetchSessionTransferToken("refresh_token_old")) - .thenReturn(sessionTransferCredentialsRequest) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( - SessionTransferCredentialsMock.create( + Mockito.`when`(client.ssoExchange("refresh_token_old")) + .thenReturn(SSOCredentialsRequest) + Mockito.`when`(SSOCredentialsRequest.execute()).thenReturn( + SSOCredentialsMock.create( "web-sso-token", "identity-token", "issued-token-type", @@ -289,11 +289,11 @@ public class CredentialsManagerTest { 60 ) ) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onSuccess( - sessionTransferCredentialsCaptor.capture() + SSOCredentialsCaptor.capture() ) - val credentials = sessionTransferCredentialsCaptor.firstValue + val credentials = SSOCredentialsCaptor.firstValue MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) @@ -304,18 +304,18 @@ public class CredentialsManagerTest { } @Test - public fun shouldFailOnGetNewSessionTransferCredentialsWhenRefreshTokenExpired() { + public fun shouldFailOnGetNewSSOCredentialsWhenRefreshTokenExpired() { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken") Mockito.`when`( - client.fetchSessionTransferToken("refreshToken") - ).thenReturn(sessionTransferCredentialsRequest) + client.ssoExchange("refreshToken") + ).thenReturn(SSOCredentialsRequest) //Trigger failure val authenticationException = AuthenticationException( "invalid_grant", "Unknown or invalid refresh token." ) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenThrow(authenticationException) - manager.getSessionTransferCredentials(ssoCallback) + Mockito.`when`(SSOCredentialsRequest.execute()).thenThrow(authenticationException) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -324,17 +324,17 @@ public class CredentialsManagerTest { MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException)) MatcherAssert.assertThat( exception.message, - Is.`is`("An error occurred while trying to use the Refresh Token to renew the Credentials.") + Is.`is`("The exchange of the refresh token for SSO credentials failed.") ) } @Test @ExperimentalCoroutinesApi - public fun shouldFailOnAwaitSessionTransferCredentialsWhenNoRefreshTokenWasSaved(): Unit = runTest { + public fun shouldFailOnAwaitSSOCredentialsWhenNoRefreshTokenWasSaved(): Unit = runTest { verifyNoMoreInteractions(client) Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn(null) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSessionTransferCredentials() } + runBlocking { manager.awaitSsoCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -345,13 +345,13 @@ public class CredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldSaveNewRefreshingTokenOnAwaitSessionTransferCredentials(): Unit = runTest { + public fun shouldSaveNewRefreshingTokenOnAwaitSSOCredentials(): Unit = runTest { Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")) .thenReturn("refresh_token_old") - Mockito.`when`(client.fetchSessionTransferToken("refresh_token_old")) - .thenReturn(sessionTransferCredentialsRequest) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( - SessionTransferCredentialsMock.create( + Mockito.`when`(client.ssoExchange("refresh_token_old")) + .thenReturn(SSOCredentialsRequest) + Mockito.`when`(SSOCredentialsRequest.execute()).thenReturn( + SSOCredentialsMock.create( "web-sso-token", "identity-token", "issued-token-type", @@ -360,7 +360,7 @@ public class CredentialsManagerTest { 60 ) ) - val credentials = manager.awaitSessionTransferCredentials() + val credentials = manager.awaitSsoCredentials() MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt index 09e9a7762..42866e796 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt @@ -15,8 +15,8 @@ import com.auth0.android.request.internal.GsonProvider import com.auth0.android.request.internal.Jwt import com.auth0.android.result.Credentials import com.auth0.android.result.CredentialsMock -import com.auth0.android.result.SessionTransferCredentials -import com.auth0.android.result.SessionTransferCredentialsMock +import com.auth0.android.result.SSOCredentials +import com.auth0.android.result.SSOCredentialsMock import com.auth0.android.util.Clock import com.google.gson.Gson import com.nhaarman.mockitokotlin2.KArgumentCaptor @@ -28,7 +28,6 @@ import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions -import com.nhaarman.mockitokotlin2.verifyZeroInteractions import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest @@ -74,13 +73,13 @@ public class SecureCredentialsManagerTest { private lateinit var callback: Callback @Mock - private lateinit var ssoCallback: Callback + private lateinit var ssoCallback: Callback @Mock private lateinit var request: Request @Mock - private lateinit var sessionTransferCredentialsRequest: Request + private lateinit var SSOCredentialsRequest: Request @Mock private lateinit var crypto: CryptoUtil @@ -103,7 +102,7 @@ public class SecureCredentialsManagerTest { private val credentialsCaptor: KArgumentCaptor = argumentCaptor() private val exceptionCaptor: KArgumentCaptor = argumentCaptor() - private val sessionTransferCredentialsCaptor: KArgumentCaptor = argumentCaptor() + private val SSOCredentialsCaptor: KArgumentCaptor = argumentCaptor() private val stringCaptor: KArgumentCaptor = argumentCaptor() @@ -170,13 +169,13 @@ public class SecureCredentialsManagerTest { /* - * SAVE SessionTransfer credentials test + * SAVE SSO credentials test */ @Test public fun shouldNotSaveIfThereIsErrorInGettingTheExistingCredentials() { verifyNoMoreInteractions(storage) - val ssoCredentials = SessionTransferCredentialsMock.create( + val ssoCredentials = SSOCredentialsMock.create( "accessToken", "identityToken", "issuedTokenType", "tokenType", "refresh_token", 60 ) @@ -190,35 +189,15 @@ public class SecureCredentialsManagerTest { ) Mockito.`when`(crypto.decrypt(storedJson.toByteArray())) .thenThrow(CredentialsManagerException.NO_CREDENTIALS) - manager.saveSessionTransferCredentials(ssoCredentials) + manager.saveSsoCredentials(ssoCredentials) verify(storage, times(0)).store("com.auth0.credentials", storedJson) verify(storage, times(0)).store("com.auth0.credentials_can_refresh", true) } @Test - public fun shouldNotSaveIfTheNewSessionTokenCredentialRefreshAndIdTokenAreSameAsTheExistingOne() { + public fun shouldSaveIfTheNewSSOCredentialRefreshAndIdTokenIsNotSameAsTheExistingOne() { verifyNoMoreInteractions(storage) - val sessionTransferCredentials = SessionTransferCredentialsMock.create( - "accessToken", "idToken", - "issuedTokenType", "tokenType", "refreshToken", 60 - ) - val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - val storedJson = insertTestCredentials( - hasIdToken = true, - hasAccessToken = true, - hasRefreshToken = true, - willExpireAt = expiresAt, - scope = "scope" - ) - manager.saveSessionTransferCredentials(sessionTransferCredentials) - verify(storage, times(0)).store("com.auth0.credentials", storedJson) - verify(storage, times(0)).store("com.auth0.credentials_can_refresh", true) - } - - @Test - public fun shouldSaveIfTheNewSessionTransferCredentialRefreshAndIdTokenIsNotSameAsTheExistingOne() { - verifyNoMoreInteractions(storage) - val sessionTransferCredentials = SessionTransferCredentialsMock.create( + val sessionTransferCredentials = SSOCredentialsMock.create( "accessToken", "identityToken", "issuedTokenType", "tokenType", "refresh_token", 60 ) @@ -240,7 +219,7 @@ public class SecureCredentialsManagerTest { ) val json = gson.toJson(newCredentials) Mockito.`when`(crypto.encrypt(any())).thenReturn(json.toByteArray()) - manager.saveSessionTransferCredentials(sessionTransferCredentials) + manager.saveSsoCredentials(sessionTransferCredentials) verify(storage).store(eq("com.auth0.credentials"), stringCaptor.capture()) val encodedJson = stringCaptor.firstValue MatcherAssert.assertThat(encodedJson, Is.`is`(Matchers.notNullValue())) @@ -252,13 +231,13 @@ public class SecureCredentialsManagerTest { } /* - * GET Session Transfer credentials test + * GET SSO credentials test */ @Test - public fun shouldThrowExceptionIfNoCredentialsExistOnGetSessionTransferCredentials() { + public fun shouldThrowExceptionIfNoCredentialsExistOnGetSSOCredentials() { Mockito.`when`(storage.retrieveString("com.auth0.credentials")) .thenReturn(null) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -271,7 +250,7 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnGetSessionTransferCredentials() { + public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnGetSSOCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) val storedJson = insertTestCredentials( hasIdToken = true, @@ -282,7 +261,7 @@ public class SecureCredentialsManagerTest { ) Mockito.`when`(crypto.decrypt(storedJson.toByteArray())) .thenThrow(CredentialsManagerException.CRYPTO_EXCEPTION) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -295,7 +274,7 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnGetSessionTransferCredentials() { + public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnGetSSOCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) insertTestCredentials( hasIdToken = true, @@ -304,7 +283,7 @@ public class SecureCredentialsManagerTest { willExpireAt = expiresAt, scope = "scope" ) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -317,12 +296,12 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldFetchTheNewRefreshTokenOnGetSessionTransferCredentials() { + public fun shouldFetchTheNewRefreshTokenOnGetSSOCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) - .thenReturn(sessionTransferCredentialsRequest) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( - SessionTransferCredentialsMock.create( + Mockito.`when`(client.ssoExchange("refreshToken")) + .thenReturn(SSOCredentialsRequest) + Mockito.`when`(SSOCredentialsRequest.execute()).thenReturn( + SSOCredentialsMock.create( "web-sso-token", "identity-token", "issued-token-type", @@ -348,11 +327,11 @@ public class SecureCredentialsManagerTest { ) val json = gson.toJson(newCredentials) Mockito.`when`(crypto.encrypt(any())).thenReturn(json.toByteArray()) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onSuccess( - sessionTransferCredentialsCaptor.capture() + SSOCredentialsCaptor.capture() ) - val credentials = sessionTransferCredentialsCaptor.firstValue + val credentials = SSOCredentialsCaptor.firstValue MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) MatcherAssert.assertThat(credentials.tokenType, Is.`is`("token-type")) @@ -369,10 +348,10 @@ public class SecureCredentialsManagerTest { } @Test - public fun shouldFailWhenRefreshTokenExpiredOnGetSessionTransferCredentials() { + public fun shouldFailWhenRefreshTokenExpiredOnGetSSOCredentials() { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) - .thenReturn(sessionTransferCredentialsRequest) + Mockito.`when`(client.ssoExchange("refreshToken")) + .thenReturn(SSOCredentialsRequest) insertTestCredentials( hasIdToken = true, hasAccessToken = true, @@ -385,8 +364,8 @@ public class SecureCredentialsManagerTest { "invalid_grant", "Unknown or invalid refresh token." ) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenThrow(authenticationException) - manager.getSessionTransferCredentials(ssoCallback) + Mockito.`when`(SSOCredentialsRequest.execute()).thenThrow(authenticationException) + manager.getSsoCredentials(ssoCallback) verify(ssoCallback).onFailure( exceptionCaptor.capture() ) @@ -395,7 +374,7 @@ public class SecureCredentialsManagerTest { MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException)) MatcherAssert.assertThat( exception.message, - Is.`is`("An error occurred while trying to use the Refresh Token to renew the Credentials.") + Is.`is`("The exchange of the refresh token for SSO credentials failed.") ) } @@ -404,12 +383,12 @@ public class SecureCredentialsManagerTest { */ @Test @ExperimentalCoroutinesApi - public fun shouldFailWhenNoExistingCredentialsWasSavedOnAwaitSessionTransferCredentials(): Unit = runTest { + public fun shouldFailWhenNoExistingCredentialsWasSavedOnAwaitSSOCredentials(): Unit = runTest { verifyNoMoreInteractions(client) Mockito.`when`(storage.retrieveString("com.auth0.credentials")) .thenReturn(null) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSessionTransferCredentials() } + runBlocking { manager.awaitSsoCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -420,7 +399,7 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnAwaitSessionTransferCredentials(): Unit = + public fun shouldThrowExceptionIfFetchingExistingCredentialsFailsOnAwaitSSOCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) val storedJson = insertTestCredentials( @@ -433,7 +412,7 @@ public class SecureCredentialsManagerTest { Mockito.`when`(crypto.decrypt(storedJson.toByteArray())) .thenThrow(CredentialsManagerException.CRYPTO_EXCEPTION) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSessionTransferCredentials() } + runBlocking { manager.awaitSsoCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -444,7 +423,7 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnAwaitSessionTransferCredentials(): Unit = + public fun shouldThrowExceptionIfExistingCredentialsHasNoRefreshTokenOnAwaitSSOCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) insertTestCredentials( @@ -454,9 +433,9 @@ public class SecureCredentialsManagerTest { willExpireAt = expiresAt, scope = "scope" ) - manager.getSessionTransferCredentials(ssoCallback) + manager.getSsoCredentials(ssoCallback) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSessionTransferCredentials() } + runBlocking { manager.awaitSsoCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat( @@ -467,12 +446,12 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldFetchTheNewRefreshTokenOnAwaitSessionTransferCredentials(): Unit = runTest { + public fun shouldFetchTheNewRefreshTokenOnAwaitSSOCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) - .thenReturn(sessionTransferCredentialsRequest) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenReturn( - SessionTransferCredentialsMock.create( + Mockito.`when`(client.ssoExchange("refreshToken")) + .thenReturn(SSOCredentialsRequest) + Mockito.`when`(SSOCredentialsRequest.execute()).thenReturn( + SSOCredentialsMock.create( "web-sso-token", "identity-token", "issued-token-type", @@ -499,7 +478,7 @@ public class SecureCredentialsManagerTest { val json = gson.toJson(newCredentials) Mockito.`when`(crypto.encrypt(any())).thenReturn(json.toByteArray()) val credentials = runBlocking { - manager.awaitSessionTransferCredentials() + manager.awaitSsoCredentials() } MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(credentials.sessionTransferToken, Is.`is`("web-sso-token")) @@ -518,10 +497,10 @@ public class SecureCredentialsManagerTest { @Test @ExperimentalCoroutinesApi - public fun shouldFailWhenRefreshTokenExpiredOnAwaitSessionTransferCredentials(): Unit = runTest { + public fun shouldFailWhenRefreshTokenExpiredOnAwaitSSOCredentials(): Unit = runTest { val expiresAt = Date(CredentialsMock.ONE_HOUR_AHEAD_MS) - Mockito.`when`(client.fetchSessionTransferToken("refreshToken")) - .thenReturn(sessionTransferCredentialsRequest) + Mockito.`when`(client.ssoExchange("refreshToken")) + .thenReturn(SSOCredentialsRequest) insertTestCredentials( hasIdToken = true, hasAccessToken = true, @@ -534,15 +513,15 @@ public class SecureCredentialsManagerTest { "invalid_grant", "Unknown or invalid refresh token." ) - Mockito.`when`(sessionTransferCredentialsRequest.execute()).thenThrow(authenticationException) + Mockito.`when`(SSOCredentialsRequest.execute()).thenThrow(authenticationException) val exception = assertThrows(CredentialsManagerException::class.java) { - runBlocking { manager.awaitSessionTransferCredentials() } + runBlocking { manager.awaitSsoCredentials() } } MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue())) MatcherAssert.assertThat(exception.cause, Is.`is`(authenticationException)) MatcherAssert.assertThat( exception.message, - Is.`is`("An error occurred while trying to use the Refresh Token to renew the Credentials.") + Is.`is`("The exchange of the refresh token for SSO credentials failed.") ) } diff --git a/auth0/src/test/java/com/auth0/android/result/SessionTransferCredentialsMock.kt b/auth0/src/test/java/com/auth0/android/result/SSOCredentialsMock.kt similarity index 74% rename from auth0/src/test/java/com/auth0/android/result/SessionTransferCredentialsMock.kt rename to auth0/src/test/java/com/auth0/android/result/SSOCredentialsMock.kt index 8b6a6280c..203fc6424 100644 --- a/auth0/src/test/java/com/auth0/android/result/SessionTransferCredentialsMock.kt +++ b/auth0/src/test/java/com/auth0/android/result/SSOCredentialsMock.kt @@ -1,6 +1,6 @@ package com.auth0.android.result -public class SessionTransferCredentialsMock { +public class SSOCredentialsMock { public companion object { @@ -11,8 +11,8 @@ public class SessionTransferCredentialsMock { type: String, refreshToken: String?, expiresIn: Int - ): SessionTransferCredentials { - return SessionTransferCredentials( + ): SSOCredentials { + return SSOCredentials( accessToken,idToken, issuedTokenType, type, expiresIn, refreshToken ) } From 7d65b543cbc3d464323a1df93ec88aa4f26e3fdc Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 17 Apr 2025 11:37:21 +0530 Subject: [PATCH 8/8] Removed the unused WebClientProvider class --- EXAMPLES.md | 2 +- .../android/provider/WebClientProvider.kt | 27 ------------------- .../auth0/android/result/SSOCredentials.kt | 8 +++--- 3 files changed, 5 insertions(+), 32 deletions(-) delete mode 100644 auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt diff --git a/EXAMPLES.md b/EXAMPLES.md index 0c3063280..ebfa05f37 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -545,7 +545,7 @@ authentication This feature allows you to authenticate a user in a web session using the refresh token obtained from the native session without requiring the user to log in again. -Call the API to fetch a webSessionTransferToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` end point by passing as a query parameter or a cookie value. +Call the API to fetch a webSessionTransferToken in exchange for a refresh token. Use the obtained token to authenticate the user by calling the `/authorize` endpoint, passing the token as a query parameter or a cookie value. ```kotlin authentication diff --git a/auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt b/auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt deleted file mode 100644 index 5ae240d59..000000000 --- a/auth0/src/main/java/com/auth0/android/provider/WebClientProvider.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.auth0.android.provider - -import android.webkit.CookieManager -import android.webkit.WebView -import com.auth0.android.Auth0 - -/** - * Provider class to handle native to web sso - */ -public object WebClientProvider { - private const val TAG = "WebSSOProvider" - - public fun configureSSOWebView( - account: Auth0, - url: String, - webView: WebView, - sessionToken: String, - ): WebView { - val cookieManager = CookieManager.getInstance() - cookieManager.setAcceptCookie(true) - cookieManager.setCookie( - url, - "session_token=$sessionToken; path=/" - ) - return webView - } -} \ No newline at end of file diff --git a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt index 8f8abda57..f56812087 100644 --- a/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt +++ b/auth0/src/main/java/com/auth0/android/result/SSOCredentials.kt @@ -3,11 +3,11 @@ package com.auth0.android.result import com.google.gson.annotations.SerializedName /** - * Holds the token credentials required for web SSO . + * Holds the token credentials required for web SSO. */ public data class SSOCredentials( /** - * The token used for web SSO . + * The token used for web SSO. * * @return the session transfer token. */ @@ -30,7 +30,7 @@ public data class SSOCredentials( @field:SerializedName("id_token") public val idToken: String, /** - * Type of the token issued.In this case, an Auth0 session transfer token + * Type of the token issued. In this case, an Auth0 session transfer token. * * @return the issued token type. */ @@ -47,7 +47,7 @@ public data class SSOCredentials( /** * Expiration duration of the session transfer token in seconds. Session transfer tokens are short-lived and expire after a few minutes. - * Once expired, the session transfer tokens can no longer be used for web SSO. + * Once expired, the session transfer tokens can no longer be used for web SSO. * * @return the expiration duration of this session transfer token */