Skip to content

Commit

Permalink
Merge branch 'master' into auth-session
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-tennert authored May 6, 2024
2 parents 67d33ac + cd0f98d commit 34371aa
Show file tree
Hide file tree
Showing 39 changed files with 1,140 additions and 494 deletions.
1 change: 1 addition & 0 deletions Functions/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ kotlin {
languageSettings.optIn("kotlin.RequiresOptIn")
languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseInternal")
languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseExperimental")
compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes")
}
val commonMain by getting {
dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.jan.supabase.functions

/**
* The region where the function is invoked.
* @param value The value of the region
*/
enum class FunctionRegion(val value: String) {
ANY("any"),
AP_NORTHEAST_1("ap-northeast-1"),
AP_NORTHEAST_2("ap-northeast-2"),
AP_SOUTH_1("ap-south-1"),
AP_SOUTHEAST_1("ap-southeast-1"),
AP_SOUTHEAST_2("ap-southeast-2"),
CA_CENTRAL_1("ca-central-1"),
EU_CENTRAL_1("eu-central-1"),
EU_WEST_1("eu-west-1"),
EU_WEST_2("eu-west-2"),
EU_WEST_3("eu-west-3"),
SA_EAST_1("sa-east-1"),
US_EAST_1("us-east-1"),
US_WEST_1("us-west-1"),
US_WEST_2("us-west-2"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import io.github.jan.supabase.plugins.SupabasePluginProvider
import io.github.jan.supabase.serializer.KotlinXSerializer
import io.ktor.client.plugins.HttpRequestTimeoutException
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.header
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsText
Expand Down Expand Up @@ -61,12 +62,16 @@ class Functions(override val config: Config, override val supabaseClient: Supaba
* Invokes a remote edge function. The authorization token is automatically added to the request.
* @param function The function to invoke
* @param builder The request builder to configure the request
* @param region The region where the function is invoked
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline operator fun invoke(function: String, crossinline builder: HttpRequestBuilder.() -> Unit): HttpResponse {
return api.post(function, builder)
suspend inline operator fun invoke(function: String, region: FunctionRegion = config.defaultRegion, crossinline builder: HttpRequestBuilder.() -> Unit): HttpResponse {
return api.post(function) {
builder()
header("x-region", region.value)
}
}

/**
Expand All @@ -75,34 +80,50 @@ class Functions(override val config: Config, override val supabaseClient: Supaba
* @param function The function to invoke
* @param body The body of the request
* @param headers Headers to add to the request
* @param region The region where the function is invoked
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline operator fun <reified T : Any> invoke(function: String, body: T, headers: Headers = Headers.Empty): HttpResponse = invoke(function) {
suspend inline operator fun <reified T : Any> invoke(function: String, body: T, region: FunctionRegion = config.defaultRegion, headers: Headers = Headers.Empty): HttpResponse = invoke(function) {
this.headers.appendAll(headers)
header("x-region", region.value)
setBody(serializer.encode(body))
}

/**
* Invokes a remote edge function. The authorization token is automatically added to the request.
* @param function The function to invoke
* @param headers Headers to add to the request
* @param region The region where the function is invoked
* @throws RestException or one of its subclasses if receiving an error response
* @throws HttpRequestTimeoutException if the request timed out
* @throws HttpRequestException on network related issues
*/
suspend inline operator fun invoke(function: String, headers: Headers = Headers.Empty): HttpResponse = invoke(function) {
suspend inline operator fun invoke(function: String, region: FunctionRegion = config.defaultRegion, headers: Headers = Headers.Empty): HttpResponse = invoke(function) {
this.headers.appendAll(headers)
header("x-region", region.value)
}

/**
* Builds an [EdgeFunction] which can be invoked multiple times
* @param function The function name
* @param headers Headers to add to the requests when invoking the function
* @param region The region where the function is invoked
*/
@OptIn(SupabaseInternal::class)
fun buildEdgeFunction(function: String, headers: Headers = Headers.Empty) = EdgeFunction(function, headers, supabaseClient)
fun buildEdgeFunction(
function: String,
region: FunctionRegion = config.defaultRegion,
headers: Headers = Headers.Empty
) = EdgeFunction(
functionName = function,
headers = Headers.build {
appendAll(headers)
append("x-region", region.value)
},
supabaseClient = supabaseClient
)

override suspend fun parseErrorResponse(response: HttpResponse): RestException {
val error = response.bodyAsText()
Expand All @@ -124,6 +145,11 @@ class Functions(override val config: Config, override val supabaseClient: Supaba

override var serializer: SupabaseSerializer? = null

/**
* The default region to use when invoking a function
*/
var defaultRegion: FunctionRegion = FunctionRegion.ANY

}

companion object : SupabasePluginProvider<Config, Functions> {
Expand Down
2 changes: 2 additions & 0 deletions GoTrue/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ kotlin {
languageSettings.optIn("kotlin.RequiresOptIn")
languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseInternal")
languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseExperimental")
compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes")
}
val commonMain by getting {
dependencies {
Expand Down Expand Up @@ -75,6 +76,7 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(libs.bundles.testing)
implementation(project(":test-common"))
}
}
val jvmMain by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,13 @@ sealed interface Auth : MainPlugin<AuthConfig>, CustomSerializationPlugin {
* @param provider The OAuth provider
* @param redirectUrl The redirect url to use. If you don't specify this, the platform specific will be used, like deeplinks on android.
* @param config Extra configuration
* @return The OAuth url to open in the browser if [ExternalAuthConfigDefaults.automaticallyOpenUrl] is false, otherwise null.
*/
suspend fun linkIdentity(
provider: OAuthProvider,
redirectUrl: String? = defaultRedirectUrl(),
config: ExternalAuthConfigDefaults.() -> Unit = {}
)
): String?

/**
* Unlinks an OAuth Identity from an existing user.
Expand Down Expand Up @@ -289,7 +290,7 @@ sealed interface Auth : MainPlugin<AuthConfig>, CustomSerializationPlugin {
/**
* Imports a user session and starts auto-refreshing if [autoRefresh] is true
*/
suspend fun importSession(session: UserSession, autoRefresh: Boolean = true, source: SessionSource = SessionSource.Unknown)
suspend fun importSession(session: UserSession, autoRefresh: Boolean = config.alwaysAutoRefresh, source: SessionSource = SessionSource.Unknown)

/**
* Imports the jwt token and retrieves the user profile.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,28 @@ internal class AuthImpl(
provider: OAuthProvider,
redirectUrl: String?,
config: ExternalAuthConfigDefaults.() -> Unit
) {
): String? {
val automaticallyOpen = ExternalAuthConfigDefaults().apply(config).automaticallyOpenUrl
val fetchUrl: suspend (String?) -> String = { redirectTo: String? ->
val url = getOAuthUrl(provider, redirectTo, "user/identities/authorize", config)
val response = api.rawRequest(url) {
method = HttpMethod.Get
}
response.request.url.toString()
}
if(!automaticallyOpen) {
return fetchUrl(redirectUrl ?: "")
}
startExternalAuth(
redirectUrl = redirectUrl,
getUrl = {
val url = getOAuthUrl(provider, it, "user/identities/authorize", config)
val response = api.rawRequest(url) {
method = HttpMethod.Get
}
response.request.url.toString()
fetchUrl(it)
},
onSessionSuccess = {
importSession(it, source = SessionSource.UserIdentitiesChanged(it))
}
)
return null
}

override suspend fun unlinkIdentity(identityId: String, updateLocalUser: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ interface CodeVerifierCache {
/**
* A [CodeVerifierCache] that uses the [AtomicRef] API.
*/
class MemoryCodeVerifierCache: CodeVerifierCache {
class MemoryCodeVerifierCache(codeVerifier: String? = null): CodeVerifierCache {

private var codeVerifier by atomic<String?>(null)
private var codeVerifier by atomic(codeVerifier)

override suspend fun saveCodeVerifier(codeVerifier: String) {
this.codeVerifier = codeVerifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.io.encoding.ExperimentalEncodingApi

internal object PKCEConstants {
const val VERIFIER_LENGTH = 64
const val CHALLENGE_METHOD = "S256"
const val CHALLENGE_METHOD = "s256"
}

@OptIn(ExperimentalEncodingApi::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.jan.supabase.gotrue.providers

import io.github.jan.supabase.gotrue.Auth

/**
* Configuration for external authentication providers like Google, Twitter, etc.
*/
Expand All @@ -20,4 +22,9 @@ open class ExternalAuthConfigDefaults {
*/
val queryParams = mutableMapOf<String, String>()

/**
* Automatically open the URL in the browser. Only applies to [Auth.linkIdentity].
*/
var automaticallyOpenUrl: Boolean = true

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.github.jan.supabase.gotrue.auth
import io.github.jan.supabase.gotrue.generateCodeChallenge
import io.github.jan.supabase.gotrue.generateCodeVerifier
import io.github.jan.supabase.gotrue.providers.AuthProvider
import io.github.jan.supabase.gotrue.putCodeChallenge
import io.github.jan.supabase.gotrue.redirectTo
import io.github.jan.supabase.gotrue.user.UserSession
import io.github.jan.supabase.putJsonObject
Expand All @@ -19,7 +20,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.put

/**
* A default authentication provider
Expand Down Expand Up @@ -88,9 +88,8 @@ sealed interface DefaultAuthProvider<C, R> : AuthProvider<C, R> {
}
val response = gotrue.api.postJson(url, buildJsonObject {
putJsonObject(body)
codeChallenge?.let {
put("code_challenge", it)
put("code_challenge_method", "s256")
if (codeChallenge != null) {
putCodeChallenge(codeChallenge)
}
}) {
redirectUrl?.let { redirectTo(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.github.jan.supabase.gotrue.auth
import io.github.jan.supabase.gotrue.generateCodeChallenge
import io.github.jan.supabase.gotrue.generateCodeVerifier
import io.github.jan.supabase.gotrue.providers.AuthProvider
import io.github.jan.supabase.gotrue.putCaptchaToken
import io.github.jan.supabase.gotrue.user.UserSession
import io.github.jan.supabase.putJsonObject
import kotlinx.serialization.json.JsonObject
Expand All @@ -32,14 +33,15 @@ data object OTP: AuthProvider<OTP.Config, Unit> {
* @param phone The phone number of the user
* @param data Additional data to store with the user
* @param createUser Whether to create a new user if the user doesn't exist
*
* @param captchaToken The captcha token for the request
*/
class Config(
@PublishedApi internal val serializer: SupabaseSerializer,
var email: String? = null,
var phone: String? = null,
var data: JsonObject? = null,
var createUser: Boolean = true,
var captchaToken: String? = null
) {

/**
Expand Down Expand Up @@ -87,6 +89,7 @@ data object OTP: AuthProvider<OTP.Config, Unit> {
put("code_challenge", it)
put("code_challenge_method", "s256")
}
otpConfig.captchaToken?.let { putCaptchaToken(it) }
}) {
redirectUrl?.let { url.parameters.append("redirect_to", it) }
}
Expand All @@ -97,6 +100,6 @@ data object OTP: AuthProvider<OTP.Config, Unit> {
onSuccess: suspend (UserSession) -> Unit,
redirectUrl: String?,
config: (Config.() -> Unit)?
): Unit? = login(supabaseClient, onSuccess, redirectUrl, config)
): Unit = login(supabaseClient, onSuccess, redirectUrl, config)

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ data class UserSession(
val expiresIn: Long,
@SerialName("token_type")
val tokenType: String,
val user: UserInfo?,
val user: UserInfo? = null,
@SerialName("type")
val type: String = "",
val expiresAt: Instant = Clock.System.now() + (expiresIn.seconds),
Expand Down
5 changes: 5 additions & 0 deletions GoTrue/src/commonTest/kotlin/AdminApiTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AdminApiTest {

//TODO: Implement tests

}
Loading

0 comments on commit 34371aa

Please sign in to comment.