Skip to content

Commit 88780b6

Browse files
committed
feat(realtime): pull access token mechanism
1 parent 9eac916 commit 88780b6

File tree

4 files changed

+63
-37
lines changed

4 files changed

+63
-37
lines changed

Sources/Realtime/V2/RealtimeChannelV2.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ struct Socket: Sendable {
2929
var broadcastURL: @Sendable () -> URL
3030
var status: @Sendable () -> RealtimeClientStatus
3131
var options: @Sendable () -> RealtimeClientOptions
32-
var accessToken: @Sendable () -> String?
32+
var accessToken: @Sendable () async -> String?
3333
var apiKey: @Sendable () -> String?
3434
var makeRef: @Sendable () -> Int
3535

@@ -46,7 +46,12 @@ extension Socket {
4646
broadcastURL: { [weak client] in client?.broadcastURL ?? URL(string: "http://localhost")! },
4747
status: { [weak client] in client?.status ?? .disconnected },
4848
options: { [weak client] in client?.options ?? .init() },
49-
accessToken: { [weak client] in client?.mutableState.accessToken },
49+
accessToken: { [weak client] in
50+
if let accessToken = try? await client?.options.accessToken?() {
51+
return accessToken
52+
}
53+
return client?.mutableState.accessToken
54+
},
5055
apiKey: { [weak client] in client?.apikey },
5156
makeRef: { [weak client] in client?.makeRef() ?? 0 },
5257
connect: { [weak client] in await client?.connect() },
@@ -139,7 +144,7 @@ public final class RealtimeChannelV2: Sendable {
139144

140145
let payload = RealtimeJoinPayload(
141146
config: joinConfig,
142-
accessToken: socket.accessToken()
147+
accessToken: await socket.accessToken()
143148
)
144149

145150
let joinRef = socket.makeRef().description
@@ -213,7 +218,7 @@ public final class RealtimeChannelV2: Sendable {
213218
if let apiKey = socket.apiKey() {
214219
headers[.apiKey] = apiKey
215220
}
216-
if let accessToken = socket.accessToken() {
221+
if let accessToken = await socket.accessToken() {
217222
headers[.authorization] = "Bearer \(accessToken)"
218223
}
219224

Sources/Realtime/V2/RealtimeClientV2.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ public final class RealtimeClientV2: Sendable {
107107
apikey = options.apikey
108108

109109
mutableState.withValue {
110-
$0.accessToken = options.accessToken ?? options.apikey
110+
if let accessToken = options.headers[.authorization]?.split(separator: " ").last {
111+
$0.accessToken = String(accessToken)
112+
} else {
113+
$0.accessToken = options.apikey
114+
}
111115
}
112116
}
113117

Sources/Realtime/V2/Types.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
//
77

88
import Foundation
9-
import Helpers
109
import HTTPTypes
10+
import Helpers
1111

1212
#if canImport(FoundationNetworking)
1313
import FoundationNetworking
@@ -22,6 +22,7 @@ public struct RealtimeClientOptions: Sendable {
2222
var disconnectOnSessionLoss: Bool
2323
var connectOnSubscribe: Bool
2424
var fetch: (@Sendable (_ request: URLRequest) async throws -> (Data, URLResponse))?
25+
package var accessToken: (@Sendable () async throws -> String)?
2526
package var logger: (any SupabaseLogger)?
2627

2728
public static let defaultHeartbeatInterval: TimeInterval = 15
@@ -38,6 +39,7 @@ public struct RealtimeClientOptions: Sendable {
3839
disconnectOnSessionLoss: Bool = Self.defaultDisconnectOnSessionLoss,
3940
connectOnSubscribe: Bool = Self.defaultConnectOnSubscribe,
4041
fetch: (@Sendable (_ request: URLRequest) async throws -> (Data, URLResponse))? = nil,
42+
accessToken: (@Sendable () async throws -> String)? = nil,
4143
logger: (any SupabaseLogger)? = nil
4244
) {
4345
self.headers = HTTPFields(headers)
@@ -47,19 +49,13 @@ public struct RealtimeClientOptions: Sendable {
4749
self.disconnectOnSessionLoss = disconnectOnSessionLoss
4850
self.connectOnSubscribe = connectOnSubscribe
4951
self.fetch = fetch
52+
self.accessToken = accessToken
5053
self.logger = logger
5154
}
5255

5356
var apikey: String? {
5457
headers[.apiKey]
5558
}
56-
57-
var accessToken: String? {
58-
guard let accessToken = headers[.authorization]?.split(separator: " ").last else {
59-
return nil
60-
}
61-
return String(accessToken)
62-
}
6359
}
6460

6561
public typealias RealtimeSubscription = ObservationToken

Sources/Supabase/SupabaseClient.swift

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import ConcurrencyExtras
33
import Foundation
44
@_exported import Functions
5+
import HTTPTypes
56
import Helpers
67
import IssueReporting
78
@_exported import PostgREST
89
@_exported import Realtime
910
@_exported import Storage
10-
import HTTPTypes
1111

1212
#if canImport(FoundationNetworking)
1313
import FoundationNetworking
@@ -33,10 +33,11 @@ public final class SupabaseClient: Sendable {
3333
/// Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
3434
public var auth: AuthClient {
3535
if options.auth.accessToken != nil {
36-
reportIssue("""
37-
Supabase Client is configured with the auth.accessToken option,
38-
accessing supabase.auth is not possible.
39-
""")
36+
reportIssue(
37+
"""
38+
Supabase Client is configured with the auth.accessToken option,
39+
accessing supabase.auth is not possible.
40+
""")
4041
}
4142
return _auth
4243
}
@@ -80,7 +81,14 @@ public final class SupabaseClient: Sendable {
8081
let _realtime: UncheckedSendable<RealtimeClient>
8182

8283
/// Realtime client for Supabase
83-
public let realtimeV2: RealtimeClientV2
84+
public var realtimeV2: RealtimeClientV2 {
85+
mutableState.withValue {
86+
if $0.realtime == nil {
87+
$0.realtime = _initRealtimeClient()
88+
}
89+
return $0.realtime!
90+
}
91+
}
8492

8593
/// Supabase Functions allows you to deploy and invoke edge functions.
8694
public var functions: FunctionsClient {
@@ -112,6 +120,7 @@ public final class SupabaseClient: Sendable {
112120
var storage: SupabaseStorageClient?
113121
var rest: PostgrestClient?
114122
var functions: FunctionsClient?
123+
var realtime: RealtimeClientV2?
115124

116125
var changedAccessToken: String?
117126
}
@@ -189,18 +198,6 @@ public final class SupabaseClient: Sendable {
189198
)
190199
)
191200

192-
var realtimeOptions = options.realtime
193-
realtimeOptions.headers.merge(with: _headers)
194-
195-
if realtimeOptions.logger == nil {
196-
realtimeOptions.logger = options.global.logger
197-
}
198-
199-
realtimeV2 = RealtimeClientV2(
200-
url: supabaseURL.appendingPathComponent("/realtime/v1"),
201-
options: realtimeOptions
202-
)
203-
204201
if options.auth.accessToken == nil {
205202
listenForAuthEvents()
206203
}
@@ -351,11 +348,12 @@ public final class SupabaseClient: Sendable {
351348
}
352349

353350
private func adapt(request: URLRequest) async -> URLRequest {
354-
let token: String? = if let accessToken = options.auth.accessToken {
355-
try? await accessToken()
356-
} else {
357-
try? await auth.session.accessToken
358-
}
351+
let token: String? =
352+
if let accessToken = options.auth.accessToken {
353+
try? await accessToken()
354+
} else {
355+
try? await auth.session.accessToken
356+
}
359357

360358
var request = request
361359
if let token {
@@ -377,7 +375,9 @@ public final class SupabaseClient: Sendable {
377375

378376
private func handleTokenChanged(event: AuthChangeEvent, session: Session?) async {
379377
let accessToken: String? = mutableState.withValue {
380-
if [.initialSession, .signedIn, .tokenRefreshed].contains(event), $0.changedAccessToken != session?.accessToken {
378+
if [.initialSession, .signedIn, .tokenRefreshed].contains(event),
379+
$0.changedAccessToken != session?.accessToken
380+
{
381381
$0.changedAccessToken = session?.accessToken
382382
return session?.accessToken ?? supabaseKey
383383
}
@@ -393,4 +393,25 @@ public final class SupabaseClient: Sendable {
393393
realtime.setAuth(accessToken)
394394
await realtimeV2.setAuth(accessToken)
395395
}
396+
397+
private func _initRealtimeClient() -> RealtimeClientV2 {
398+
var realtimeOptions = options.realtime
399+
realtimeOptions.headers.merge(with: _headers)
400+
401+
if realtimeOptions.logger == nil {
402+
realtimeOptions.logger = options.global.logger
403+
}
404+
405+
if realtimeOptions.accessToken == nil {
406+
realtimeOptions.accessToken = { [weak self] in
407+
guard let self else { return "" }
408+
return try await self.auth.session.accessToken
409+
}
410+
}
411+
412+
return RealtimeClientV2(
413+
url: supabaseURL.appendingPathComponent("/realtime/v1"),
414+
options: realtimeOptions
415+
)
416+
}
396417
}

0 commit comments

Comments
 (0)