Skip to content

Commit

Permalink
Merge branch 'master' into wasm
Browse files Browse the repository at this point in the history
# Conflicts:
#	gradle.properties
#	gradle/libs.versions.toml
  • Loading branch information
jan-tennert committed Aug 17, 2024
2 parents 7c8f1a3 + a07783f commit 756266f
Show file tree
Hide file tree
Showing 112 changed files with 2,456 additions and 319 deletions.
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,70 @@
# Changelog

## 2.6.0 - August 16, 2024

### Core

- Update Kotlin to `2.0.10`

### Postgrest

- Expose `headers` and `params` in `PostgrestRequestBuilder` by @jan-tennert in #689

You can now set custom headers & url parameters while making a postgrest request

### Auth

- **Add support for third-party auth by @jan-tennert in #688**

You can now use third-party auth providers like Firebase Auth instead of Supabase Auth by specifying a `AccessTokenProvider` in the `SupabaseClientBuilder`:
```kotlin
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
accessToken = {
//fetch the third party token
"my-token"
}
}
```
This will be used for the `Authorization` header and other modules like Realtime and Storage integrations!
**Note:** The `Auth` plugin cannot be used in combination and will throw an exception if used when setting `accessToken`.
- **Changes to Multi-Factor-Authentication by @jan-tennert in #681**
- Refactor the syntax for enrolling MFA factors and add support for the Phone factor type:
```kotlin
//Enrolling a phone factor
val factor = client.auth.mfa.enroll(FactorType.Phone, friendlyName) {
phone = "+123456789"
}

//Enrolling a TOTP factor
val factor = client.auth.mfa.enroll(FactorType.TOTP, friendlyName) {
issuer = "Issuer"
}
```
- Add a `channel` parameter to `MfaApi#createChallenge` to allow sending phone MFA messages to either `SMS` or `WHATSAPP`.
- Deprecate `MfaApi#loggedInUsingMfa` and `MfaApi#isMfaEnabled` & their flow variants in favor of `MfaApi#status` and `MfaApi#statusFlow`:
```kotlin
val (enabled, active) = client.auth.mfa.status

//Flow variant
client.auth.mfa.statusFlow.collect { (enabled, active) ->
processChange(enabled, active)
}
```
- Add `SlackOIDC` `OAuthProvider` by @jan-tennert in #688

### Realtime

- Remove client-side rate-limiting in #678 by @jan-tennert
- Fix broadcasting to a private channel via the HTTP API in #673 by @jan-tennert
- Fix callbacks not getting removed correctly in the `CallbackManager` in #673 by @jan-tennert
- Change internal realtime implementation to be more robust and testable in #673 by @jan-tennert
- Add `Realtime.Config#websocketFactory`: This is highly internal and should be only modified if you know what you are doing

### Storage

- The `StorageApi#authenticatedRequest` method is now suspending
- All uploading methods will now return a `FileUploadResponse` instead of a `String`, which includes the actual path and some other properties.

## 2.5.4 - July 27, 2024

### Realtime
Expand Down
3 changes: 1 addition & 2 deletions Functions/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(project(":"))
api(project(":gotrue-kt"))
addModules(SupabaseModule.GOTRUE, SupabaseModule.SUPABASE)
}
}
val commonTest by getting {
Expand Down
3 changes: 2 additions & 1 deletion GoTrue/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(project(":"))
addModules(SupabaseModule.SUPABASE)
implementation(libs.krypto)
}
}
Expand All @@ -55,6 +55,7 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(libs.bundles.testing)
implementation(libs.turbine)
implementation(project(":test-common"))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.github.jan.supabase.gotrue

import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.annotations.SupabaseInternal
import io.github.jan.supabase.plugins.MainConfig
import io.github.jan.supabase.plugins.MainPlugin

/**
* Returns the access token used for requests. The token is resolved in the following order:
* 1. [jwtToken] if not null
* 2. [SupabaseClient.resolveAccessToken] if not null
* 3. [Auth.currentAccessTokenOrNull] if the Auth plugin is installed
* 4. [SupabaseClient.supabaseKey] if [keyAsFallback] is true
*/
@SupabaseInternal
suspend fun SupabaseClient.resolveAccessToken(
jwtToken: String? = null,
keyAsFallback: Boolean = true
): String? {
val key = if(keyAsFallback) supabaseKey else null
return jwtToken ?: accessToken?.invoke()
?: pluginManager.getPluginOrNull(Auth)?.currentAccessTokenOrNull() ?: key
}

/**
* Returns the access token used for requests. The token is resolved in the following order:
* 1. [MainConfig.jwtToken] if not null
* 2. [SupabaseClient.resolveAccessToken] if not null
* 3. [Auth.currentAccessTokenOrNull] if the Auth plugin is installed
* 4. [SupabaseClient.supabaseKey] if [keyAsFallback] is true
*/
@SupabaseInternal
suspend fun <C : MainConfig> SupabaseClient.resolveAccessToken(
plugin: MainPlugin<C>,
keyAsFallback: Boolean = true
) = resolveAccessToken(plugin.config.jwtToken, keyAsFallback)
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ internal class AuthImpl(
override val pluginKey: String
get() = Auth.key

init {
if(supabaseClient.accessToken != null) error("The Auth plugin is not available when using a custom access token provider. Please uninstall the Auth plugin.")
}

override fun init() {
setupPlatform()
if (config.autoLoadFromStorage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ class AuthenticatedSupabaseApi @SupabaseInternal constructor(
private val jwtToken: String? = null // Can be configured plugin-wide. By default, all plugins use the token from the current session
): SupabaseApi(resolveUrl, parseErrorResponse, supabaseClient) {

override suspend fun rawRequest(url: String, builder: HttpRequestBuilder.() -> Unit): HttpResponse = super.rawRequest(url) {
val jwtToken = jwtToken ?: supabaseClient.pluginManager.getPluginOrNull(Auth)?.currentAccessTokenOrNull() ?: supabaseClient.supabaseKey
bearerAuth(jwtToken)
builder()
defaultRequest?.invoke(this)
override suspend fun rawRequest(url: String, builder: HttpRequestBuilder.() -> Unit): HttpResponse {
val accessToken = supabaseClient.resolveAccessToken(jwtToken) ?: error("No access token available")
return super.rawRequest(url) {
bearerAuth(accessToken)
builder()
defaultRequest?.invoke(this)
}
}

suspend fun rawRequest(builder: HttpRequestBuilder.() -> Unit): HttpResponse = rawRequest("", builder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,28 @@ import io.github.jan.supabase.supabaseJson
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

/**
* Represents an MFA factor type
* @param value The name of the factor type
*/
sealed class FactorType<T>(val value: String) {
sealed class FactorType<Config, Response>(val value: String) {

@SupabaseInternal
abstract suspend fun decodeResponse(json: JsonObject): T
abstract suspend fun encodeConfig(config: Config.() -> Unit): JsonObject

@SupabaseInternal
abstract suspend fun decodeResponse(json: JsonObject): Response

/**
* TOTP (timed one-time password) MFA factor
*/
data object TOTP : FactorType<TOTP.Response>("totp") {

override suspend fun decodeResponse(json: JsonObject): Response {
return supabaseJson.decodeFromJsonElement(json["totp"]?.jsonObject ?: error("No 'totp' object found in factor response"))
}
data object TOTP : FactorType<TOTP.Config, TOTP.Response>("totp") {

/**
* @param secret The secret used to generate the TOTP code
Expand All @@ -39,6 +41,53 @@ sealed class FactorType<T>(val value: String) {
val uri: String
)

/**
* @param issuer Domain which the user is enrolling with
*/
@Serializable
data class Config(
var issuer: String? = null,
)

override suspend fun decodeResponse(json: JsonObject): Response {
return supabaseJson.decodeFromJsonElement(json["totp"]?.jsonObject ?: error("No 'totp' object found in factor response"))
}

override suspend fun encodeConfig(config: Config.() -> Unit): JsonObject {
return supabaseJson.encodeToJsonElement(Config().apply(config)).jsonObject
}

}

/**
* Phone MFA factor
*/
data object Phone : FactorType<Phone.Config, Phone.Response>("phone") {

/**
* @param phone Phone number of the MFA factor in E.164 format. Used to send messages
*/
@Serializable
data class Response(
val phone: String
)

/**
* @param phone The phone number to send the SMS to. Number should conform to E.164 format
*/
@Serializable
data class Config(
var phone: String? = null,
)

override suspend fun decodeResponse(json: JsonObject): Response {
return Response(json["phone"]?.jsonPrimitive?.contentOrNull ?: error("No 'phone' entry found in factor response"))
}

override suspend fun encodeConfig(config: Config.() -> Unit): JsonObject {
return supabaseJson.encodeToJsonElement(Config().apply(config)).jsonObject
}

}

}
Loading

0 comments on commit 756266f

Please sign in to comment.