Skip to content

Commit

Permalink
fix(realtime): remove jwt check (#658)
Browse files Browse the repository at this point in the history
* fix(realtime): remove jwt check

Also:
- Call `setAuth` after heartbeat
- Send `x-client-info` when joining channel

* fix tests

* only send token if changed
  • Loading branch information
grdsdev authored Feb 10, 2025
1 parent 554f93c commit 4c95559
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 33 deletions.
25 changes: 23 additions & 2 deletions .swiftpm/xcode/xcshareddata/xcschemes/Realtime.xcscheme
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.7">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand All @@ -26,8 +26,29 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Realtime"
BuildableName = "Realtime"
BlueprintName = "Realtime"
ReferencedContainer = "container:">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "RealtimeTests"
BuildableName = "RealtimeTests"
BlueprintName = "RealtimeTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
3 changes: 2 additions & 1 deletion Sources/Realtime/RealtimeChannelV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ public final class RealtimeChannelV2: Sendable {

let payload = RealtimeJoinPayload(
config: joinConfig,
accessToken: await socket._getAccessToken()
accessToken: await socket._getAccessToken(),
version: socket.options.headers[.xClientInfo]
)

let joinRef = socket.makeRef()
Expand Down
28 changes: 13 additions & 15 deletions Sources/Realtime/RealtimeClientV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,18 @@ public final class RealtimeClientV2: Sendable {
wsTransport: @escaping WebSocketTransport,
http: any HTTPClientType
) {
var options = options
if options.headers[.xClientInfo] == nil {
options.headers[.xClientInfo] = "realtime-swift/\(version)"
}

self.url = url
self.options = options
self.wsTransport = wsTransport
self.http = http
apikey = options.apikey

mutableState.withValue {
mutableState.withValue { [options] in
if let accessToken = options.headers[.authorization]?.split(separator: " ").last {
$0.accessToken = String(accessToken)
} else {
Expand Down Expand Up @@ -353,15 +358,15 @@ public final class RealtimeClientV2: Sendable {
if Task.isCancelled {
break
}
self?.sendHeartbeat()
await self?.sendHeartbeat()
}
}
mutableState.withValue {
$0.heartbeatTask = heartbeatTask
}
}

private func sendHeartbeat() {
private func sendHeartbeat() async {
let pendingHeartbeatRef: String? = mutableState.withValue {
if $0.pendingHeartbeatRef != nil {
$0.pendingHeartbeatRef = nil
Expand All @@ -383,6 +388,7 @@ public final class RealtimeClientV2: Sendable {
payload: [:]
)
)
await setAuth()
} else {
options.logger?.debug("Heartbeat timeout")
reconnect()
Expand Down Expand Up @@ -416,21 +422,13 @@ public final class RealtimeClientV2: Sendable {
/// 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?()
}
var tokenToSend = token

if token == nil {
token = mutableState.accessToken
if tokenToSend == nil {
tokenToSend = try? await options.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)")
guard tokenToSend != mutableState.accessToken else {
return
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/Realtime/RealtimeJoinConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import Foundation
struct RealtimeJoinPayload: Codable {
var config: RealtimeJoinConfig
var accessToken: String?
var version: String?

enum CodingKeys: String, CodingKey {
case config
case accessToken = "access_token"
case version
}
}

Expand Down
25 changes: 10 additions & 15 deletions Tests/RealtimeTests/RealtimeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ final class RealtimeTests: XCTestCase {
XCTAssertEqual(channelStatuses.value, [.unsubscribed, .subscribing, .subscribed])

assertInlineSnapshot(of: client.sentEvents.map(\.json), as: .json) {
"""
#"""
[
{
"text" : {
Expand Down Expand Up @@ -147,14 +147,15 @@ final class RealtimeTests: XCTestCase {
"key" : ""
},
"private" : false
}
},
"version" : "realtime-swift\/0.0.0"
},
"ref" : "1",
"topic" : "realtime:public:messages"
}
}
]
"""
"""#
}
}

Expand Down Expand Up @@ -199,7 +200,7 @@ final class RealtimeTests: XCTestCase {
$0.event == "phx_join"
}
assertInlineSnapshot(of: events, as: .json) {
"""
#"""
[
{
"event" : "phx_join",
Expand All @@ -218,7 +219,8 @@ final class RealtimeTests: XCTestCase {
"key" : ""
},
"private" : false
}
},
"version" : "realtime-swift\/0.0.0"
},
"ref" : "1",
"topic" : "realtime:public:messages"
Expand All @@ -240,13 +242,14 @@ final class RealtimeTests: XCTestCase {
"key" : ""
},
"private" : false
}
},
"version" : "realtime-swift\/0.0.0"
},
"ref" : "2",
"topic" : "realtime:public:messages"
}
]
"""
"""#
}
}

Expand Down Expand Up @@ -378,14 +381,6 @@ final class RealtimeTests: XCTestCase {
XCTAssertEqual(sut.mutableState.accessToken, validToken)
}

func testSetAuthWithExpiredToken() async throws {
let expiredToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOi02NDA5MjIxMTIwMH0.tnbZRC8vEyK3zaxPxfOjNgvpnuum18dxYlXeHJ4r7u8"
await sut.setAuth(expiredToken)

XCTAssertNotEqual(sut.mutableState.accessToken, expiredToken)
}

func testSetAuthWithNonJWT() async throws {
let token = "sb-token"
await sut.setAuth(token)
Expand Down

0 comments on commit 4c95559

Please sign in to comment.