Skip to content

Commit 3f2466c

Browse files
samuelalakeBunsDevsteipete
authored
fix(swift): keep device auth compatible with v2 gateways (#80656)
* fix(swift): keep device auth compatible with v2 gateways * refactor(swift): contain v2 auth compatibility * docs(changelog): note Swift v2 auth compatibility * chore(apple): sync native string inventory * chore: keep release changelog owned --------- Co-authored-by: Val Alexander <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
1 parent e500a4f commit 3f2466c

5 files changed

Lines changed: 56 additions & 15 deletions

File tree

apps/.i18n/native-source.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21259,15 +21259,15 @@
2125921259
},
2126021260
{
2126121261
"kind": "conditional-branch",
21262-
"line": 457,
21262+
"line": 455,
2126321263
"path": "apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift",
2126421264
"source": " [\\(initial)]",
2126521265
"surface": "apple",
2126621266
"id": "native.apple.2b7392aa9f8b47df"
2126721267
},
2126821268
{
2126921269
"kind": "conditional-branch",
21270-
"line": 504,
21270+
"line": 502,
2127121271
"path": "apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift",
2127221272
"source": " — \\(option.hint!)",
2127321273
"surface": "apple",

apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,17 +274,15 @@ actor GatewayWizardClient {
274274
let connectNonce = try await self.waitForConnectChallenge()
275275
let identity = DeviceIdentityStore.loadOrCreate()
276276
let signedAtMs = Int(Date().timeIntervalSince1970 * 1000)
277-
let payload = GatewayDeviceAuthPayload.buildV3(
277+
let payload = GatewayDeviceAuthPayload.buildConnectCompatibilityPayload(
278278
deviceId: identity.deviceId,
279279
clientId: clientId,
280280
clientMode: clientMode,
281281
role: role,
282282
scopes: scopes,
283283
signedAtMs: signedAtMs,
284284
token: self.token,
285-
nonce: connectNonce,
286-
platform: platform,
287-
deviceFamily: "Mac")
285+
nonce: connectNonce)
288286
if let device = GatewayDeviceAuthPayload.signedDeviceDictionary(
289287
payload: payload,
290288
identity: identity,

apps/shared/OpenClawKit/Sources/OpenClawKit/DeviceAuthPayload.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@ import Foundation
22
import OpenClawProtocol
33

44
public enum GatewayDeviceAuthPayload {
5+
public static func buildConnectCompatibilityPayload(
6+
deviceId: String,
7+
clientId: String,
8+
clientMode: String,
9+
role: String,
10+
scopes: [String],
11+
signedAtMs: Int,
12+
token: String?,
13+
nonce: String) -> String
14+
{
15+
// Managed gateways deployed before v3 metadata payload support still
16+
// verify v2 signatures. Swift connect signers temporarily omit signed
17+
// metadata until managed and supported self-managed gateways verify v3.
18+
let scopeString = scopes.joined(separator: ",")
19+
let authToken = token ?? ""
20+
return [
21+
"v2",
22+
deviceId,
23+
clientId,
24+
clientMode,
25+
role,
26+
scopeString,
27+
String(signedAtMs),
28+
authToken,
29+
nonce,
30+
].joined(separator: "|")
31+
}
32+
533
public static func buildV3(
634
deviceId: String,
735
clientId: String,

apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayChannel.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -567,17 +567,15 @@ public actor GatewayChannelActor {
567567
let signedAtMs = Int(Date().timeIntervalSince1970 * 1000)
568568
let connectNonce = try await self.waitForConnectChallenge()
569569
if includeDeviceIdentity, let identity {
570-
let payload = GatewayDeviceAuthPayload.buildV3(
570+
let payload = GatewayDeviceAuthPayload.buildConnectCompatibilityPayload(
571571
deviceId: identity.deviceId,
572572
clientId: clientId,
573573
clientMode: clientMode,
574574
role: role,
575575
scopes: scopes,
576576
signedAtMs: signedAtMs,
577577
token: selectedAuth.signatureToken,
578-
nonce: connectNonce,
579-
platform: platform,
580-
deviceFamily: InstanceIdentity.deviceFamily)
578+
nonce: connectNonce)
581579
if let device = GatewayDeviceAuthPayload.signedDeviceDictionary(
582580
payload: payload,
583581
identity: identity,

apps/shared/OpenClawKit/Tests/OpenClawKitTests/DeviceAuthPayloadTests.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,24 @@ import Testing
33

44
@Suite("DeviceAuthPayload")
55
struct DeviceAuthPayloadTests {
6-
@Test("builds canonical v3 payload vector")
7-
func buildsCanonicalV3PayloadVector() {
6+
@Test
7+
func `builds Swift connect compatibility payload with v2 canonical fields`() {
8+
let payload = GatewayDeviceAuthPayload.buildConnectCompatibilityPayload(
9+
deviceId: "dev-1",
10+
clientId: "openclaw-macos",
11+
clientMode: "ui",
12+
role: "operator",
13+
scopes: ["operator.admin", "operator.read"],
14+
signedAtMs: 1_700_000_000_000,
15+
token: "tok-123",
16+
nonce: "nonce-abc")
17+
#expect(
18+
payload
19+
== "v2|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1700000000000|tok-123|nonce-abc")
20+
}
21+
22+
@Test
23+
func `builds canonical v3 payload vector`() {
824
let payload = GatewayDeviceAuthPayload.buildV3(
925
deviceId: "dev-1",
1026
clientId: "openclaw-macos",
@@ -18,11 +34,12 @@ struct DeviceAuthPayloadTests {
1834
deviceFamily: " iPhone ")
1935
#expect(
2036
payload
21-
== "v3|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1700000000000|tok-123|nonce-abc|ios|iphone")
37+
==
38+
"v3|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1700000000000|tok-123|nonce-abc|ios|iphone")
2239
}
2340

24-
@Test("normalizes metadata with ASCII-only lowercase")
25-
func normalizesMetadataWithAsciiLowercase() {
41+
@Test
42+
func `normalizes metadata with ASCII-only lowercase`() {
2643
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(" İOS ") == "İos")
2744
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(" MAC ") == "mac")
2845
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(nil) == "")

0 commit comments

Comments
 (0)