Skip to content

Commit 27a366e

Browse files
authored
Merge branch 'main' into SDK-5905
2 parents 5010ce0 + 7c2602b commit 27a366e

File tree

10 files changed

+262
-4
lines changed

10 files changed

+262
-4
lines changed

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.5.0
1+
3.6.0

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Change Log
22

3+
## [3.6.0](https://github.com/auth0/Auth0.Android/tree/3.6.0) (2025-04-28)
4+
[Full Changelog](https://github.com/auth0/Auth0.Android/compare/3.5.0...3.6.0)
5+
6+
**Added**
7+
- Added new error type to CredentialsManagerException class [\#821](https://github.com/auth0/Auth0.Android/pull/821) ([pmathew92](https://github.com/pmathew92))
8+
- Added Native to Web support [\#803](https://github.com/auth0/Auth0.Android/pull/803) ([pmathew92](https://github.com/pmathew92))
9+
10+
**Changed**
11+
- Removed experimental tag from TWA [\#818](https://github.com/auth0/Auth0.Android/pull/818) ([pmathew92](https://github.com/pmathew92))
12+
313
## [3.5.0](https://github.com/auth0/Auth0.Android/tree/3.5.0) (2025-03-17)
414
[Full Changelog](https://github.com/auth0/Auth0.Android/compare/3.4.0...3.5.0)
515

EXAMPLES.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
- [Sign Up with a database connection](#sign-up-with-a-database-connection)
1919
- [Get user information](#get-user-information)
2020
- [Custom Token Exchange](#custom-token-exchange)
21-
- [Native to Web SSO login](#native-to-web-sso-login)
21+
- [Native to Web SSO login [EA]](#native-to-web-sso-login-ea)
2222
- [Credentials Manager](#credentials-manager)
2323
- [Secure Credentials Manager](#secure-credentials-manager)
2424
- [Usage](#usage)
@@ -541,7 +541,11 @@ authentication
541541
</details>
542542

543543

544-
## Native to Web SSO login
544+
## Native to Web SSO login [EA]
545+
546+
> [!NOTE]
547+
> This feature is currently available in [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access). Please reach out to Auth0 support to get it
548+
> enabled for your tenant.
545549
546550
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.
547551

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ To install Auth0.Android with [Gradle](https://gradle.org/), simply add the foll
5151

5252
```gradle
5353
dependencies {
54-
implementation 'com.auth0.android:auth0:3.5.0'
54+
implementation 'com.auth0.android:auth0:3.6.0'
5555
}
5656
```
5757

auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,12 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
930930
* parameter with the session transfer token. For example,
931931
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
932932
*
933+
* ##Availability
934+
*
935+
* This feature is currently available in
936+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
937+
* Please reach out to Auth0 support to get it enabled for your tenant.
938+
*
933939
*
934940
* @param refreshToken A valid refresh token obtained as part of Auth0 authentication
935941
* @return a request to fetch a session transfer token

auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.auth0.android.authentication.storage
22

33
import android.text.TextUtils
4+
import android.util.Log
45
import androidx.annotation.VisibleForTesting
56
import com.auth0.android.authentication.AuthenticationAPIClient
67
import com.auth0.android.authentication.AuthenticationException
@@ -62,6 +63,12 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
6263
* parameter with the session transfer token. For example,
6364
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
6465
*
66+
* ## Availability
67+
*
68+
* This feature is currently available in
69+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
70+
* Please reach out to Auth0 support to get it enabled for your tenant.
71+
*
6572
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
6673
* This method will handle saving the refresh_token, if a new one is issued.
6774
*/
@@ -77,6 +84,12 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
7784
* parameter with the session transfer token. For example,
7885
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
7986
*
87+
* ## Availability
88+
*
89+
* This feature is currently available in
90+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
91+
* Please reach out to Auth0 support to get it enabled for your tenant.
92+
*
8093
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
8194
* This method will handle saving the refresh_token, if a new one is issued.
8295
*/
@@ -110,6 +123,17 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
110123
error
111124
)
112125
)
126+
} catch (exception: RuntimeException) {
127+
Log.e(
128+
TAG,
129+
"Caught unexpected exceptions while fetching sso token ${exception.stackTraceToString()}"
130+
)
131+
callback.onFailure(
132+
CredentialsManagerException(
133+
CredentialsManagerException.Code.UNKNOWN_ERROR,
134+
exception
135+
)
136+
)
113137
}
114138
}
115139
}
@@ -122,6 +146,12 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
122146
* parameter with the session transfer token. For example,
123147
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
124148
*
149+
* ## Availability
150+
*
151+
* This feature is currently available in
152+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
153+
* Please reach out to Auth0 support to get it enabled for your tenant.
154+
*
125155
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
126156
* This method will handle saving the refresh_token, if a new one is issued.
127157
*/
@@ -139,6 +169,12 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
139169
* parameter with the session transfer token. For example,
140170
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
141171
*
172+
* ## Availability
173+
*
174+
* This feature is currently available in
175+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
176+
* Please reach out to Auth0 support to get it enabled for your tenant.
177+
*
142178
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
143179
* This method will handle saving the refresh_token, if a new one is issued.
144180
*/
@@ -442,6 +478,20 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
442478
exception, error
443479
)
444480
)
481+
} catch (exception: RuntimeException) {
482+
/**
483+
* Catching any unexpected runtime errors in the token renewal flow
484+
*/
485+
Log.e(
486+
TAG,
487+
"Caught unexpected exceptions for token renewal ${exception.stackTraceToString()}"
488+
)
489+
callback.onFailure(
490+
CredentialsManagerException(
491+
CredentialsManagerException.Code.UNKNOWN_ERROR,
492+
exception
493+
)
494+
)
445495
}
446496
}
447497
}
@@ -527,5 +577,6 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
527577
// This is no longer used as we get the credentials expiry from the access token only,
528578
// but we still store it so users can rollback to versions where it is required.
529579
private const val LEGACY_KEY_CACHE_EXPIRES_AT = "com.auth0.cache_expires_at"
580+
private val TAG = CredentialsManager::class.java.simpleName
530581
}
531582
}

auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManagerException.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class CredentialsManagerException :
4646
NO_NETWORK,
4747
API_ERROR,
4848
SSO_EXCHANGE_FAILED,
49+
UNKNOWN_ERROR
4950
}
5051

5152
private var code: Code?
@@ -146,6 +147,8 @@ public class CredentialsManagerException :
146147
public val SSO_EXCHANGE_FAILED: CredentialsManagerException =
147148
CredentialsManagerException(Code.SSO_EXCHANGE_FAILED)
148149

150+
public val UNKNOWN_ERROR: CredentialsManagerException = CredentialsManagerException(Code.UNKNOWN_ERROR)
151+
149152

150153
private fun getMessage(code: Code): String {
151154
return when (code) {
@@ -191,6 +194,7 @@ public class CredentialsManagerException :
191194
Code.NO_NETWORK -> "Failed to execute the network request."
192195
Code.API_ERROR -> "An error occurred while processing the request."
193196
Code.SSO_EXCHANGE_FAILED ->"The exchange of the refresh token for SSO credentials failed."
197+
Code.UNKNOWN_ERROR -> "An unknown error has occurred while fetching the token. Please check the error cause for more details."
194198
}
195199
}
196200
}

auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
133133
* parameter with the session transfer token. For example,
134134
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
135135
*
136+
* ## Availability
137+
*
138+
* This feature is currently available in
139+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
140+
* Please reach out to Auth0 support to get it enabled for your tenant.
141+
*
136142
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
137143
* This method will handle saving the refresh_token, if a new one is issued.
138144
*/
@@ -148,6 +154,12 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
148154
* parameter with the session transfer token. For example,
149155
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
150156
*
157+
* ## Availability
158+
*
159+
* This feature is currently available in
160+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
161+
* Please reach out to Auth0 support to get it enabled for your tenant.
162+
*
151163
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
152164
* This method will handle saving the refresh_token, if a new one is issued.
153165
*/
@@ -192,6 +204,17 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
192204
CredentialsManagerException.Code.STORE_FAILED, error
193205
)
194206
callback.onFailure(exception)
207+
} catch (exception: RuntimeException) {
208+
Log.e(
209+
TAG,
210+
"Caught unexpected exceptions while fetching sso token ${exception.stackTraceToString()}"
211+
)
212+
callback.onFailure(
213+
CredentialsManagerException(
214+
CredentialsManagerException.Code.UNKNOWN_ERROR,
215+
exception
216+
)
217+
)
195218
}
196219
}
197220
}
@@ -204,6 +227,12 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
204227
* parameter with the session transfer token. For example,
205228
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
206229
*
230+
* ## Availability
231+
*
232+
* This feature is currently available in
233+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
234+
* Please reach out to Auth0 support to get it enabled for your tenant.
235+
*
207236
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
208237
* This method will handle saving the refresh_token, if a new one is issued.
209238
*/
@@ -221,6 +250,12 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
221250
* parameter with the session transfer token. For example,
222251
* `https://example.com/login?session_transfer_token=THE_TOKEN`.
223252
*
253+
* ## Availability
254+
*
255+
* This feature is currently available in
256+
* [Early Access](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages#early-access).
257+
* Please reach out to Auth0 support to get it enabled for your tenant.
258+
*
224259
* It will fail with [CredentialsManagerException] if the existing refresh_token is null or no longer valid.
225260
* This method will handle saving the refresh_token, if a new one is issued.
226261
*/
@@ -680,6 +715,21 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
680715
)
681716
)
682717
return@execute
718+
} catch (exception: RuntimeException) {
719+
/**
720+
* Catching any unexpected runtime errors in the token renewal flow
721+
*/
722+
Log.e(
723+
TAG,
724+
"Caught unexpected exceptions for token renewal ${exception.stackTraceToString()}"
725+
)
726+
callback.onFailure(
727+
CredentialsManagerException(
728+
CredentialsManagerException.Code.UNKNOWN_ERROR,
729+
exception
730+
)
731+
)
732+
return@execute
683733
}
684734

685735
try {

auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,31 @@ public class CredentialsManagerTest {
328328
)
329329
}
330330

331+
@Test
332+
public fun shouldFailOnGetNewSSOCredentialsWhenUnexpectedErrorOccurs() {
333+
Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken")
334+
Mockito.`when`(
335+
client.ssoExchange("refreshToken")
336+
).thenReturn(SSOCredentialsRequest)
337+
//Trigger failure
338+
val runtimeException = RuntimeException(
339+
"unexpected_error"
340+
)
341+
Mockito.`when`(SSOCredentialsRequest.execute()).thenThrow(runtimeException)
342+
manager.getSsoCredentials(ssoCallback)
343+
verify(ssoCallback).onFailure(
344+
exceptionCaptor.capture()
345+
)
346+
val exception = exceptionCaptor.firstValue
347+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
348+
MatcherAssert.assertThat(exception, Is.`is`(CredentialsManagerException.UNKNOWN_ERROR))
349+
MatcherAssert.assertThat(exception.cause, Is.`is`(runtimeException))
350+
MatcherAssert.assertThat(
351+
exception.message,
352+
Is.`is`("An unknown error has occurred while fetching the token. Please check the error cause for more details.")
353+
)
354+
}
355+
331356
@Test
332357
@ExperimentalCoroutinesApi
333358
public fun shouldFailOnAwaitSSOCredentialsWhenNoRefreshTokenWasSaved(): Unit = runTest {
@@ -1122,6 +1147,46 @@ public class CredentialsManagerTest {
11221147
)
11231148
}
11241149

1150+
@Test
1151+
public fun shouldGetAndFailToRenewExpiredCredentialsWhenAnyUnexpectedErrorOccurs() {
1152+
Mockito.`when`(storage.retrieveString("com.auth0.id_token")).thenReturn("idToken")
1153+
Mockito.`when`(storage.retrieveString("com.auth0.access_token")).thenReturn("accessToken")
1154+
Mockito.`when`(storage.retrieveString("com.auth0.refresh_token")).thenReturn("refreshToken")
1155+
Mockito.`when`(storage.retrieveString("com.auth0.token_type")).thenReturn("type")
1156+
val expirationTime = CredentialsMock.CURRENT_TIME_MS //Same as current time --> expired
1157+
Mockito.`when`(storage.retrieveLong("com.auth0.expires_at")).thenReturn(expirationTime)
1158+
Mockito.`when`(storage.retrieveLong("com.auth0.cache_expires_at"))
1159+
.thenReturn(expirationTime)
1160+
Mockito.`when`(storage.retrieveString("com.auth0.scope")).thenReturn("scope")
1161+
Mockito.`when`(
1162+
client.renewAuth("refreshToken")
1163+
).thenReturn(request)
1164+
//Trigger failure
1165+
val runtimeException = NullPointerException("Something went wrong")
1166+
Mockito.`when`(request.execute()).thenThrow(runtimeException)
1167+
manager.getCredentials(callback)
1168+
verify(storage, never())
1169+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt())
1170+
verify(storage, never())
1171+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyLong())
1172+
verify(storage, never())
1173+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
1174+
verify(storage, never())
1175+
.store(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())
1176+
verify(storage, never()).remove(ArgumentMatchers.anyString())
1177+
verify(callback).onFailure(
1178+
exceptionCaptor.capture()
1179+
)
1180+
val exception = exceptionCaptor.firstValue
1181+
MatcherAssert.assertThat(exception, Is.`is`(Matchers.notNullValue()))
1182+
MatcherAssert.assertThat(exception, Is.`is`(CredentialsManagerException.UNKNOWN_ERROR))
1183+
MatcherAssert.assertThat(exception.cause, Is.`is`(runtimeException))
1184+
MatcherAssert.assertThat(
1185+
exception.message,
1186+
Is.`is`("An unknown error has occurred while fetching the token. Please check the error cause for more details.")
1187+
)
1188+
}
1189+
11251190
@Test
11261191
public fun shouldClearCredentials() {
11271192
manager.clearCredentials()

0 commit comments

Comments
 (0)