mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge pull request #7500 from element-hq/feature/account-provider-full-mxid
Let users enter their full Matrix user ID on the account provider screen
This commit is contained in:
+16
-2
@@ -30,6 +30,7 @@ import io.element.android.features.login.impl.login.LoginModeEvent
|
||||
import io.element.android.features.login.impl.login.LoginModeState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.uri.ensureProtocol
|
||||
import io.element.android.libraries.matrix.api.core.MatrixPatterns
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
|
||||
@AssistedInject
|
||||
@@ -87,6 +88,11 @@ class ConfirmAccountProviderPresenter(
|
||||
var submittedAccountProvider by remember { mutableStateOf<String?>(null) }
|
||||
val latestSubmittedAccountProvider by rememberUpdatedState(submittedAccountProvider)
|
||||
|
||||
// A login hint (the full Matrix user ID) captured when the user enters one, so the authentication
|
||||
// server can pre-fill their identity. Null when a plain account provider was entered.
|
||||
var submittedLoginHint by remember { mutableStateOf<String?>(null) }
|
||||
val latestSubmittedLoginHint by rememberUpdatedState(submittedLoginHint)
|
||||
|
||||
// Once the chosen account provider has been validated and persisted, proceed with the actual sign in / sign up.
|
||||
// Reuse the details resolved while validating, so login does not configure (and re-network) the homeserver again.
|
||||
LaunchedEffect(changeServerState.changeServerAction) {
|
||||
@@ -97,7 +103,7 @@ class ConfirmAccountProviderPresenter(
|
||||
isAccountCreation = params.isAccountCreation,
|
||||
homeserverUrl = latestSubmittedAccountProvider.orEmpty().trim(),
|
||||
resolvedHomeserverUrl = null,
|
||||
loginHint = null,
|
||||
loginHint = latestSubmittedLoginHint,
|
||||
preConfiguredDetails = homeServerDetails,
|
||||
)
|
||||
)
|
||||
@@ -116,8 +122,16 @@ class ConfirmAccountProviderPresenter(
|
||||
// renders the full server rather than the typed prefix, and survives the OAuth round-trip.
|
||||
val accountProvider = event.accountProvider.trim()
|
||||
userInput = accountProvider
|
||||
// If the user entered a full Matrix user ID (e.g. "@alice:example.org"), sign in to its
|
||||
// homeserver and pass the ID as a login hint so the authentication server can pre-fill it.
|
||||
// Otherwise treat the input as the account provider host. Only the homeserver is ever
|
||||
// persisted to history, never the user ID.
|
||||
val isUserId = MatrixPatterns.isUserId(accountProvider)
|
||||
// For a user ID the homeserver is the domain part (after the ':'), keeping any port.
|
||||
val host = if (isUserId) accountProvider.substringAfter(":") else accountProvider
|
||||
submittedLoginHint = "mxid:$accountProvider".takeIf { isUserId && !params.isAccountCreation }
|
||||
// Default the scheme to https:// so entering a bare host (e.g. "matrix.org") works.
|
||||
val accountProviderUrl = accountProvider.ensureProtocol()
|
||||
val accountProviderUrl = host.ensureProtocol()
|
||||
submittedAccountProvider = accountProviderUrl
|
||||
changeServerState.eventSink(
|
||||
ChangeServerEvents.ChangeServer(AccountProvider(url = accountProviderUrl))
|
||||
|
||||
+72
@@ -460,6 +460,78 @@ class ConfirmAccountProviderPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - continue with a full matrix id signs in to its homeserver and passes a login hint`() = runTest {
|
||||
val submittedUrls = mutableListOf<String>()
|
||||
val authenticationService = FakeMatrixAuthenticationService(
|
||||
setHomeserverResult = { url ->
|
||||
submittedUrls.add(url)
|
||||
Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true))
|
||||
},
|
||||
)
|
||||
val presenter = createConfirmAccountProviderPresenter(matrixAuthenticationService = authenticationService)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ConfirmAccountProviderEvents.Continue("@alice:example.org"))
|
||||
awaitLoginMode { it is AsyncData.Success }
|
||||
assertThat(submittedUrls.first()).isEqualTo("https://example.org")
|
||||
assertThat(authenticationService.getOAuthUrlLoginHint).isEqualTo("mxid:@alice:example.org")
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - continue with a matrix id keeps the port in the homeserver`() = runTest {
|
||||
val submittedUrls = mutableListOf<String>()
|
||||
val authenticationService = FakeMatrixAuthenticationService(
|
||||
setHomeserverResult = { url ->
|
||||
submittedUrls.add(url)
|
||||
Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true))
|
||||
},
|
||||
)
|
||||
val presenter = createConfirmAccountProviderPresenter(matrixAuthenticationService = authenticationService)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ConfirmAccountProviderEvents.Continue("@alice:example.org:8448"))
|
||||
awaitLoginMode { it is AsyncData.Success }
|
||||
assertThat(submittedUrls.first()).isEqualTo("https://example.org:8448")
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - continue with a plain account provider passes no login hint`() = runTest {
|
||||
val authenticationService = FakeMatrixAuthenticationService(
|
||||
setHomeserverResult = { Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) },
|
||||
)
|
||||
val presenter = createConfirmAccountProviderPresenter(matrixAuthenticationService = authenticationService)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ConfirmAccountProviderEvents.Continue("matrix.org"))
|
||||
awaitLoginMode { it is AsyncData.Success }
|
||||
assertThat(authenticationService.getOAuthUrlLoginHint).isNull()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - continue with a matrix id during account creation passes no login hint`() = runTest {
|
||||
val authenticationService = FakeMatrixAuthenticationService(
|
||||
setHomeserverResult = { Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) },
|
||||
)
|
||||
val presenter = createConfirmAccountProviderPresenter(
|
||||
params = ConfirmAccountProviderPresenter.Params(isAccountCreation = true),
|
||||
matrixAuthenticationService = authenticationService,
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(ConfirmAccountProviderEvents.Continue("@alice:example.org"))
|
||||
awaitLoginMode { it is AsyncData.Success }
|
||||
assertThat(authenticationService.getOAuthUrlLoginHint).isNull()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaits until the emitted state's login mode matches [predicate], skipping the intermediate
|
||||
* account-provider validation states, and returns that state.
|
||||
|
||||
+5
@@ -70,10 +70,15 @@ class FakeMatrixAuthenticationService(
|
||||
return importCreatedSessionLambda(externalSession)
|
||||
}
|
||||
|
||||
/** The login hint passed to the most recent [getOAuthUrl] call. */
|
||||
var getOAuthUrlLoginHint: String? = null
|
||||
private set
|
||||
|
||||
override suspend fun getOAuthUrl(
|
||||
prompt: OAuthPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OAuthDetails> = simulateLongTask {
|
||||
getOAuthUrlLoginHint = loginHint
|
||||
oAuthError?.let { Result.failure(it) } ?: Result.success(AN_OAUTH_DATA)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user