diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a3c98cce7..7f8efbbb4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This release will bump the Realm file format 24. Opening a file with an older fo * Removed `LogConfiguration`. Log levels and custom loggers can be set with `RealmLog`. (Issue [#1691](https://github.com/realm/realm-kotlin/issues/1691) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1038)) * Removed deprecated `io.realm.kotlin.types.ObjectId`. Use `org.mongodb.kbson.BsonObjectId` or its type alias `org.mongodb.kbson.ObjectId` instead. (Issue [#1749](https://github.com/realm/realm-kotlin/issues/1749) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1082)) * Removed deprecated `RealmClass.isEmbedded`. Class embeddeness can be check with `RealmClassKind.EMBEDDED`. (Issue [#1753](https://github.com/realm/realm-kotlin/issues/1753) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1080)) +* Some authentication related operations will no longer throw specialized `InvalidCredentialsException` and `CredentialsCannotBeLinkedException` but the more general `AuthException` and `ServiceException`. (Issue [#1763](https://github.com/realm/realm-kotlin/issues/1763)/[RKOTLIN-1091](https://jira.mongodb.org/browse/RKOTLIN-1091)) * [Sync] Removed deprecated methods `User.identity` and `User.provider`, user identities can be accessed with the already existing `User.identities`. (Issue [#1751](https://github.com/realm/realm-kotlin/issues/1751) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1083)) * [Sync] `App.allUsers` does no longer return a map, but only a list of users known locally. (Issue [#1751](https://github.com/realm/realm-kotlin/issues/1751) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1083)) * [Sync ]Removed deprecated `DiscardUnsyncedChangesStrategy.onError`. (Issue [#1755](https://github.com/realm/realm-kotlin/issues/1755) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1085)) diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt index 65581130b1..e56f4793f2 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/App.kt @@ -21,7 +21,6 @@ import io.realm.kotlin.mongodb.annotations.ExperimentalEdgeServerApi import io.realm.kotlin.mongodb.auth.EmailPasswordAuth import io.realm.kotlin.mongodb.exceptions.AppException import io.realm.kotlin.mongodb.exceptions.AuthException -import io.realm.kotlin.mongodb.exceptions.InvalidCredentialsException import io.realm.kotlin.mongodb.internal.AppConfigurationImpl import io.realm.kotlin.mongodb.internal.AppImpl import io.realm.kotlin.mongodb.sync.Sync @@ -115,10 +114,6 @@ public interface App { * * @param credentials the credentials representing the type of login. * @return the logged in [User]. - * @throws InvalidCredentialsException if the provided credentials were not correct. Note, only - * [AuthenticationProvider.EMAIL_PASSWORD], [AuthenticationProvider.API_KEY] and - * [AuthenticationProvider.JWT] can throw this exception. Other authentication providers throw - * an [AuthException] instead. * @throws AuthException if a problem occurred when logging in. See the exception message for * further details. * @throws io.realm.kotlin.mongodb.exceptions.ServiceException for other failures that can happen when diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ServiceExceptions.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ServiceExceptions.kt index 6d2a4db3c9..5b9e7f31d4 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ServiceExceptions.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ServiceExceptions.kt @@ -66,8 +66,8 @@ public class BadRequestException internal constructor(message: String) : Service * This exception is considered the top-level or "catch-all" for problems related to user account * actions. The exact reason for the error can be found in [Throwable.message]. * - * Generally, this exception does not need to be caught as more specific subtypes are available. - * These will be documented for the relevant API methods. + * For some error scenarios there are more specific and descriptive subtypes available. + * These are documented for the relevant API methods where they can be thrown. * * @see UserAlreadyConfirmedException * @see UserNotFoundException diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/RealmSyncUtils.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/RealmSyncUtils.kt index b86783b6f0..73fb222306 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/RealmSyncUtils.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/RealmSyncUtils.kt @@ -172,39 +172,14 @@ internal fun convertAppError(appError: AppError): Throwable { // generic `ServiceException`'s. when (appError.code) { ErrorCode.RLM_ERR_INTERNAL_SERVER_ERROR -> { - if (msg.contains("linking an anonymous identity is not allowed") || // Trying to link an anonymous account to a named one. - msg.contains("linking a local-userpass identity is not allowed") // Trying to link two email logins with each other - ) { - CredentialsCannotBeLinkedException(msg) - } else { - ServiceException(msg) - } + ServiceException(msg) } ErrorCode.RLM_ERR_INVALID_SESSION -> { - if (msg.contains("a user already exists with the specified provider")) { - CredentialsCannotBeLinkedException(msg) - } else { - ServiceException(msg) - } + ServiceException(msg) } ErrorCode.RLM_ERR_USER_DISABLED, ErrorCode.RLM_ERR_AUTH_ERROR -> { - // Some auth providers return a generic AuthError when - // invalid credentials are presented. We make a best effort - // to map these to a more sensible `InvalidCredentialsExceptions` - if (msg.contains("invalid API key")) { - // API Key - // See https://github.com/10gen/baas/blob/master/authprovider/providers/apikey/provider.go - InvalidCredentialsException(msg) - } else if (msg.contains("invalid custom auth token:")) { - // Custom JWT - // See https://github.com/10gen/baas/blob/master/authprovider/providers/custom/provider.go - InvalidCredentialsException(msg) - } else { - // It does not look possible to reliably detect Facebook, Google and Apple - // invalid tokens: https://github.com/10gen/baas/blob/master/authprovider/providers/oauth2/oauth.go#L139 - AuthException(msg) - } + AuthException(msg) } ErrorCode.RLM_ERR_USER_NOT_FOUND -> { UserNotFoundException(msg) diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/UserImpl.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/UserImpl.kt index 3b41746c5b..22bb208e00 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/UserImpl.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/UserImpl.kt @@ -25,8 +25,6 @@ import io.realm.kotlin.mongodb.Functions import io.realm.kotlin.mongodb.User import io.realm.kotlin.mongodb.UserIdentity import io.realm.kotlin.mongodb.auth.ApiKeyAuth -import io.realm.kotlin.mongodb.exceptions.CredentialsCannotBeLinkedException -import io.realm.kotlin.mongodb.exceptions.ServiceException import io.realm.kotlin.mongodb.mongo.MongoClient import kotlinx.coroutines.channels.Channel import org.mongodb.kbson.ExperimentalKBsonSerializerApi @@ -153,29 +151,17 @@ public class UserImpl( if (state != User.State.LOGGED_IN) { throw IllegalStateException("User must be logged in, in order to link credentials to it.") } - try { - Channel>(1).use { channel -> - RealmInterop.realm_app_link_credentials( - app.nativePointer, - nativePointer, - (credentials as CredentialsImpl).nativePointer, - channelResultCallback(channel) { userPointer -> - UserImpl(userPointer, app) - } - ) - channel.receive().getOrThrow() - return this - } - } catch (ex: ServiceException) { - // Linking an account with itself throws a different error code than other linking errors: - // It is unclear if this error is shared between other error scenarios, so for now, - // we remap the exception type here instead of in the generic handler in - // `RealmSyncUtils.kt`. - if (ex.message?.contains("[Service][InvalidSession(2)] a user already exists with the specified provider.") == true) { - throw CredentialsCannotBeLinkedException(ex.message!!) - } else { - throw ex - } + Channel>(1).use { channel -> + RealmInterop.realm_app_link_credentials( + app.nativePointer, + nativePointer, + (credentials as CredentialsImpl).nativePointer, + channelResultCallback(channel) { userPointer -> + UserImpl(userPointer, app) + } + ) + channel.receive().getOrThrow() + return this } } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt index 55f9b94eb5..06ae7fc104 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AppTests.kt @@ -33,7 +33,7 @@ import io.realm.kotlin.mongodb.LoggedOut import io.realm.kotlin.mongodb.Removed import io.realm.kotlin.mongodb.User import io.realm.kotlin.mongodb.annotations.ExperimentalEdgeServerApi -import io.realm.kotlin.mongodb.exceptions.InvalidCredentialsException +import io.realm.kotlin.mongodb.exceptions.AuthException import io.realm.kotlin.mongodb.exceptions.ServiceException import io.realm.kotlin.mongodb.sync.SyncConfiguration import io.realm.kotlin.test.mongodb.SyncServerConfig @@ -144,7 +144,7 @@ class AppTests { null } }?.let { credentials: Credentials -> - assertFailsWith { + assertFailsWith { app.login(credentials) } } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/CredentialsTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/CredentialsTests.kt index b666ebb3dc..d76391c980 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/CredentialsTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/CredentialsTests.kt @@ -369,7 +369,7 @@ class CredentialsTests { payload = mapOf("mail" to TestHelper.randomEmail(), "id" to 0) ) - assertFailsWithMessage("Error: Authentication failed.") { + assertFailsWithMessage("unauthorized") { runBlocking { app.login(credentials) } @@ -383,7 +383,7 @@ class CredentialsTests { } fail() } catch (error: AppException) { - assertTrue(error.message!!.contains("authentication via"), error.message) + assertTrue(error.message!!.contains("unauthorized"), error.message) } } } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/UserTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/UserTests.kt index 6fb44e5f98..e51b042d48 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/UserTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/UserTests.kt @@ -459,11 +459,11 @@ class UserTests { app.emailPasswordAuth.registerUser(otherEmail, otherPassword) val credentials = Credentials.emailPassword(otherEmail, otherPassword) - assertFailsWith { + assertFailsWith { anonUser.linkCredentials(credentials) }.let { assertTrue( - it.message!!.contains("linking a local-userpass identity is not allowed when one is already linked"), + it.message!!.contains("unauthorized"), it.message ) } @@ -478,11 +478,11 @@ class UserTests { val (email2, password2) = randomEmail() to "123456" app.emailPasswordAuth.registerUser(email2, password2) val credentials2 = Credentials.emailPassword(email2, password2) - assertFailsWith { + assertFailsWith { emailUser1.linkCredentials(credentials2) }.let { assertTrue( - it.message!!.contains("linking a local-userpass identity is not allowed when one is already linked"), + it.message!!.contains("unauthorized"), it.message ) } @@ -512,11 +512,11 @@ class UserTests { app.emailPasswordAuth.registerUser(email, password) val creds = Credentials.emailPassword(email, password) app.login(creds) - assertFailsWith { + assertFailsWith { anonUser.linkCredentials(creds) }.let { assertTrue( - it.message!!.contains("a user already exists with the specified provider"), + it.message!!.contains("unauthorized"), it.message ) }