@@ -3,6 +3,7 @@ package com.auth0.android.authentication.storage
33import com.auth0.android.NetworkErrorException
44import com.auth0.android.authentication.AuthenticationAPIClient
55import com.auth0.android.authentication.AuthenticationException
6+ import com.auth0.android.authentication.storage.BaseCredentialsManager.Companion.DEFAULT_MIN_TTL
67import com.auth0.android.callback.Callback
78import com.auth0.android.request.Request
89import com.auth0.android.request.internal.GsonProvider
@@ -672,7 +673,7 @@ public class CredentialsManagerTest {
672673 Mockito .`when `(
673674 client.renewAuth(" refresh_token" , " audience" )
674675 ).thenReturn(request)
675- val newDate = Date (CredentialsMock .CURRENT_TIME_MS + 1 * 1000 )
676+ val newDate = Date (CredentialsMock .CURRENT_TIME_MS + ( DEFAULT_MIN_TTL + 10 ) * 1000L )
676677 val jwtMock = mock<Jwt >()
677678 Mockito .`when `(jwtMock.expiresAt).thenReturn(newDate)
678679 Mockito .`when `(jwtDecoder.decode(" newId" )).thenReturn(jwtMock)
@@ -1770,6 +1771,103 @@ public class CredentialsManagerTest {
17701771 MatcherAssert .assertThat(manager.hasValidCredentials(), Is .`is `(true ))
17711772 }
17721773
1774+ @Test
1775+ public fun shouldRenewCredentialsViaCallbackWhenTokenExpiresWithinDefaultMinTtl () {
1776+ // Token expires in 30 seconds, which is within DEFAULT_MIN_TTL (60s)
1777+ val expirationTime = CredentialsMock .CURRENT_TIME_MS + 30 * 1000
1778+ Mockito .`when `(storage.retrieveString(" com.auth0.id_token" )).thenReturn(" idToken" )
1779+ Mockito .`when `(storage.retrieveString(" com.auth0.access_token" )).thenReturn(" accessToken" )
1780+ Mockito .`when `(storage.retrieveString(" com.auth0.refresh_token" )).thenReturn(" refreshToken" )
1781+ Mockito .`when `(storage.retrieveString(" com.auth0.token_type" )).thenReturn(" type" )
1782+ Mockito .`when `(storage.retrieveLong(" com.auth0.expires_at" )).thenReturn(expirationTime)
1783+ Mockito .`when `(storage.retrieveLong(" com.auth0.cache_expires_at" ))
1784+ .thenReturn(expirationTime)
1785+ Mockito .`when `(storage.retrieveString(" com.auth0.scope" )).thenReturn(" scope" )
1786+ Mockito .`when `(
1787+ client.renewAuth(" refreshToken" )
1788+ ).thenReturn(request)
1789+ val newDate = Date (CredentialsMock .ONE_HOUR_AHEAD_MS )
1790+ val jwtMock = mock<Jwt >()
1791+ Mockito .`when `(jwtMock.expiresAt).thenReturn(newDate)
1792+ Mockito .`when `(jwtDecoder.decode(" newId" )).thenReturn(jwtMock)
1793+
1794+ val renewedCredentials =
1795+ Credentials (" newId" , " newAccess" , " newType" , " refreshToken" , newDate, " newScope" )
1796+ Mockito .`when `(request.execute()).thenReturn(renewedCredentials)
1797+ // Use no-arg getCredentials which now uses DEFAULT_MIN_TTL
1798+ manager.getCredentials(callback)
1799+ verify(callback).onSuccess(
1800+ credentialsCaptor.capture()
1801+ )
1802+ // Verify renewal was triggered (client.renewAuth was called)
1803+ verify(client).renewAuth(" refreshToken" )
1804+ val retrievedCredentials = credentialsCaptor.firstValue
1805+ MatcherAssert .assertThat(retrievedCredentials, Is .`is `(Matchers .notNullValue()))
1806+ MatcherAssert .assertThat(retrievedCredentials.idToken, Is .`is `(" newId" ))
1807+ MatcherAssert .assertThat(retrievedCredentials.accessToken, Is .`is `(" newAccess" ))
1808+ }
1809+
1810+ @Test
1811+ @ExperimentalCoroutinesApi
1812+ public fun shouldAwaitRenewedCredentialsWhenTokenExpiresWithinDefaultMinTtl (): Unit = runTest {
1813+ // Token expires in 30 seconds, which is within DEFAULT_MIN_TTL (60s)
1814+ val expirationTime = CredentialsMock .CURRENT_TIME_MS + 30 * 1000
1815+ Mockito .`when `(storage.retrieveString(" com.auth0.id_token" )).thenReturn(" idToken" )
1816+ Mockito .`when `(storage.retrieveString(" com.auth0.access_token" )).thenReturn(" accessToken" )
1817+ Mockito .`when `(storage.retrieveString(" com.auth0.refresh_token" )).thenReturn(" refreshToken" )
1818+ Mockito .`when `(storage.retrieveString(" com.auth0.token_type" )).thenReturn(" type" )
1819+ Mockito .`when `(storage.retrieveLong(" com.auth0.expires_at" )).thenReturn(expirationTime)
1820+ Mockito .`when `(storage.retrieveLong(" com.auth0.cache_expires_at" ))
1821+ .thenReturn(expirationTime)
1822+ Mockito .`when `(storage.retrieveString(" com.auth0.scope" )).thenReturn(" scope" )
1823+ Mockito .`when `(
1824+ client.renewAuth(" refreshToken" )
1825+ ).thenReturn(request)
1826+ val newDate = Date (CredentialsMock .ONE_HOUR_AHEAD_MS )
1827+ val jwtMock = mock<Jwt >()
1828+ Mockito .`when `(jwtMock.expiresAt).thenReturn(newDate)
1829+ Mockito .`when `(jwtDecoder.decode(" newId" )).thenReturn(jwtMock)
1830+
1831+ val renewedCredentials =
1832+ Credentials (" newId" , " newAccess" , " newType" , " refreshToken" , newDate, " newScope" )
1833+ Mockito .`when `(request.execute()).thenReturn(renewedCredentials)
1834+ // Use no-arg awaitCredentials which now uses DEFAULT_MIN_TTL
1835+ val result = manager.awaitCredentials()
1836+ // Verify renewal was triggered
1837+ verify(client).renewAuth(" refreshToken" )
1838+ MatcherAssert .assertThat(result, Is .`is `(Matchers .notNullValue()))
1839+ MatcherAssert .assertThat(result.idToken, Is .`is `(" newId" ))
1840+ MatcherAssert .assertThat(result.accessToken, Is .`is `(" newAccess" ))
1841+ }
1842+
1843+ @Test
1844+ public fun shouldNotHaveValidCredentialsWhenTokenExpiresWithinDefaultMinTtlAndNoRefreshToken () {
1845+ // Token expires in 30 seconds, within DEFAULT_MIN_TTL (60s), and no refresh token
1846+ val expirationTime = CredentialsMock .CURRENT_TIME_MS + 30 * 1000
1847+ Mockito .`when `(storage.retrieveLong(" com.auth0.expires_at" )).thenReturn(expirationTime)
1848+ Mockito .`when `(storage.retrieveLong(" com.auth0.cache_expires_at" ))
1849+ .thenReturn(expirationTime)
1850+ Mockito .`when `(storage.retrieveString(" com.auth0.refresh_token" )).thenReturn(null )
1851+ Mockito .`when `(storage.retrieveString(" com.auth0.id_token" )).thenReturn(" idToken" )
1852+ Mockito .`when `(storage.retrieveString(" com.auth0.access_token" )).thenReturn(" accessToken" )
1853+ // No-arg hasValidCredentials now uses DEFAULT_MIN_TTL, so token expiring in 30s is invalid
1854+ Assert .assertFalse(manager.hasValidCredentials())
1855+ }
1856+
1857+ @Test
1858+ public fun shouldHaveValidCredentialsWhenTokenExpiresWithinDefaultMinTtlButRefreshTokenAvailable () {
1859+ // Token expires in 30 seconds, within DEFAULT_MIN_TTL (60s), but refresh token is available
1860+ val expirationTime = CredentialsMock .CURRENT_TIME_MS + 30 * 1000
1861+ Mockito .`when `(storage.retrieveLong(" com.auth0.expires_at" )).thenReturn(expirationTime)
1862+ Mockito .`when `(storage.retrieveLong(" com.auth0.cache_expires_at" ))
1863+ .thenReturn(expirationTime)
1864+ Mockito .`when `(storage.retrieveString(" com.auth0.refresh_token" )).thenReturn(" refreshToken" )
1865+ Mockito .`when `(storage.retrieveString(" com.auth0.id_token" )).thenReturn(" idToken" )
1866+ Mockito .`when `(storage.retrieveString(" com.auth0.access_token" )).thenReturn(" accessToken" )
1867+ // Even though token expires within DEFAULT_MIN_TTL, refresh token makes it valid
1868+ MatcherAssert .assertThat(manager.hasValidCredentials(), Is .`is `(true ))
1869+ }
1870+
17731871 @Test
17741872 public fun shouldNotHaveCredentialsWhenAccessTokenAndIdTokenAreMissing () {
17751873 Mockito .`when `(storage.retrieveString(" com.auth0.id_token" )).thenReturn(null )
@@ -1812,7 +1910,7 @@ public class CredentialsManagerTest {
18121910 // now, update the clock and retry
18131911 manager.setClock(object : Clock {
18141912 override fun getCurrentTimeMillis (): Long {
1815- return CredentialsMock .CURRENT_TIME_MS - 1000
1913+ return CredentialsMock .CURRENT_TIME_MS - ( DEFAULT_MIN_TTL * 1000 + 1000 )
18161914 }
18171915 })
18181916 MatcherAssert .assertThat(manager.hasValidCredentials(), Is .`is `(true ))
@@ -1829,7 +1927,6 @@ public class CredentialsManagerTest {
18291927 })
18301928 }
18311929
1832-
18331930 @Test
18341931 public fun shouldAddParametersToRequest () {
18351932 Mockito .`when `(storage.retrieveString(" com.auth0.id_token" )).thenReturn(" idToken" )
0 commit comments