Skip to content

Commit

Permalink
Merge branch 'main' into replace-HTTPRequest
Browse files Browse the repository at this point in the history
# Conflicts:
#	Sources/Realtime/V2/Types.swift
#	Sources/Supabase/SupabaseClient.swift
#	Tests/IntegrationTests/Postgrest/PostgrestFilterTests.swift
  • Loading branch information
zunda-pixel committed Dec 10, 2024
2 parents 9322ce2 + 7a88591 commit 96c9255
Show file tree
Hide file tree
Showing 23 changed files with 289 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "2.23.0"
".": "2.24.0"
}
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [2.24.0](https://github.com/supabase/supabase-swift/compare/v2.23.0...v2.24.0) (2024-12-05)


### Features

* **realtime:** pull access token mechanism ([#615](https://github.com/supabase/supabase-swift/issues/615)) ([c88dd36](https://github.com/supabase/supabase-swift/commit/c88dd3675b8bc7da93c71847ff9ba9862323ff8d))


### Bug Fixes

* **realtime:** prevent sending expired tokens ([#618](https://github.com/supabase/supabase-swift/issues/618)) ([595277b](https://github.com/supabase/supabase-swift/commit/595277b5eb35b8b76bbb000d44fc221c4d3298f1))

## [2.23.0](https://github.com/supabase/supabase-swift/compare/v2.22.1...v2.23.0) (2024-11-22)


Expand Down
8 changes: 4 additions & 4 deletions Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
793895CC2954ABFF0044F2B8 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793895CB2954ABFF0044F2B8 /* RootView.swift */; };
793895CE2954AC000044F2B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 793895CD2954AC000044F2B8 /* Assets.xcassets */; };
793895D22954AC000044F2B8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 793895D12954AC000044F2B8 /* Preview Assets.xcassets */; };
793E03092B2CED5D00AC7DED /* Contants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E03082B2CED5D00AC7DED /* Contants.swift */; };
793E03092B2CED5D00AC7DED /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E03082B2CED5D00AC7DED /* Constants.swift */; };
793E030B2B2CEDDA00AC7DED /* ActionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E030A2B2CEDDA00AC7DED /* ActionState.swift */; };
793E030D2B2DAB5700AC7DED /* SignInWithApple.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793E030C2B2DAB5700AC7DED /* SignInWithApple.swift */; };
79401F332BC6FEAE004C9C0F /* SignInWithOAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79401F322BC6FEAE004C9C0F /* SignInWithOAuth.swift */; };
Expand Down Expand Up @@ -86,7 +86,7 @@
793895CD2954AC000044F2B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
793895CF2954AC000044F2B8 /* Examples.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Examples.entitlements; sourceTree = "<group>"; };
793895D12954AC000044F2B8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
793E03082B2CED5D00AC7DED /* Contants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contants.swift; sourceTree = "<group>"; };
793E03082B2CED5D00AC7DED /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
793E030A2B2CEDDA00AC7DED /* ActionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionState.swift; sourceTree = "<group>"; };
793E030C2B2DAB5700AC7DED /* SignInWithApple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithApple.swift; sourceTree = "<group>"; };
79401F322BC6FEAE004C9C0F /* SignInWithOAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithOAuth.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -226,7 +226,7 @@
794EF1232955F3DE008C9526 /* TodoListRow.swift */,
796298982AEBBA77000AA957 /* MFAFlow.swift */,
79AF04852B2CE586008761AD /* Debug.swift */,
793E03082B2CED5D00AC7DED /* Contants.swift */,
793E03082B2CED5D00AC7DED /* Constants.swift */,
793E030A2B2CEDDA00AC7DED /* ActionState.swift */,
79E2B55B2B97A2310042CD21 /* UIApplicationExtensions.swift */,
797EFB672BABD90500098D6B /* Stringfy.swift */,
Expand Down Expand Up @@ -507,7 +507,7 @@
79401F332BC6FEAE004C9C0F /* SignInWithOAuth.swift in Sources */,
79B1C80E2BAC017C00D991AA /* AnyJSONView.swift in Sources */,
79E2B5552B9788BF0042CD21 /* GoogleSignInSDKFlow.swift in Sources */,
793E03092B2CED5D00AC7DED /* Contants.swift in Sources */,
793E03092B2CED5D00AC7DED /* Constants.swift in Sources */,
794C61D62BAD1E12000E6B0F /* UserIdentityList.swift in Sources */,
793895CC2954ABFF0044F2B8 /* RootView.swift in Sources */,
7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Contants.swift
// Constants.swift
// Examples
//
// Created by Guilherme Souza on 15/12/23.
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "06dc63c6d8da54ee11ceb268cde1fa68161afc96",
"version" : "3.9.1"
"revision" : "ff0f781cf7c6a22d52957e50b104f5768b50c779",
"version" : "3.10.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ let package = Package(
dependencies: [
...
.package(
url: "https://github.com/supabase-community/supabase-swift.git",
url: "https://github.com/supabase/supabase-swift.git",
from: "2.0.0"
),
],
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ public final class AuthClient: Sendable {
var hasExpired = true
var session: Session

let jwt = try decode(jwt: accessToken)
let jwt = JWT.decodePayload(accessToken)
if let exp = jwt?["exp"] as? TimeInterval {
expiresAt = Date(timeIntervalSince1970: exp)
hasExpired = expiresAt <= now
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/AuthMFA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public struct AuthMFA: Sendable {
{
do {
let session = try await sessionManager.session()
let payload = try decode(jwt: session.accessToken)
let payload = JWT.decodePayload(session.accessToken)

var currentLevel: AuthenticatorAssuranceLevels?

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Contants.swift
// Constants.swift
//
//
// Created by Guilherme Souza on 22/05/24.
Expand Down Expand Up @@ -33,9 +33,9 @@ struct APIVersion {
}

static func date(for name: Name) -> Date {
let formattar = ISO8601DateFormatter()
formattar.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formattar.date(from: "\(name.rawValue)T00:00:00.0Z")!
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter.date(from: "\(name.rawValue)T00:00:00.0Z")!
}
}

Expand Down
30 changes: 0 additions & 30 deletions Sources/Auth/Internal/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,33 +39,3 @@ private func extractParams(from fragment: String) -> [URLQueryItem] {
: nil
}
}

func decode(jwt: String) throws -> [String: Any]? {
let parts = jwt.split(separator: ".")
guard parts.count == 3 else {
return nil
}

let payload = String(parts[1])
guard let data = base64URLDecode(payload) else {
return nil
}
let json = try JSONSerialization.jsonObject(with: data, options: [])
guard let decodedPayload = json as? [String: Any] else {
return nil
}
return decodedPayload
}

private func base64URLDecode(_ value: String) -> Data? {
var base64 = value.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let length = Double(base64.lengthOfBytes(using: .utf8))
let requiredLength = 4 * ceil(length / 4.0)
let paddingLength = requiredLength - length
if paddingLength > 0 {
let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
base64 = base64 + padding
}
return Data(base64Encoded: base64, options: .ignoreUnknownCharacters)
}
40 changes: 40 additions & 0 deletions Sources/Helpers/JWT.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// JWT.swift
// Supabase
//
// Created by Guilherme Souza on 28/11/24.
//

import Foundation

package enum JWT {
package static func decodePayload(_ jwt: String) -> [String: Any]? {
let parts = jwt.split(separator: ".")
guard parts.count == 3 else {
return nil
}

let payload = String(parts[1])
guard let data = base64URLDecode(payload) else {
return nil
}
let json = try? JSONSerialization.jsonObject(with: data, options: [])
guard let decodedPayload = json as? [String: Any] else {
return nil
}
return decodedPayload
}

private static func base64URLDecode(_ value: String) -> Data? {
var base64 = value.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let length = Double(base64.lengthOfBytes(using: .utf8))
let requiredLength = 4 * ceil(length / 4.0)
let paddingLength = requiredLength - length
if paddingLength > 0 {
let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
base64 = base64 + padding
}
return Data(base64Encoded: base64, options: .ignoreUnknownCharacters)
}
}
2 changes: 1 addition & 1 deletion Sources/Helpers/Version.swift
Original file line number Diff line number Diff line change
@@ -1 +1 @@
package let version = "2.23.0" // {x-release-please-version}
package let version = "2.24.0" // {x-release-please-version}
13 changes: 9 additions & 4 deletions Sources/Realtime/V2/RealtimeChannelV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct Socket: Sendable {
var broadcastURL: @Sendable () -> URL
var status: @Sendable () -> RealtimeClientStatus
var options: @Sendable () -> RealtimeClientOptions
var accessToken: @Sendable () -> String?
var accessToken: @Sendable () async -> String?
var apiKey: @Sendable () -> String?
var makeRef: @Sendable () -> Int

Expand All @@ -50,7 +50,12 @@ extension Socket {
broadcastURL: { [weak client] in client?.broadcastURL ?? URL(string: "http://localhost")! },
status: { [weak client] in client?.status ?? .disconnected },
options: { [weak client] in client?.options ?? .init() },
accessToken: { [weak client] in client?.mutableState.accessToken },
accessToken: { [weak client] in
if let accessToken = try? await client?.options.accessToken?() {
return accessToken
}
return client?.mutableState.accessToken
},
apiKey: { [weak client] in client?.apikey },
makeRef: { [weak client] in client?.makeRef() ?? 0 },
connect: { [weak client] in await client?.connect() },
Expand Down Expand Up @@ -143,7 +148,7 @@ public final class RealtimeChannelV2: Sendable {

let payload = RealtimeJoinPayload(
config: joinConfig,
accessToken: socket.accessToken()
accessToken: await socket.accessToken()
)

let joinRef = socket.makeRef().description
Expand Down Expand Up @@ -217,7 +222,7 @@ public final class RealtimeChannelV2: Sendable {
if let apiKey = socket.apiKey() {
headers[.apiKey] = apiKey
}
if let accessToken = socket.accessToken() {
if let accessToken = await socket.accessToken() {
headers[.authorization] = "Bearer \(accessToken)"
}

Expand Down
34 changes: 30 additions & 4 deletions Sources/Realtime/V2/RealtimeClientV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ public final class RealtimeClientV2: Sendable {
apikey = options.apikey

mutableState.withValue {
$0.accessToken = options.accessToken ?? options.apikey
if let accessToken = options.headers[.authorization]?.split(separator: " ").last {
$0.accessToken = String(accessToken)
} else {
$0.accessToken = options.apikey
}
}
}

Expand Down Expand Up @@ -369,9 +373,31 @@ public final class RealtimeClientV2: Sendable {
}

/// Sets the JWT access token used for channel subscription authorization and Realtime RLS.
/// - Parameter token: A JWT string.
public func setAuth(_ token: String?) async {
mutableState.withValue {
///
/// If `token` is nil it will use the ``RealtimeClientOptions/accessToken`` callback function or the token set on the client.
///
/// On callback used, it will set the value of the token internal to the client.
/// - Parameter token: A JWT string to override the token set on the client.
public func setAuth(_ token: String? = nil) async {
var token = token

if token == nil {
token = try? await options.accessToken?()
}

if token == nil {
token = mutableState.accessToken
}

if let token, let payload = JWT.decodePayload(token),
let exp = payload["exp"] as? TimeInterval, exp < Date().timeIntervalSince1970
{
options.logger?.warning(
"InvalidJWTToken: Invalid value for JWT claim \"exp\" with value \(exp)")
return
}

mutableState.withValue { [token] in
$0.accessToken = token
}

Expand Down
10 changes: 3 additions & 7 deletions Sources/Realtime/V2/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public struct RealtimeClientOptions: Sendable {
_ bodyData: Data?
) async throws -> (Data, HTTPResponse)
)?
package var accessToken: (@Sendable () async throws -> String)?
package var logger: (any SupabaseLogger)?

public static let defaultHeartbeatInterval: TimeInterval = 15
Expand All @@ -49,6 +50,7 @@ public struct RealtimeClientOptions: Sendable {
_ bodyData: Data?
) async throws -> (Data, HTTPResponse)
)? = nil,
accessToken: (@Sendable () async throws -> String)? = nil,
logger: (any SupabaseLogger)? = nil
) {
self.headers = headers
Expand All @@ -58,19 +60,13 @@ public struct RealtimeClientOptions: Sendable {
self.disconnectOnSessionLoss = disconnectOnSessionLoss
self.connectOnSubscribe = connectOnSubscribe
self.fetch = fetch
self.accessToken = accessToken
self.logger = logger
}

var apikey: String? {
headers[.apiKey]
}

var accessToken: String? {
guard let accessToken = headers[.authorization]?.split(separator: " ").last else {
return nil
}
return String(accessToken)
}
}

public typealias RealtimeSubscription = ObservationToken
Expand Down
Loading

0 comments on commit 96c9255

Please sign in to comment.