Skip to content

Commit c61ee59

Browse files
authored
Improve the callback uri format and customization. (#4664)
* Remove unused SUPPORT_EMAIL_ADDRESS * Improve the callback uri format and customization. Use io.element.android for the scheme of Oidc redirection for Element X. For nightly the scheme will be io.element.android.nightly For debug the scheme will be io.element.android.debug Element Pro is using `io.element`
1 parent 1904c98 commit c61ee59

File tree

20 files changed

+166
-49
lines changed

20 files changed

+166
-49
lines changed

app/build.gradle.kts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,25 @@ android {
106106
logger.warnInBox("Building ${defaultConfig.applicationId} ($baseAppName)")
107107

108108
buildTypes {
109+
val oidcRedirectSchemeBase = BuildTimeConfig.METADATA_HOST_REVERSED ?: "io.element.android"
109110
getByName("debug") {
110111
resValue("string", "app_name", "$baseAppName dbg")
112+
resValue(
113+
"string",
114+
"login_redirect_scheme",
115+
"$oidcRedirectSchemeBase.debug",
116+
)
111117
applicationIdSuffix = ".debug"
112118
signingConfig = signingConfigs.getByName("debug")
113119
}
114120

115121
getByName("release") {
116122
resValue("string", "app_name", baseAppName)
123+
resValue(
124+
"string",
125+
"login_redirect_scheme",
126+
oidcRedirectSchemeBase,
127+
)
117128
signingConfig = signingConfigs.getByName("debug")
118129

119130
postprocessing {
@@ -131,6 +142,11 @@ android {
131142
applicationIdSuffix = ".nightly"
132143
versionNameSuffix = "-nightly"
133144
resValue("string", "app_name", "$baseAppName nightly")
145+
resValue(
146+
"string",
147+
"login_redirect_scheme",
148+
"$oidcRedirectSchemeBase.nightly",
149+
)
134150
matchingFallbacks += listOf("release")
135151
signingConfig = signingConfigs.getByName("nightly")
136152

@@ -284,6 +300,7 @@ dependencies {
284300
testImplementation(libs.test.truth)
285301
testImplementation(libs.test.turbine)
286302
testImplementation(projects.libraries.matrix.test)
303+
testImplementation(projects.services.toolbox.test)
287304

288305
koverDependencies()
289306
}

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@
6060
<category android:name="android.intent.category.DEFAULT" />
6161
<category android:name="android.intent.category.BROWSABLE" />
6262

63-
<!-- Note: the scheme must match the scheme of the value of OidcConfig.REDIRECT_URI -->
64-
<data android:scheme="io.element" />
63+
<data android:scheme="@string/login_redirect_scheme" />
6564
</intent-filter>
6665
<!--
6766
Element web links
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.x.oidc
9+
10+
import com.squareup.anvil.annotations.ContributesBinding
11+
import io.element.android.libraries.di.AppScope
12+
import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider
13+
import io.element.android.services.toolbox.api.strings.StringProvider
14+
import io.element.android.x.R
15+
import javax.inject.Inject
16+
17+
@ContributesBinding(AppScope::class)
18+
class DefaultOidcRedirectUrlProvider @Inject constructor(
19+
private val stringProvider: StringProvider,
20+
) : OidcRedirectUrlProvider {
21+
override fun provide() = buildString {
22+
append(stringProvider.getString(R.string.login_redirect_scheme))
23+
append(":/")
24+
}
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.x.oidc
9+
10+
import com.google.common.truth.Truth.assertThat
11+
import io.element.android.services.toolbox.test.strings.FakeStringProvider
12+
import io.element.android.x.R
13+
import org.junit.Test
14+
15+
class DefaultOidcRedirectUrlProviderTest {
16+
@Test
17+
fun `test provide`() {
18+
val stringProvider = FakeStringProvider(
19+
defaultResult = "str"
20+
)
21+
val sut = DefaultOidcRedirectUrlProvider(
22+
stringProvider = stringProvider,
23+
)
24+
val result = sut.provide()
25+
assertThat(result).isEqualTo("str:/")
26+
assertThat(stringProvider.lastResIdParam).isEqualTo(R.string.login_redirect_scheme)
27+
}
28+
}

appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData
2020
import io.element.android.libraries.matrix.test.A_ROOM_ID
2121
import io.element.android.libraries.matrix.test.A_SESSION_ID
2222
import io.element.android.libraries.matrix.test.A_THREAD_ID
23+
import io.element.android.libraries.matrix.test.auth.FakeOidcRedirectUrlProvider
2324
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
2425
import io.element.android.libraries.oidc.api.OidcAction
2526
import io.element.android.libraries.oidc.impl.DefaultOidcIntentResolver
26-
import io.element.android.libraries.oidc.impl.OidcUrlParser
27+
import io.element.android.libraries.oidc.impl.DefaultOidcUrlParser
2728
import io.element.android.tests.testutils.lambda.lambdaError
2829
import org.junit.Assert.assertThrows
2930
import org.junit.Test
@@ -119,7 +120,7 @@ class IntentResolverTest {
119120
val sut = createIntentResolver()
120121
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
121122
action = Intent.ACTION_VIEW
122-
data = "io.element:/callback?error=access_denied&state=IFF1UETGye2ZA8pO".toUri()
123+
data = "io.element.android:/?error=access_denied&state=IFF1UETGye2ZA8pO".toUri()
123124
}
124125
val result = sut.resolve(intent)
125126
assertThat(result).isEqualTo(
@@ -134,13 +135,13 @@ class IntentResolverTest {
134135
val sut = createIntentResolver()
135136
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
136137
action = Intent.ACTION_VIEW
137-
data = "io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB".toUri()
138+
data = "io.element.android:/?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB".toUri()
138139
}
139140
val result = sut.resolve(intent)
140141
assertThat(result).isEqualTo(
141142
ResolvedIntent.Oidc(
142143
oidcAction = OidcAction.Success(
143-
url = "io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB"
144+
url = "io.element.android:/?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB"
144145
)
145146
)
146147
)
@@ -151,7 +152,7 @@ class IntentResolverTest {
151152
val sut = createIntentResolver()
152153
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
153154
action = Intent.ACTION_VIEW
154-
data = "io.element:/callback/invalid".toUri()
155+
data = "io.element.android:/invalid".toUri()
155156
}
156157
assertThrows(IllegalStateException::class.java) {
157158
sut.resolve(intent)
@@ -246,7 +247,9 @@ class IntentResolverTest {
246247
return IntentResolver(
247248
deeplinkParser = DeeplinkParser(),
248249
oidcIntentResolver = DefaultOidcIntentResolver(
249-
oidcUrlParser = OidcUrlParser()
250+
oidcUrlParser = DefaultOidcUrlParser(
251+
oidcRedirectUrlProvider = FakeOidcRedirectUrlProvider(),
252+
)
250253
),
251254
permalinkParser = FakePermalinkParser(
252255
result = permalinkParserResult

docs/oidc.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ Server list: https://github.com/element-hq/oidc-playground
1111
Metadata iOS: (from https://github.com/element-hq/element-x-ios/blob/5f9d07377cebc4f21d9668b1a25f6e3bb22f64a1/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift#L28)
1212

1313
clientName: InfoPlistReader.main.bundleDisplayName,
14-
redirectUri: "io.element:/callback",
14+
redirectUri: "io.element.android:/",
1515
clientUri: "https://element.io",
1616
tosUri: "https://element.io/user-terms-of-service",
1717
policyUri: "https://element.io/privacy"
1818

1919

2020
Android:
2121
clientName = "Element",
22-
redirectUri = "io.element:/callback",
22+
redirectUri = "io.element.android:/",
2323
clientUri = "https://element.io",
2424
tosUri = "https://element.io/user-terms-of-service",
2525
policyUri = "https://element.io/privacy"

libraries/matrix/api/build.gradle.kts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ android {
2727
name = "CLIENT_URI",
2828
value = BuildTimeConfig.URL_WEBSITE ?: "https://element.io"
2929
)
30-
buildConfigFieldStr(
31-
name = "REDIRECT_URI",
32-
value = buildString {
33-
append(BuildTimeConfig.METADATA_HOST_REVERSED ?: "io.element")
34-
append(":/callback")
35-
}
36-
)
3730
buildConfigFieldStr(
3831
name = "LOGO_URI",
3932
value = BuildTimeConfig.URL_LOGO ?: "https://element.io/mobile-icon.png"

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ import io.element.android.libraries.matrix.api.BuildConfig
1212
object OidcConfig {
1313
const val CLIENT_URI = BuildConfig.CLIENT_URI
1414

15-
// Notes:
16-
// 1. the scheme must match the value declared in the AndroidManifest.xml
17-
// 2. the scheme must be the reverse of the host of CLIENT_URI
18-
const val REDIRECT_URI = BuildConfig.REDIRECT_URI
19-
2015
// Note: host must match with the host of CLIENT_URI
2116
const val LOGO_URI = BuildConfig.LOGO_URI
2217

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.matrix.api.auth
9+
10+
interface OidcRedirectUrlProvider {
11+
fun provide(): String
12+
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ package io.element.android.libraries.matrix.impl.auth
99

1010
import io.element.android.libraries.core.meta.BuildMeta
1111
import io.element.android.libraries.matrix.api.auth.OidcConfig
12+
import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider
1213
import org.matrix.rustcomponents.sdk.OidcConfiguration
1314
import javax.inject.Inject
1415

1516
class OidcConfigurationProvider @Inject constructor(
1617
private val buildMeta: BuildMeta,
18+
private val oidcRedirectUrlProvider: OidcRedirectUrlProvider,
1719
) {
1820
fun get(): OidcConfiguration = OidcConfiguration(
1921
clientName = buildMeta.applicationName,
20-
redirectUri = OidcConfig.REDIRECT_URI,
22+
redirectUri = oidcRedirectUrlProvider.provide(),
2123
clientUri = OidcConfig.CLIENT_URI,
2224
logoUri = OidcConfig.LOGO_URI,
2325
tosUri = OidcConfig.TOS_URI,

0 commit comments

Comments
 (0)