Skip to content

Commit 926f9a0

Browse files
authored
Merge pull request #911 from supabase-community/js-fragments
Fix exception on non-session hash parameters and only consume used parameters
2 parents 044b2db + d574862 commit 926f9a0

File tree

7 files changed

+103
-28
lines changed

7 files changed

+103
-28
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,17 @@ interface Auth : MainPlugin<AuthConfig>, CustomSerializationPlugin {
411411

412412
companion object : SupabasePluginProvider<AuthConfig, Auth> {
413413

414+
internal val HASH_PARAMETERS = listOf(
415+
"access_token",
416+
"refresh_token",
417+
"expires_in",
418+
"expires_at",
419+
"token_type",
420+
"type",
421+
"provider_refresh_token",
422+
"provider_token"
423+
)
424+
414425
override val key = "auth"
415426

416427
override val logger: SupabaseLogger = SupabaseClient.createLogger("Supabase-Auth")
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.github.jan.supabase.auth
2+
3+
import io.github.jan.supabase.annotations.SupabaseInternal
4+
import io.github.jan.supabase.auth.status.SessionSource
5+
import io.github.jan.supabase.auth.user.UserSession
6+
import io.github.jan.supabase.buildUrl
7+
import io.github.jan.supabase.logging.d
8+
import io.ktor.client.request.HttpRequestBuilder
9+
import kotlinx.coroutines.launch
10+
11+
@SupabaseInternal
12+
fun Auth.parseFragmentAndImportSession(fragment: String, onSessionSuccess: (UserSession) -> Unit = {}) {
13+
Auth.logger.d { "Parsing deeplink fragment $fragment" }
14+
val session = try {
15+
parseSessionFromFragment(fragment)
16+
} catch(e: IllegalArgumentException) {
17+
Auth.logger.d(e) { "Received invalid session fragment. Ignoring." }
18+
return
19+
}
20+
this as AuthImpl
21+
authScope.launch {
22+
val user = retrieveUser(session.accessToken)
23+
val newSession = session.copy(user = user)
24+
onSessionSuccess(newSession)
25+
importSession(newSession, source = SessionSource.External)
26+
}
27+
}
28+
29+
@SupabaseInternal
30+
fun HttpRequestBuilder.redirectTo(url: String) {
31+
this.url.parameters["redirect_to"] = url
32+
}
33+
34+
internal fun consumeHashParameters(parameters: List<String>, url: String): String {
35+
return buildUrl(url) {
36+
fragment = fragment.split("&").filter {
37+
it.split("=").first() !in parameters
38+
}.joinToString("&")
39+
}
40+
}
41+
42+
internal fun consumeUrlParameter(parameters: List<String>, url: String): String {
43+
return buildUrl(url) {
44+
parameters.forEach { parameter ->
45+
this.parameters.remove(parameter)
46+
}
47+
}
48+
}

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

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,7 @@
11
package io.github.jan.supabase.auth
22

33
import io.github.jan.supabase.SupabaseClient
4-
import io.github.jan.supabase.annotations.SupabaseInternal
5-
import io.github.jan.supabase.auth.status.SessionSource
64
import io.github.jan.supabase.auth.user.UserSession
7-
import io.github.jan.supabase.logging.d
8-
import io.ktor.client.request.HttpRequestBuilder
9-
import kotlinx.coroutines.launch
10-
11-
@SupabaseInternal
12-
fun Auth.parseFragmentAndImportSession(fragment: String, onSessionSuccess: (UserSession) -> Unit = {}) {
13-
Auth.logger.d { "Parsing deeplink fragment $fragment" }
14-
val session = parseSessionFromFragment(fragment)
15-
this as AuthImpl
16-
authScope.launch {
17-
val user = retrieveUser(session.accessToken)
18-
val newSession = session.copy(user = user)
19-
onSessionSuccess(newSession)
20-
importSession(newSession, source = SessionSource.External)
21-
}
22-
}
23-
24-
@SupabaseInternal
25-
fun HttpRequestBuilder.redirectTo(url: String) {
26-
this.url.parameters["redirect_to"] = url
27-
}
285

296
internal fun invalidArg(message: String): Nothing = throw IllegalArgumentException(message)
307

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import io.github.jan.supabase.auth.consumeHashParameters
2+
import io.github.jan.supabase.auth.consumeUrlParameter
3+
import io.github.jan.supabase.auth.redirectTo
4+
import io.ktor.client.request.HttpRequestBuilder
5+
import io.ktor.client.request.url
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
9+
class UrlUtilsTest {
10+
11+
@Test
12+
fun testConsumeHashParameters() {
13+
val url = "https://example.com/#test=123&state=abc&code=xyz"
14+
val newUrl = consumeHashParameters(listOf("test", "state"), url)
15+
val expectedUrl = "https://example.com/#code=xyz"
16+
assertEquals(expectedUrl, newUrl)
17+
}
18+
19+
@Test
20+
fun testConsumeUrlParameter() {
21+
val url = "https://example.com/?test=123&state=abc&code=xyz"
22+
val newUrl = consumeUrlParameter(listOf("test", "state"), url)
23+
val expectedUrl = "https://example.com/?code=xyz"
24+
assertEquals(expectedUrl, newUrl)
25+
}
26+
27+
@Test
28+
fun testRedirectTo() {
29+
val url = "https://example.com/"
30+
val redirectTo = "https://redirect.com"
31+
val newUrl = HttpRequestBuilder().apply {
32+
url(url)
33+
redirectTo(redirectTo)
34+
}.url.toString()
35+
val expectedUrl = "https://example.com/?redirect_to=https%3A%2F%2Fredirect.com"
36+
assertEquals(expectedUrl, newUrl)
37+
}
38+
39+
}

Auth/src/jsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ actual fun Auth.setupPlatform() {
2323
}
2424
Auth.logger.d { "Found hash: $afterHash" }
2525
parseFragmentAndImportSession(afterHash) {
26-
val newURL = window.location.href.split("#")[0];
26+
val newURL = consumeHashParameters(Auth.HASH_PARAMETERS, window.location.href)
2727
window.history.replaceState(null, window.document.title, newURL);
2828
}
2929
}
@@ -36,7 +36,7 @@ actual fun Auth.setupPlatform() {
3636
val session = exchangeCodeForSession(code)
3737
importSession(session, source = SessionSource.External)
3838
}
39-
val newURL = window.location.href.split("?")[0];
39+
val newURL = consumeUrlParameter(listOf("code"), window.location.href)
4040
window.history.replaceState(null, window.document.title, newURL);
4141
}
4242

Auth/src/wasmJsMain/kotlin/io/github/jan/supabase/auth/setupPlatform.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ actual fun Auth.setupPlatform() {
2121
}
2222
Auth.logger.d { "Found hash: $afterHash" }
2323
parseFragmentAndImportSession(afterHash) {
24-
val newURL = window.location.href.split("#")[0];
24+
val newURL = consumeHashParameters(Auth.HASH_PARAMETERS, window.location.href)
2525
window.history.replaceState(null, window.document.title, newURL);
2626
}
2727
}
@@ -34,7 +34,7 @@ actual fun Auth.setupPlatform() {
3434
val session = exchangeCodeForSession(code)
3535
importSession(session)
3636
}
37-
val newURL = window.location.href.split("?")[0];
37+
val newURL = consumeUrlParameter(listOf("code"), window.location.href)
3838
window.history.replaceState(null, window.document.title, newURL);
3939
}
4040

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pluginManagement {
88
}
99

1010
plugins {
11-
id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0")
11+
id("org.gradle.toolchains.foojay-resolver-convention") version("0.10.0")
1212
}
1313

1414
// Main Modules

0 commit comments

Comments
 (0)