Skip to content

Commit 0ce064c

Browse files
authored
Merge pull request #596 from supabase-community/auth-exceptions
Handle `weak_password` and `session_not_found` auth error codes
2 parents f57f9cc + f269c50 commit 0ce064c

File tree

7 files changed

+98
-12
lines changed

7 files changed

+98
-12
lines changed

GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/Auth.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.github.jan.supabase.SupabaseClient
44
import io.github.jan.supabase.exceptions.HttpRequestException
55
import io.github.jan.supabase.exceptions.RestException
66
import io.github.jan.supabase.gotrue.admin.AdminApi
7+
import io.github.jan.supabase.gotrue.exception.AuthWeakPasswordException
78
import io.github.jan.supabase.gotrue.mfa.MfaApi
89
import io.github.jan.supabase.gotrue.providers.AuthProvider
910
import io.github.jan.supabase.gotrue.providers.ExternalAuthConfigDefaults
@@ -79,12 +80,14 @@ sealed interface Auth : MainPlugin<AuthConfig>, CustomSerializationPlugin {
7980
*
8081
* Example:
8182
* ```kotlin
82-
* val result = gotrue.signUpWith(Email) {
83+
* val result = auth.signUpWith(Email) {
8384
* email = "[email protected]"
8485
* password = "password"
8586
* }
87+
* ```
8688
* or
87-
* gotrue.signUpWith(Google) // Opens the browser to login with google
89+
* ```kotlin
90+
* auth.signUpWith(Google) // Opens the browser to login with google
8891
* ```
8992
*
9093
* @param provider the provider to use for signing up. E.g. [Email], [Phone] or [Google]
@@ -94,6 +97,7 @@ sealed interface Auth : MainPlugin<AuthConfig>, CustomSerializationPlugin {
9497
* @throws RestException or one of its subclasses if receiving an error response
9598
* @throws HttpRequestTimeoutException if the request timed out
9699
* @throws HttpRequestException on network related issues
100+
* @throws AuthWeakPasswordException if using the [Email] or [Phone] provider and the password is too weak. You can get the reasons via [AuthWeakPasswordException.reasons]
97101
*/
98102
suspend fun <C, R, Provider : AuthProvider<C, R>> signUpWith(
99103
provider: Provider,
@@ -106,12 +110,14 @@ sealed interface Auth : MainPlugin<AuthConfig>, CustomSerializationPlugin {
106110
*
107111
* Example:
108112
* ```kotlin
109-
* val result = gotrue.signInWith(Email) {
113+
* val result = auth.signInWith(Email) {
110114
* email = "[email protected]"
111115
* password = "password"
112116
* }
117+
* ```
113118
* or
114-
* gotrue.signInWith(Google) // Opens the browser to login with google
119+
* ```kotlin
120+
* auth.signInWith(Google) // Opens the browser to login with google
115121
* ```
116122
*
117123
* @param provider the provider to use for signing in. E.g. [Email], [Phone] or [Google]

GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/AuthImpl.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import io.github.jan.supabase.exceptions.UnauthorizedRestException
1010
import io.github.jan.supabase.exceptions.UnknownRestException
1111
import io.github.jan.supabase.gotrue.admin.AdminApi
1212
import io.github.jan.supabase.gotrue.admin.AdminApiImpl
13+
import io.github.jan.supabase.gotrue.exception.AuthSessionMissingException
14+
import io.github.jan.supabase.gotrue.exception.AuthWeakPasswordException
1315
import io.github.jan.supabase.gotrue.mfa.MfaApi
1416
import io.github.jan.supabase.gotrue.mfa.MfaApiImpl
1517
import io.github.jan.supabase.gotrue.providers.AuthProvider
@@ -460,6 +462,7 @@ internal class AuthImpl(
460462
override suspend fun parseErrorResponse(response: HttpResponse): RestException {
461463
val errorBody =
462464
response.bodyOrNull<GoTrueErrorResponse>() ?: GoTrueErrorResponse("Unknown error", "")
465+
checkErrorCodes(errorBody)?.let { return it }
463466
return when (response.status) {
464467
HttpStatusCode.Unauthorized -> UnauthorizedRestException(
465468
errorBody.error,
@@ -471,7 +474,6 @@ internal class AuthImpl(
471474
response,
472475
errorBody.description
473476
)
474-
475477
HttpStatusCode.UnprocessableEntity -> BadRequestRestException(
476478
errorBody.error,
477479
response,
@@ -481,6 +483,20 @@ internal class AuthImpl(
481483
}
482484
}
483485

486+
private fun checkErrorCodes(error: GoTrueErrorResponse): RestException? {
487+
return when (error.error) {
488+
AuthWeakPasswordException.CODE -> AuthWeakPasswordException(error.error, error.weakPassword?.reasons ?: emptyList())
489+
AuthSessionMissingException.CODE -> {
490+
authScope.launch {
491+
Auth.logger.e { "Received session not found api error. Clearing session..." }
492+
clearSession()
493+
}
494+
AuthSessionMissingException()
495+
}
496+
else -> null
497+
}
498+
}
499+
484500
@OptIn(SupabaseExperimental::class)
485501
override fun getOAuthUrl(
486502
provider: OAuthProvider,

GoTrue/src/commonMain/kotlin/io/github/jan/supabase/gotrue/GoTrueErrorResponse.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,24 @@ import kotlinx.serialization.builtins.serializer
66
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
77
import kotlinx.serialization.encoding.Decoder
88
import kotlinx.serialization.encoding.Encoder
9+
import kotlinx.serialization.json.Json
910
import kotlinx.serialization.json.JsonDecoder
11+
import kotlinx.serialization.json.decodeFromJsonElement
1012
import kotlinx.serialization.json.jsonObject
1113
import kotlinx.serialization.json.jsonPrimitive
1214

1315
@Serializable(with = GoTrueErrorResponse.Companion::class)
1416
internal data class GoTrueErrorResponse(
1517
val error: String,
16-
val description: String?
18+
val description: String? = null,
19+
val weakPassword: WeakPassword? = null
1720
) {
1821

22+
@Serializable
23+
data class WeakPassword(
24+
val reasons: List<String>
25+
)
26+
1927
companion object : KSerializer<GoTrueErrorResponse> {
2028

2129
override val descriptor = buildClassSerialDescriptor("GoTrueErrorResponse") {
@@ -25,9 +33,12 @@ internal data class GoTrueErrorResponse(
2533
override fun deserialize(decoder: Decoder): GoTrueErrorResponse {
2634
decoder as JsonDecoder
2735
val json = decoder.decodeJsonElement()
28-
val error = json.jsonObject["error"]?.jsonPrimitive?.content ?: json.jsonObject["msg"]?.jsonPrimitive?.content ?: json.toString()
29-
val description = json.jsonObject["error_description"]?.jsonPrimitive?.content
30-
return GoTrueErrorResponse(error, description)
36+
val error = json.jsonObject["error_code"]?.jsonPrimitive?.content ?: "unknown_error"
37+
val description = json.jsonObject["error_description"]?.jsonPrimitive?.content ?: json.jsonObject["msg"]?.jsonPrimitive?.content ?: json.jsonObject["message"]?.jsonPrimitive?.content ?: json.toString()
38+
val weakPassword = if(json.jsonObject.containsKey("weak_password")) {
39+
Json.decodeFromJsonElement<WeakPassword>(json.jsonObject["weak_password"]!!)
40+
} else null
41+
return GoTrueErrorResponse(error, description, weakPassword)
3142
}
3243

3344
override fun serialize(encoder: Encoder, value: GoTrueErrorResponse) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.github.jan.supabase.gotrue.exception
2+
3+
import io.github.jan.supabase.exceptions.RestException
4+
5+
/**
6+
* Base class for rest exceptions thrown by the Auth API.
7+
*/
8+
open class AuthRestException(errorCode: String, message: String): RestException(
9+
error = errorCode,
10+
description = null,
11+
message = message
12+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.github.jan.supabase.gotrue.exception
2+
3+
/**
4+
* Exception thrown when a session is not found.
5+
*/
6+
class AuthSessionMissingException: AuthRestException(
7+
errorCode = CODE,
8+
message = "Session not found. This can happen if the user was logged out or deleted."
9+
) {
10+
11+
internal companion object {
12+
const val CODE = "session_not_found"
13+
}
14+
15+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.github.jan.supabase.gotrue.exception
2+
3+
/**
4+
* Exception thrown when a session is not found.
5+
* @param description The description of the exception.
6+
* @param reasons The reasons why the password is weak.
7+
*/
8+
class AuthWeakPasswordException(
9+
description: String,
10+
val reasons: List<String>
11+
) : AuthRestException(
12+
CODE,
13+
description,
14+
) {
15+
16+
internal companion object {
17+
const val CODE = "weak_password"
18+
}
19+
20+
}

src/commonMain/kotlin/io/github/jan/supabase/exceptions/RestException.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ import io.ktor.client.statement.request
55

66
/**
77
* Base class for all response-related exceptions
8-
* @param error The error returned by supabase
9-
* @param description The error description returned by supabase
8+
*
9+
* Plugins may extend this class to provide more specific exceptions
10+
* @param error The error returned by Supabase
11+
* @param description The error description returned by Supabase
12+
* @see UnauthorizedRestException
13+
* @see BadRequestRestException
14+
* @see NotFoundRestException
15+
* @see UnknownRestException
1016
*/
11-
sealed class RestException(val error: String, val description: String?, message: String): Exception(message) {
17+
open class RestException(val error: String, val description: String?, message: String): Exception(message) {
1218

1319
constructor(error: String, response: HttpResponse, message: String? = null): this(error, message, """
1420
$error${message?.let { " ($it)" } ?: ""}

0 commit comments

Comments
 (0)