Skip to content

Commit 4fea239

Browse files
Merge pull request #323 from okta/idx_configurable_defaults
Make AuthFoundationDefaults configurable by other SDKs
2 parents 7277658 + 884ef31 commit 4fea239

File tree

14 files changed

+250
-56
lines changed

14 files changed

+250
-56
lines changed

.bacon.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ test_suites:
77
criteria: MERGE
88
queue_name: small
99
trigger: AUTO
10+
11+
- name: sca-scan
12+
script_path: /root/okta/okta-mobile-kotlin/scripts/
13+
sort_order: '1'
14+
timeout: '200'
15+
script_name: dependency_scan
16+
criteria: MAINLINE
17+
queue_name: small

auth-foundation/api/auth-foundation.api

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,36 @@ public final class com/okta/authfoundation/BiometricExceptionDetails$OnAuthentic
7575
public abstract interface annotation class com/okta/authfoundation/InternalAuthFoundationApi : java/lang/annotation/Annotation {
7676
}
7777

78+
public final class com/okta/authfoundation/SdkDefaults {
79+
public static final field INSTANCE Lcom/okta/authfoundation/SdkDefaults;
80+
public final fun getGetAccessTokenValidator ()Lkotlin/jvm/functions/Function0;
81+
public final fun getGetCacheFactory ()Lkotlin/jvm/functions/Function0;
82+
public final fun getGetClock ()Lkotlin/jvm/functions/Function0;
83+
public final fun getGetComputeDispatcher ()Lkotlin/jvm/functions/Function0;
84+
public final fun getGetCookieJar ()Lkotlin/jvm/functions/Function0;
85+
public final fun getGetDeviceSecretValidator ()Lkotlin/jvm/functions/Function0;
86+
public final fun getGetEventCoordinator ()Lkotlin/jvm/functions/Function0;
87+
public final fun getGetIdTokenValidator ()Lkotlin/jvm/functions/Function0;
88+
public final fun getGetIoDispatcher ()Lkotlin/jvm/functions/Function0;
89+
public final fun getGetLoginCancellationDebounceTime ()Lkotlin/jvm/functions/Function0;
90+
public final fun getGetOkHttpClientFactory ()Lkotlin/jvm/functions/Function0;
91+
public final fun getGetTokenEncryptionHandler ()Lkotlin/jvm/functions/Function0;
92+
public final fun getGetTokenStorageFactory ()Lkotlin/jvm/functions/Function0;
93+
public final fun setGetAccessTokenValidator (Lkotlin/jvm/functions/Function0;)V
94+
public final fun setGetCacheFactory (Lkotlin/jvm/functions/Function0;)V
95+
public final fun setGetClock (Lkotlin/jvm/functions/Function0;)V
96+
public final fun setGetComputeDispatcher (Lkotlin/jvm/functions/Function0;)V
97+
public final fun setGetCookieJar (Lkotlin/jvm/functions/Function0;)V
98+
public final fun setGetDeviceSecretValidator (Lkotlin/jvm/functions/Function0;)V
99+
public final fun setGetEventCoordinator (Lkotlin/jvm/functions/Function0;)V
100+
public final fun setGetIdTokenValidator (Lkotlin/jvm/functions/Function0;)V
101+
public final fun setGetIoDispatcher (Lkotlin/jvm/functions/Function0;)V
102+
public final fun setGetLoginCancellationDebounceTime (Lkotlin/jvm/functions/Function0;)V
103+
public final fun setGetOkHttpClientFactory (Lkotlin/jvm/functions/Function0;)V
104+
public final fun setGetTokenEncryptionHandler (Lkotlin/jvm/functions/Function0;)V
105+
public final fun setGetTokenStorageFactory (Lkotlin/jvm/functions/Function0;)V
106+
}
107+
78108
public abstract interface class com/okta/authfoundation/claims/ClaimsProvider {
79109
public abstract fun availableClaims ()Ljava/util/Set;
80110
public abstract fun deserializeClaim (Ljava/lang/String;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;

auth-foundation/build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ apply plugin: 'binary-compatibility-validator'
1212

1313
def copyKotlinTemplates = tasks.register('copyKotlinTemplates', Copy) {
1414
from("src/main/kotlinTemplates")
15-
into("$buildDir/generated/sources/kotlinTemplates")
15+
into("${layout.buildDirectory.get()}/generated/sources/kotlinTemplates")
1616
expand(projectVersion: project.version)
1717
}
1818

@@ -79,7 +79,6 @@ dependencies {
7979
implementation libs.okio.jvm
8080
implementation libs.kotlin.serialization.okio
8181
implementation libs.security.crypto
82-
implementation libs.startup.runtime
8382
implementation libs.room.runtime
8483
implementation libs.room.ktx
8584
implementation libs.sqlcipher.android

auth-foundation/src/main/java/com/okta/authfoundation/AuthFoundationDefaults.kt

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,32 @@
1515
*/
1616
package com.okta.authfoundation
1717

18+
import com.okta.authfoundation.SdkDefaults.getAccessTokenValidator
19+
import com.okta.authfoundation.SdkDefaults.getCacheFactory
20+
import com.okta.authfoundation.SdkDefaults.getClock
21+
import com.okta.authfoundation.SdkDefaults.getComputeDispatcher
22+
import com.okta.authfoundation.SdkDefaults.getCookieJar
23+
import com.okta.authfoundation.SdkDefaults.getDeviceSecretValidator
24+
import com.okta.authfoundation.SdkDefaults.getEventCoordinator
25+
import com.okta.authfoundation.SdkDefaults.getIdTokenValidator
26+
import com.okta.authfoundation.SdkDefaults.getIoDispatcher
27+
import com.okta.authfoundation.SdkDefaults.getLoginCancellationDebounceTime
28+
import com.okta.authfoundation.SdkDefaults.getOkHttpClientFactory
29+
import com.okta.authfoundation.SdkDefaults.getTokenEncryptionHandler
30+
import com.okta.authfoundation.SdkDefaults.getTokenStorageFactory
1831
import com.okta.authfoundation.client.AccessTokenValidator
1932
import com.okta.authfoundation.client.Cache
20-
import com.okta.authfoundation.client.DefaultAccessTokenValidator
21-
import com.okta.authfoundation.client.DefaultDeviceSecretValidator
22-
import com.okta.authfoundation.client.DefaultIdTokenValidator
2333
import com.okta.authfoundation.client.DeviceSecretValidator
2434
import com.okta.authfoundation.client.IdTokenValidator
2535
import com.okta.authfoundation.client.OidcClock
26-
import com.okta.authfoundation.client.SharedPreferencesCache
27-
import com.okta.authfoundation.credential.DefaultTokenEncryptionHandler
28-
import com.okta.authfoundation.credential.RoomTokenStorage
2936
import com.okta.authfoundation.credential.Token
3037
import com.okta.authfoundation.credential.TokenEncryptionHandler
3138
import com.okta.authfoundation.credential.TokenStorage
3239
import com.okta.authfoundation.events.EventCoordinator
33-
import kotlinx.coroutines.Dispatchers
3440
import okhttp3.Call
3541
import okhttp3.CookieJar
36-
import okhttp3.OkHttpClient
37-
import java.time.Instant
3842
import kotlin.coroutines.CoroutineContext
3943
import kotlin.time.Duration
40-
import kotlin.time.Duration.Companion.seconds
4144

4245
/**
4346
* The defaults used in various classes throughout the rest of the SDK.
@@ -48,45 +51,45 @@ import kotlin.time.Duration.Companion.seconds
4851
*/
4952
object AuthFoundationDefaults {
5053
/** The default Call.Factory. */
51-
var okHttpClientFactory: () -> Call.Factory by NoSetAfterGetWithLazyDefaultFactory { { OkHttpClient() } }
54+
var okHttpClientFactory: () -> Call.Factory by NoSetAfterGetWithLazyDefaultFactory { getOkHttpClientFactory() }
5255

5356
/** The CoroutineDispatcher which should be used for IO bound tasks. */
54-
var ioDispatcher: CoroutineContext by NoSetAfterGetWithLazyDefaultFactory { Dispatchers.IO }
57+
var ioDispatcher: CoroutineContext by NoSetAfterGetWithLazyDefaultFactory { getIoDispatcher() }
5558

5659
/** The CoroutineDispatcher which should be used for compute bound tasks. */
57-
var computeDispatcher: CoroutineContext by NoSetAfterGetWithLazyDefaultFactory { Dispatchers.Default }
60+
var computeDispatcher: CoroutineContext by NoSetAfterGetWithLazyDefaultFactory { getComputeDispatcher() }
5861

5962
/** The default EventCoordinator. */
60-
var eventCoordinator: EventCoordinator by NoSetAfterGetWithLazyDefaultFactory { EventCoordinator(emptyList()) }
63+
var eventCoordinator: EventCoordinator by NoSetAfterGetWithLazyDefaultFactory { getEventCoordinator() }
6164

6265
/** The default OidcClock. */
63-
var clock: OidcClock by NoSetAfterGetWithLazyDefaultFactory { OidcClock { Instant.now().epochSecond } }
66+
var clock: OidcClock by NoSetAfterGetWithLazyDefaultFactory { getClock() }
6467

6568
/** The default IdTokenValidator. */
66-
var idTokenValidator: IdTokenValidator by NoSetAfterGetWithLazyDefaultFactory { DefaultIdTokenValidator() }
69+
var idTokenValidator: IdTokenValidator by NoSetAfterGetWithLazyDefaultFactory { getIdTokenValidator() }
6770

6871
/** The default AccessTokenValidator. */
69-
var accessTokenValidator: AccessTokenValidator by NoSetAfterGetWithLazyDefaultFactory { DefaultAccessTokenValidator() }
72+
var accessTokenValidator: AccessTokenValidator by NoSetAfterGetWithLazyDefaultFactory { getAccessTokenValidator() }
7073

7174
/** The default DeviceSecretValidator. */
72-
var deviceSecretValidator: DeviceSecretValidator by NoSetAfterGetWithLazyDefaultFactory { DefaultDeviceSecretValidator() }
75+
var deviceSecretValidator: DeviceSecretValidator by NoSetAfterGetWithLazyDefaultFactory { getDeviceSecretValidator() }
7376

7477
/** The default function that returns a singleton instance of [Cache]. */
75-
var cacheFactory: suspend () -> Cache by NoSetAfterGetWithLazyDefaultFactory { { SharedPreferencesCache.getInstance() } }
78+
var cacheFactory: suspend () -> Cache by NoSetAfterGetWithLazyDefaultFactory { getCacheFactory() }
7679

7780
/** The default function that retuns a singleton instance of [TokenStorage]. */
78-
var tokenStorageFactory: suspend () -> TokenStorage by NoSetAfterGetWithLazyDefaultFactory { { RoomTokenStorage.getInstance() } }
81+
var tokenStorageFactory: suspend () -> TokenStorage by NoSetAfterGetWithLazyDefaultFactory { getTokenStorageFactory() }
7982

8083
/** The default [TokenEncryptionHandler] for encrypting and decrypting stored [Token]s.*/
81-
var tokenEncryptionHandler: TokenEncryptionHandler by NoSetAfterGetWithLazyDefaultFactory { DefaultTokenEncryptionHandler() }
84+
var tokenEncryptionHandler: TokenEncryptionHandler by NoSetAfterGetWithLazyDefaultFactory { getTokenEncryptionHandler() }
8285

8386
/** The default [CookieJar]. By default, this is [CookieJar.NO_COOKIES]. */
84-
var cookieJar: CookieJar by NoSetAfterGetWithLazyDefaultFactory { CookieJar.NO_COOKIES }
87+
var cookieJar: CookieJar by NoSetAfterGetWithLazyDefaultFactory { getCookieJar() }
8588

8689
/** The default wait time until the web login flow is cancelled after receiving empty redirect response from the web browser.
8790
* This can resolve some issues caused by older devices when invalid redirect results are returned from the older browser. When this is set to a non-zero value, it introduces a
8891
* delay to all redirects when an error is received. */
89-
var loginCancellationDebounceTime: Duration by NoSetAfterGetWithLazyDefaultFactory { 0.seconds }
92+
var loginCancellationDebounceTime: Duration by NoSetAfterGetWithLazyDefaultFactory { getLoginCancellationDebounceTime() }
9093

9194
object Encryption {
9295
/** The default keyAlias for the encryption key that will be used for encrypting the stored Token objects */
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2025-Present Okta, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.okta.authfoundation
17+
18+
import com.okta.authfoundation.client.AccessTokenValidator
19+
import com.okta.authfoundation.client.Cache
20+
import com.okta.authfoundation.client.DefaultAccessTokenValidator
21+
import com.okta.authfoundation.client.DefaultDeviceSecretValidator
22+
import com.okta.authfoundation.client.DefaultIdTokenValidator
23+
import com.okta.authfoundation.client.DeviceSecretValidator
24+
import com.okta.authfoundation.client.IdTokenValidator
25+
import com.okta.authfoundation.client.OidcClock
26+
import com.okta.authfoundation.client.SharedPreferencesCache
27+
import com.okta.authfoundation.credential.DefaultTokenEncryptionHandler
28+
import com.okta.authfoundation.credential.RoomTokenStorage
29+
import com.okta.authfoundation.credential.TokenEncryptionHandler
30+
import com.okta.authfoundation.credential.TokenStorage
31+
import com.okta.authfoundation.events.EventCoordinator
32+
import kotlinx.coroutines.Dispatchers
33+
import okhttp3.Call
34+
import okhttp3.CookieJar
35+
import okhttp3.OkHttpClient
36+
import java.time.Instant
37+
import kotlin.coroutines.CoroutineContext
38+
import kotlin.time.Duration
39+
import kotlin.time.Duration.Companion.seconds
40+
41+
@InternalAuthFoundationApi
42+
object SdkDefaults {
43+
var getOkHttpClientFactory: () -> (() -> Call.Factory) = { { OkHttpClient() } }
44+
var getIoDispatcher: () -> CoroutineContext = { Dispatchers.IO }
45+
var getComputeDispatcher: () -> CoroutineContext = { Dispatchers.Default }
46+
var getEventCoordinator: () -> EventCoordinator = { EventCoordinator(emptyList()) }
47+
var getClock: () -> OidcClock = { OidcClock { Instant.now().epochSecond } }
48+
var getIdTokenValidator: () -> IdTokenValidator = { DefaultIdTokenValidator() }
49+
var getAccessTokenValidator: () -> AccessTokenValidator = { DefaultAccessTokenValidator() }
50+
var getDeviceSecretValidator: () -> DeviceSecretValidator = { DefaultDeviceSecretValidator() }
51+
var getCacheFactory: () -> suspend () -> Cache = { { SharedPreferencesCache.getInstance() } }
52+
var getTokenStorageFactory: () -> suspend () -> TokenStorage = { { RoomTokenStorage.getInstance() } }
53+
var getTokenEncryptionHandler: () -> TokenEncryptionHandler = { DefaultTokenEncryptionHandler() }
54+
var getCookieJar: () -> CookieJar = { CookieJar.NO_COOKIES }
55+
var getLoginCancellationDebounceTime: () -> Duration = { 0.seconds }
56+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2025-Present Okta, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.okta.authfoundation
17+
18+
import io.mockk.MockKAnnotations
19+
import io.mockk.impl.annotations.MockK
20+
import io.mockk.unmockkAll
21+
import okhttp3.CookieJar
22+
import org.hamcrest.CoreMatchers.`is`
23+
import org.hamcrest.CoreMatchers.not
24+
import org.hamcrest.MatcherAssert.assertThat
25+
import org.junit.After
26+
import org.junit.Before
27+
import kotlin.test.Test
28+
29+
class AuthFoundationDefaultsTest {
30+
private lateinit var defaultSdkState: DefaultSdkState
31+
32+
@MockK
33+
private lateinit var sdkDefaultCookieJar: CookieJar
34+
35+
private lateinit var cookieJarDelegate: NoSetAfterGetWithLazyDefaultFactory<CookieJar>
36+
37+
@Before
38+
fun setup() {
39+
MockKAnnotations.init(this)
40+
defaultSdkState = DefaultSdkState(SdkDefaults.getCookieJar)
41+
SdkDefaults.getCookieJar = { sdkDefaultCookieJar }
42+
43+
cookieJarDelegate = NoSetAfterGetWithLazyDefaultFactory { SdkDefaults.getCookieJar() }
44+
}
45+
46+
@After
47+
fun tearDown() {
48+
unmockkAll()
49+
with(defaultSdkState) {
50+
SdkDefaults.getCookieJar = getCookieJar
51+
}
52+
}
53+
54+
@Test
55+
fun `get default cookie jar`() {
56+
val cookieJar by cookieJarDelegate
57+
assertThat(cookieJar, `is`(sdkDefaultCookieJar))
58+
}
59+
60+
@Test
61+
fun `set different sdkDefinedCookieJar, expect new value`() {
62+
val oldCookieJar = sdkDefaultCookieJar
63+
val newCookieJar = CookieJar.NO_COOKIES
64+
65+
sdkDefaultCookieJar = newCookieJar
66+
val cookieJar by cookieJarDelegate
67+
assertThat(cookieJar, `is`(not(oldCookieJar)))
68+
assertThat(cookieJar, `is`(newCookieJar))
69+
}
70+
71+
@Test
72+
fun `set different sdkDefinedCookieJar after accessing it, expect old value`() {
73+
val oldCookieJar = sdkDefaultCookieJar
74+
val newCookieJar = CookieJar.NO_COOKIES
75+
76+
val cookieJar by cookieJarDelegate
77+
assertThat(cookieJar, `is`(oldCookieJar))
78+
assertThat(cookieJar, `is`(not(newCookieJar)))
79+
80+
sdkDefaultCookieJar = newCookieJar
81+
82+
val cookieJar2 by cookieJarDelegate
83+
assertThat(cookieJar2, `is`(oldCookieJar))
84+
assertThat(cookieJar2, `is`(not(newCookieJar)))
85+
}
86+
87+
private class DefaultSdkState(val getCookieJar: () -> CookieJar)
88+
}

auth-foundation/src/test/java/com/okta/authfoundation/client/DefaultIdTokenValidatorTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ class DefaultIdTokenValidatorTest {
118118
@Test fun testMalformedPayload() {
119119
val idTokenClaims = IdTokenClaims(issuer = null)
120120
assertFailsWithMessage(
121-
"Unexpected 'null' literal when non-nullable string was expected",
121+
"Expected string value for a non-null key 'iss', got null literal instead at element: \$.iss\n" +
122+
"JSON input: .....ldsHoJsPzQ\",\"ds_hash\":\"DAeLOFRqifysbgsrbOgbog\",\"nonce\":null}",
122123
"",
123124
idTokenClaims
124125
)

auth-foundation/src/test/java/com/okta/authfoundation/client/NetworkUtilsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ class NetworkingTest {
878878
val response = oktaRule.createOAuth2Client().performRequest(request)
879879

880880
assertThat(events.size).isEqualTo(4)
881-
val lastRetryEvent = events.removeLast()
881+
val lastRetryEvent = events.removeAt(events.lastIndex)
882882
events.forEach { event ->
883883
val exception = assertFailsWith<IllegalStateException> {
884884
event.response.peekBody(Long.MAX_VALUE).string()

auth-foundation/src/test/java/com/okta/authfoundation/client/internal/SdkVersionsRegistryTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import org.robolectric.RobolectricTestRunner
2323

2424
@RunWith(RobolectricTestRunner::class)
2525
class SdkVersionsRegistryTest {
26-
private val defaultAndroidVersion = 19
26+
private val defaultAndroidVersion = 21
2727

2828
@Before fun reset() {
2929
SdkVersionsRegistry.reset()

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ buildscript {
1919
}
2020

2121
plugins {
22-
id 'com.google.devtools.ksp' version '2.0.0-1.0.22' apply false
22+
id 'com.google.devtools.ksp' version '2.0.21-1.0.28' apply false
2323
}
2424

2525
apply plugin: 'org.jetbrains.dokka'

0 commit comments

Comments
 (0)