Add OIDC support

This commit is contained in:
Benoit Marty 2023-04-20 21:24:07 +02:00 committed by Benoit Marty
parent ff1147e611
commit f1d2f566bc
26 changed files with 789 additions and 44 deletions

View file

@ -26,6 +26,13 @@ fun Throwable.mapAuthenticationException(): Throwable {
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(this.message!!)
is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(this.message!!)
is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(this.message!!)
is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message!!)
is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message!!)
is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message!!)
is RustAuthenticationException.OidcNotStarted -> AuthenticationException.OidcError("OidcNotStarted", message!!)
is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message!!)
else -> this
}
}

View file

@ -23,6 +23,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use {
MatrixHomeServerDetails(
url = url(),
supportsPasswordLogin = supportsPasswordLogin(),
authenticationIssuer = authenticationIssuer()
supportsOidc = supportsOidcLogin(),
)
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.impl.auth
import io.element.android.libraries.matrix.api.auth.OidcConfig
import org.matrix.rustcomponents.sdk.OidcClientMetadata
val oidcClientMetadata: OidcClientMetadata = OidcClientMetadata(
clientName = "Element",
redirectUri = OidcConfig.redirectUri,
clientUri = "https://element.io",
tosUri = "https://element.io/user-terms-of-service",
policyUri = "https://element.io/privacy"
)

View file

@ -24,8 +24,8 @@ import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.impl.RustMatrixClient
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientBuilder
import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl
import org.matrix.rustcomponents.sdk.Session
import org.matrix.rustcomponents.sdk.use
import java.io.File
@ -51,7 +52,12 @@ class RustMatrixAuthenticationService @Inject constructor(
private val sessionStore: SessionStore,
) : MatrixAuthenticationService {
private val authService: RustAuthenticationService = RustAuthenticationService(baseDirectory.absolutePath, null, null)
private val authService: RustAuthenticationService = RustAuthenticationService(
basePath = baseDirectory.absolutePath,
passphrase = null,
oidcClientMetadata = oidcClientMetadata,
customSlidingSyncProxy = null
)
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
override fun isLoggedIn(): Flow<Boolean> {
@ -91,9 +97,9 @@ class RustMatrixAuthenticationService @Inject constructor(
if (homeServerDetails != null) {
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
}
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
override suspend fun login(username: String, password: String): Result<SessionId> =
@ -103,11 +109,55 @@ class RustMatrixAuthenticationService @Inject constructor(
val sessionData = client.use { it.session().toSessionData() }
sessionStore.storeData(sessionData)
SessionId(sessionData.userId)
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
private var pendingUrlForOidcLogin: OidcAuthenticationUrl? = null
override suspend fun getOidcUrl(): Result<OidcDetails> {
return withContext(coroutineDispatchers.io) {
runCatching {
val urlForOidcLogin = authService.urlForOidcLogin()
val url = urlForOidcLogin.loginUrl()
pendingUrlForOidcLogin = urlForOidcLogin
OidcDetails(url)
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
}
}
override suspend fun cancelOidcLogin(): Result<Unit> {
return withContext(coroutineDispatchers.io) {
runCatching {
pendingUrlForOidcLogin?.close()
pendingUrlForOidcLogin = null
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
}
}
/**
* callbackUrl should be the uriRedirect from OidcClientMetadata (with all the parameters)
*/
override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> {
return withContext(coroutineDispatchers.io) {
runCatching {
val urlForOidcLogin = pendingUrlForOidcLogin ?: error("You need to call `getOidcUrl()` first")
val client = authService.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
val sessionData = client.use { it.session().toSessionData() }
pendingUrlForOidcLogin = null
sessionStore.storeData(sessionData)
SessionId(sessionData.userId)
}.mapFailure { failure ->
failure.mapAuthenticationException()
}
}
}
private fun createMatrixClient(client: Client): MatrixClient {
return RustMatrixClient(
client = client,