diff --git a/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/Config.kt b/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/Config.kt index c3943fb9113..d846f78c70f 100644 --- a/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/Config.kt +++ b/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/Config.kt @@ -47,10 +47,12 @@ data class DeviceConfig( * @property supportedEngines A set of supported sync engines, exposed via [GlobalSyncableStoreProvider]. * @property syncPeriodInMinutes Optional, how frequently periodic sync should happen. If this is `null`, * periodic syncing will be disabled. + * @property tokenServerUrl Optional, override default tokenServerUrl to support self-hosted sync storage. */ data class SyncConfig( val supportedEngines: Set, - val syncPeriodInMinutes: Long? = null + val syncPeriodInMinutes: Long? = null, + val tokenServerUrl: String? = null ) /** diff --git a/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/FxaAccountManager.kt b/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/FxaAccountManager.kt index c53e080022a..99d7112cbf2 100644 --- a/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/FxaAccountManager.kt +++ b/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/FxaAccountManager.kt @@ -908,10 +908,16 @@ open class FxaAccountManager( return } + var tokenServerUrl = syncConfig?.tokenServerUrl + if (tokenServerUrl == null) { + tokenServerUrl = account.getTokenServerEndpointURL() + } + + logger.info("caching tokenServerUrl: $tokenServerUrl") // NB: this call will inform authErrorRegistry in case an auth error is encountered. account.getAccessTokenAsync(SCOPE_SYNC).await()?.let { SyncAuthInfoCache(context).setToCache( - it.asSyncAuthInfo(account.getTokenServerEndpointURL()) + it.asSyncAuthInfo(tokenServerUrl) ) } } diff --git a/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/FxaAccountManagerTest.kt b/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/FxaAccountManagerTest.kt index e36ed4fb31b..78b3f0a52b6 100644 --- a/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/FxaAccountManagerTest.kt +++ b/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/FxaAccountManagerTest.kt @@ -372,6 +372,62 @@ class FxaAccountManagerTest { assertFalse(manager.isSyncActive()) } + @Test + fun `updating sync config with optional tokenServerUrl`() = runBlocking { + val accountStorage: AccountStorage = mock() + val profile = Profile("testUid", "test@example.com", null, "Test Profile") + val constellation: DeviceConstellation = mockDeviceConstellation() + + val syncAccessTokenExpiresAt = System.currentTimeMillis() + 10 * 60 * 1000L + val account = StatePersistenceTestableAccount(profile, constellation, tokenServerEndpointUrl = "https://some.server.com/test") { + AccessTokenInfo( + SCOPE_SYNC, + "arda", + OAuthScopedKey("kty-test", SCOPE_SYNC, "kid-test", "k-test"), + syncAccessTokenExpiresAt + ) + } + + // With a sync config including a tokenServerUrl this time + var latestSyncManager: TestSyncManager? = null + val syncConfig = SyncConfig(setOf(SyncEngine.History), syncPeriodInMinutes = 120L, tokenServerUrl = "https://different.server.com/test") + val manager = object : TestableFxaAccountManager( + context = testContext, + config = ServerConfig.release("dummyId", "http://auth-url/redirect"), + storage = accountStorage, + capabilities = setOf(DeviceCapability.SEND_TAB), + syncConfig = syncConfig, + coroutineContext = this@runBlocking.coroutineContext, + block = { account } + ) { + override fun createSyncManager(config: SyncConfig): SyncManager { + return TestSyncManager(config).also { latestSyncManager = it } + } + } + + `when`(constellation.ensureCapabilitiesAsync(any())).thenReturn(CompletableDeferred(true)) + // We have an account at the start. + `when`(accountStorage.read()).thenReturn(account) + + val syncStatusObserver = TestSyncStatusObserver() + val lifecycleOwner: LifecycleOwner = mock() + val lifecycle: Lifecycle = mock() + `when`(lifecycle.currentState).thenReturn(Lifecycle.State.STARTED) + `when`(lifecycleOwner.lifecycle).thenReturn(lifecycle) + manager.registerForSyncEvents(syncStatusObserver, lifecycleOwner, true) + manager.initAsync().await() + + // Make sure that tokenServerUrl was cached correctly. + assertFalse(SyncAuthInfoCache(testContext).expired()) + val cachedAuthInfo = SyncAuthInfoCache(testContext).getCached() + assertNotNull(cachedAuthInfo) + assertEquals("https://different.server.com/test", cachedAuthInfo!!.tokenServerUrl) + + // Can trigger syncs. + manager.syncNowAsync(SyncReason.User).await() + verify(latestSyncManager!!.dispatcher.inner, times(1)).syncNow(SyncReason.User, debounce = false) + } + @Test fun `migrating an account via copyAccountAsync - creating a new session token`() = runBlocking { // We'll test three scenarios: @@ -1842,4 +1898,4 @@ class FxaAccountManagerTest { `when`(c.refreshDevicesAsync()).thenReturn(CompletableDeferred(true)) return c } -} \ No newline at end of file +} diff --git a/docs/changelog.md b/docs/changelog.md index 60df91fa739..1ce12187b2f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -25,6 +25,8 @@ permalink: /changelog/ * Labels in labeled metrics now have a correct, and slightly stricter, regular expression. See [label format](https://mozilla.github.io/glean/user/metrics/index.html#label-format) for more information. +* **services-firefox-accounts** + * Adds an optional `tokenServerUrl` to `SyncConfig` to support self-hosted sync. # 32.0.0