Skip to content

Commit a43b728

Browse files
committed
Merge branch 'master' into not-authenticated-reason
# Conflicts: # Auth/src/commonTest/kotlin/AuthTest.kt
2 parents b5c09a1 + 56b3f18 commit a43b728

File tree

20 files changed

+161
-47
lines changed

20 files changed

+161
-47
lines changed

Auth/src/androidMain/kotlin/io/github/jan/supabase/auth/Utils.android.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ internal actual suspend fun Auth.startExternalAuth(
1414
getUrl: suspend (redirectTo: String?) -> String,
1515
onSessionSuccess: suspend (UserSession) -> Unit
1616
) {
17-
supabaseClient.openExternalUrl(getUrl(redirectUrl))
17+
config.urlLauncher.openUrl(supabaseClient, getUrl(redirectUrl))
1818
}

Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthConfig.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package io.github.jan.supabase.auth
22

33
import io.github.jan.supabase.SupabaseClientBuilder
44
import io.github.jan.supabase.SupabaseSerializer
5+
import io.github.jan.supabase.annotations.SupabaseExperimental
56
import io.github.jan.supabase.plugins.CustomSerializationConfig
67
import io.github.jan.supabase.plugins.MainConfig
78
import kotlinx.coroutines.CoroutineDispatcher
@@ -96,6 +97,12 @@ open class AuthConfigDefaults : MainConfig() {
9697
*/
9798
var enableLifecycleCallbacks: Boolean = true
9899

100+
/**
101+
* The URL launcher used to open OAuth links in the system browser.
102+
*/
103+
@SupabaseExperimental
104+
var urlLauncher: UrlLauncher = UrlLauncher.DEFAULT
105+
99106
}
100107

101108
/**
@@ -148,6 +155,7 @@ val AuthConfig.deepLinkOrNull: String?
148155
* @param enableLifecycleCallbacks Whether to stop auto-refresh on focus loss, and resume it on focus again. Currently only supported on Android.
149156
* @see AuthConfigDefaults
150157
*/
158+
@Deprecated("Use the new minimalConfig function instead", ReplaceWith("minimalConfig()"))
151159
@Suppress("LongParameterList", "unused")
152160
fun AuthConfigDefaults.minimalSettings(
153161
alwaysAutoRefresh: Boolean = false,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.github.jan.supabase.auth
2+
3+
/**
4+
* Applies minimal configuration to the [AuthConfig]. This is useful for server side applications, where you don't need to store the session or code verifier.
5+
* @see AuthConfigDefaults
6+
*/
7+
fun AuthConfigDefaults.minimalConfig() {
8+
this.alwaysAutoRefresh = false
9+
this.autoLoadFromStorage = false
10+
this.autoSaveToStorage = false
11+
this.sessionManager = MemorySessionManager()
12+
this.codeVerifierCache = MemoryCodeVerifierCache()
13+
this.enableLifecycleCallbacks = false
14+
}
15+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.github.jan.supabase.auth
2+
3+
import io.github.jan.supabase.SupabaseClient
4+
import io.github.jan.supabase.annotations.SupabaseExperimental
5+
6+
/**
7+
* A [UrlLauncher] is used to open a URL in the system browser.
8+
*/
9+
@SupabaseExperimental
10+
fun interface UrlLauncher {
11+
12+
/**
13+
* Open the given URL in the system browser.
14+
* @param url The URL to open.
15+
*/
16+
suspend fun openUrl(supabase: SupabaseClient, url: String)
17+
18+
companion object {
19+
20+
/**
21+
* Default implementation of [UrlLauncher] that opens the URL in the system browser.
22+
*/
23+
val DEFAULT = UrlLauncher { supabase, url ->
24+
supabase.openExternalUrl(url)
25+
}
26+
27+
}
28+
29+
}

Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/builtin/DefaultAuthProvider.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.github.jan.supabase.auth.providers.builtin
22

33
import io.github.jan.supabase.SupabaseClient
4-
import io.github.jan.supabase.annotations.SupabaseExperimental
54
import io.github.jan.supabase.annotations.SupabaseInternal
5+
import io.github.jan.supabase.auth.Auth
66
import io.github.jan.supabase.auth.AuthImpl
77
import io.github.jan.supabase.auth.FlowType
88
import io.github.jan.supabase.auth.auth
@@ -12,6 +12,7 @@ import io.github.jan.supabase.auth.providers.AuthProvider
1212
import io.github.jan.supabase.auth.putCodeChallenge
1313
import io.github.jan.supabase.auth.redirectTo
1414
import io.github.jan.supabase.auth.user.UserSession
15+
import io.github.jan.supabase.logging.w
1516
import io.github.jan.supabase.putJsonObject
1617
import io.github.jan.supabase.supabaseJson
1718
import io.ktor.client.call.body
@@ -20,6 +21,7 @@ import kotlinx.serialization.Serializable
2021
import kotlinx.serialization.json.JsonObject
2122
import kotlinx.serialization.json.buildJsonObject
2223
import kotlinx.serialization.json.decodeFromJsonElement
24+
import kotlinx.serialization.json.jsonObject
2325

2426
/**
2527
* A default authentication provider
@@ -65,7 +67,6 @@ sealed interface DefaultAuthProvider<C, R> : AuthProvider<C, R> {
6567
}
6668
}
6769

68-
@OptIn(SupabaseExperimental::class)
6970
override suspend fun signUp(
7071
supabaseClient: SupabaseClient,
7172
onSuccess: suspend (UserSession) -> Unit,
@@ -95,10 +96,16 @@ sealed interface DefaultAuthProvider<C, R> : AuthProvider<C, R> {
9596
redirectUrl?.let { redirectTo(it) }
9697
}
9798
val json = response.body<JsonObject>()
98-
if(json.containsKey("access_token")) {
99-
val userSession = supabaseJson.decodeFromJsonElement<UserSession>(json)
100-
onSuccess(userSession)
101-
return null
99+
if (json.containsKey("access_token")) {
100+
runCatching {
101+
val userSession = supabaseJson.decodeFromJsonElement<UserSession>(json)
102+
onSuccess(userSession)
103+
val userJson = json["user"]?.jsonObject ?: buildJsonObject { }
104+
return decodeResult(userJson)
105+
}.onFailure { exception ->
106+
Auth.logger.w(exception) { "Failed to decode user info" }
107+
return null
108+
}
102109
}
103110
return decodeResult(json)
104111
}

Auth/src/commonTest/kotlin/AccessTokenTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import io.github.jan.supabase.auth.Auth
22
import io.github.jan.supabase.auth.auth
3-
import io.github.jan.supabase.auth.minimalSettings
3+
import io.github.jan.supabase.auth.minimalConfig
44
import io.github.jan.supabase.auth.resolveAccessToken
55
import io.github.jan.supabase.testing.createMockedSupabaseClient
66
import kotlinx.coroutines.test.runTest
@@ -16,7 +16,7 @@ class AccessTokenTest {
1616
val client = createMockedSupabaseClient(
1717
configuration = {
1818
install(Auth) {
19-
minimalSettings()
19+
minimalConfig()
2020
}
2121
}
2222
)
@@ -61,7 +61,7 @@ class AccessTokenTest {
6161
val client = createMockedSupabaseClient(
6262
configuration = {
6363
install(Auth) {
64-
minimalSettings()
64+
minimalConfig()
6565
}
6666
}
6767
)

Auth/src/commonTest/kotlin/AdminApiTest.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import io.github.jan.supabase.auth.SignOutScope
44
import io.github.jan.supabase.auth.admin.LinkType
55
import io.github.jan.supabase.auth.admin.generateLinkFor
66
import io.github.jan.supabase.auth.auth
7-
import io.github.jan.supabase.auth.minimalSettings
7+
import io.github.jan.supabase.auth.minimalConfig
88
import io.github.jan.supabase.auth.user.UserInfo
99
import io.github.jan.supabase.auth.user.UserMfaFactor
1010
import io.github.jan.supabase.testing.assertMethodIs
@@ -17,7 +17,6 @@ import io.ktor.client.engine.mock.respond
1717
import io.ktor.http.HttpMethod
1818
import kotlinx.coroutines.test.runTest
1919
import kotlinx.datetime.Clock
20-
import kotlinx.serialization.encodeToString
2120
import kotlinx.serialization.json.Json
2221
import kotlinx.serialization.json.boolean
2322
import kotlinx.serialization.json.buildJsonObject
@@ -32,7 +31,7 @@ class AdminApiTest {
3231

3332
private val configuration: SupabaseClientBuilder.() -> Unit = {
3433
install(Auth) {
35-
minimalSettings()
34+
minimalConfig()
3635
}
3736
}
3837

Auth/src/commonTest/kotlin/AuthApiTest.kt

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import io.github.jan.supabase.auth.OtpType
66
import io.github.jan.supabase.auth.PKCEConstants
77
import io.github.jan.supabase.auth.SignOutScope
88
import io.github.jan.supabase.auth.auth
9-
import io.github.jan.supabase.auth.minimalSettings
9+
import io.github.jan.supabase.auth.minimalConfig
1010
import io.github.jan.supabase.auth.providers.Google
1111
import io.github.jan.supabase.auth.providers.builtin.Email
1212
import io.github.jan.supabase.auth.providers.builtin.IDToken
@@ -39,13 +39,13 @@ class AuthRequestTest {
3939

4040
private val configuration: SupabaseClientBuilder.() -> Unit = {
4141
install(Auth) {
42-
minimalSettings()
42+
minimalConfig()
4343
flowType = FlowType.PKCE
4444
}
4545
}
4646

4747
@Test
48-
fun testSignUpWithEmailNoAutoconfirm() {
48+
fun testSignUpWithEmailNoAutoConfirm() {
4949
runTest {
5050
val expectedEmail = "[email protected]"
5151
val expectedPassword = "password"
@@ -81,7 +81,43 @@ class AuthRequestTest {
8181
}
8282

8383
@Test
84-
fun testSignUpWithEmailAutoconfirm() {
84+
fun testSignUpWithEmailAutoConfirm() {
85+
runTest {
86+
val expectedEmail = "[email protected]"
87+
val expectedPassword = "password"
88+
val captchaToken = "captchaToken"
89+
val userData = buildJsonObject {
90+
put("key", "value")
91+
}
92+
val client = createMockedSupabaseClient(configuration = configuration) {
93+
val body = it.body.toJsonElement().jsonObject
94+
val metaSecurity = body["gotrue_meta_security"]!!.jsonObject
95+
assertMethodIs(HttpMethod.Post, it.method)
96+
assertPathIs("/signup", it.url.pathAfterVersion())
97+
assertEquals(expectedEmail, body["email"]?.jsonPrimitive?.content)
98+
assertEquals(expectedPassword, body["password"]?.jsonPrimitive?.content)
99+
assertEquals(captchaToken, metaSecurity["captcha_token"]?.jsonPrimitive?.content)
100+
assertEquals(userData, body["data"]!!.jsonObject)
101+
containsCodeChallenge(body)
102+
respondJson(
103+
sampleSessionWithUserData(email = "[email protected]", phone = "+1234567890")
104+
)
105+
}
106+
val user = client.auth.signUpWith(Email) {
107+
email = expectedEmail
108+
password = expectedPassword
109+
this.captchaToken = captchaToken
110+
data = userData
111+
}
112+
assertNotNull(user)
113+
assertEquals(expectedEmail, user?.email, "Email should be equal")
114+
assertNotNull(client.auth.currentSessionOrNull(), "Session should not be null")
115+
assertEquals(client.auth.sessionSource(), SessionSource.SignUp(Email))
116+
}
117+
}
118+
119+
@Test
120+
fun testSignUpWithEmailAutoConfirmWithoutUserData() {
85121
runTest {
86122
val expectedEmail = "[email protected]"
87123
val expectedPassword = "password"
@@ -116,7 +152,7 @@ class AuthRequestTest {
116152
}
117153

118154
@Test
119-
fun testSignUpWithPhoneAutoconfirm() {
155+
fun testSignUpWithPhoneAutoConfirm() {
120156
runTest {
121157
val expectedPhone = "+1234567890"
122158
val expectedPassword = "password"
@@ -135,7 +171,7 @@ class AuthRequestTest {
135171
assertEquals(userData, body["data"]!!.jsonObject)
136172
containsCodeChallenge(body)
137173
respondJson(
138-
sampleUserSession()
174+
sampleSessionWithUserData()
139175
)
140176
}
141177
val user = client.auth.signUpWith(Phone) {
@@ -144,14 +180,14 @@ class AuthRequestTest {
144180
this.captchaToken = captchaToken
145181
data = userData
146182
}
147-
assertNull(user)
183+
assertNotNull(user)
148184
assertNotNull(client.auth.currentSessionOrNull(), "Session should not be null")
149185
assertEquals(client.auth.sessionSource(), SessionSource.SignUp(Phone))
150186
}
151187
}
152188

153189
@Test
154-
fun testSignUpWithPhoneNoAutoconfirm() {
190+
fun testSignUpWithPhoneNoAutoConfirm() {
155191
runTest {
156192
val expectedPhone = "+1234567890"
157193
val expectedPassword = "password"
@@ -693,6 +729,25 @@ class AuthRequestTest {
693729
}
694730
""".trimIndent()
695731

732+
private fun sampleSessionWithUserData(email: String? = null, phone: String? = null) = """
733+
{
734+
"id": "id",
735+
"aud": "aud",
736+
"email": "$email",
737+
"phone": "$phone",
738+
"access_token": "token",
739+
"refresh_token": "refresh",
740+
"token_type": "bearer",
741+
"expires_in": 3600,
742+
"user": {
743+
"id": "id",
744+
"aud": "aud",
745+
"email": "$email",
746+
"phone": "$phone"
747+
}
748+
}
749+
""".trimIndent()
750+
696751
private fun containsCodeChallenge(body: JsonObject) {
697752
assertNotNull(body["code_challenge"])
698753
assertEquals(PKCEConstants.CHALLENGE_METHOD, body["code_challenge_method"]?.jsonPrimitive?.content)

Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import io.github.jan.supabase.auth.Auth
33
import io.github.jan.supabase.auth.auth
44
import io.github.jan.supabase.auth.exception.AuthRestException
55
import io.github.jan.supabase.auth.exception.AuthWeakPasswordException
6-
import io.github.jan.supabase.auth.minimalSettings
6+
import io.github.jan.supabase.auth.minimalConfig
77
import io.github.jan.supabase.auth.providers.builtin.Email
88
import io.github.jan.supabase.exceptions.BadRequestRestException
99
import io.github.jan.supabase.testing.createMockedSupabaseClient
@@ -25,7 +25,7 @@ class AuthRestExceptionTest {
2525

2626
private val configuration: SupabaseClientBuilder.() -> Unit = {
2727
install(Auth) {
28-
minimalSettings()
28+
minimalConfig()
2929
}
3030
}
3131

Auth/src/commonTest/kotlin/AuthTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import io.github.jan.supabase.SupabaseClientBuilder
33
import io.github.jan.supabase.auth.Auth
44
import io.github.jan.supabase.auth.MemorySessionManager
55
import io.github.jan.supabase.auth.auth
6+
import io.github.jan.supabase.auth.minimalConfig
67
import io.github.jan.supabase.auth.event.AuthEvent
78
import io.github.jan.supabase.auth.minimalSettings
89
import io.github.jan.supabase.auth.providers.Github
@@ -33,7 +34,7 @@ class AuthTest {
3334

3435
private val configuration: SupabaseClientBuilder.() -> Unit = {
3536
install(Auth) {
36-
minimalSettings()
37+
minimalConfig()
3738
}
3839
}
3940

0 commit comments

Comments
 (0)