Files
openclaw/apps/shared/OpenClawKit/Tests/OpenClawKitTests/DeviceAuthPayloadTests.swift
Peter Steinberger 98410d986e refactor(state): move device identity into canonical SQLite state (#110392)
* refactor: store device identities in SQLite

* style: satisfy Swift identity lint limits

* test: harden device identity migration fixtures

* fix: harden SQLite device identity recovery

* chore: remove stale identity helper

* refactor: isolate device identity repair

* chore: remove stale migration import

* test: validate migrated identity key types

* fix: harden device identity repair

* style: format device identity assertion

* fix: derive repaired identity key from PEM

* style: remove redundant PEM conversions

* fix: align native identity store with schema v4

* fix(state): satisfy SQLite identity CI gates

* fix(state): coordinate native identity migration

* test(doctor): include native identity claim path

* fix(infra): preserve coordinator release error

* fix(state): clear identity validation gates

* refactor(apple): remove identity test-only APIs

* refactor(apple): remove dead identity error type

* test(apple): use canonical identity test seam

* test(macos): isolate gateway readiness identity
2026-07-18 17:43:56 +01:00

95 lines
4.0 KiB
Swift

import CryptoKit
import Foundation
import Testing
@testable import OpenClawKit
@Suite("DeviceAuthPayload")
struct DeviceAuthPayloadTests {
@Test
func `builds Swift connect compatibility payload with v2 canonical fields`() {
let signedAtMs: Int64 = 1_800_000_000_000
let payload = GatewayDeviceAuthPayload.buildConnectCompatibilityPayload(
fields: .init(
deviceId: "dev-1",
client: .init(id: "openclaw-macos", mode: "ui"),
role: "operator",
scopes: ["operator.admin", "operator.read"],
signedAtMs: signedAtMs,
token: "tok-123",
nonce: "nonce-abc"))
#expect(
payload
== "v2|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1800000000000|tok-123|nonce-abc")
}
@Test
func `builds canonical v3 payload vector`() {
let signedAtMs: Int64 = 1_800_000_000_000
let payload = GatewayDeviceAuthPayload.buildV3(
fields: .init(
deviceId: "dev-1",
client: .init(id: "openclaw-macos", mode: "ui"),
role: "operator",
scopes: ["operator.admin", "operator.read"],
signedAtMs: signedAtMs,
token: "tok-123",
nonce: "nonce-abc"),
platform: " IOS ",
deviceFamily: " iPhone ")
#expect(
payload
==
"v3|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1800000000000|tok-123|nonce-abc|ios|iphone")
}
@Test
func `signed device dictionary preserves 64-bit timestamp`() throws {
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString, isDirectory: true)
let databaseURL = tempDir.appendingPathComponent("openclaw.sqlite", isDirectory: false)
defer { try? FileManager.default.removeItem(at: tempDir) }
let identity = try DeviceIdentitySQLiteStore.loadOrCreate(
databaseURL: databaseURL,
destinationStateDirURL: tempDir,
profile: .primary)
let signedAtMs: Int64 = 1_800_000_000_000
let payload = GatewayDeviceAuthPayload.buildV3(
fields: .init(
deviceId: identity.deviceId,
client: .init(id: "openclaw-watchos", mode: "node"),
role: "node",
scopes: [],
signedAtMs: signedAtMs,
token: "device-token",
nonce: "nonce-abc"),
platform: "watchOS",
deviceFamily: "Apple Watch")
let device = try #require(GatewayDeviceAuthPayload.signedDeviceDictionary(
payload: payload,
identity: identity,
signedAtMs: signedAtMs,
nonce: "nonce-abc"))
let signature = try #require(device["signature"]?.value as? String)
let signatureBase64 = signature
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let signaturePadding = String(repeating: "=", count: (4 - signatureBase64.count % 4) % 4)
let signatureData = try #require(Data(base64Encoded: signatureBase64 + signaturePadding))
let publicKeyData = try #require(Data(base64Encoded: identity.publicKey))
let publicKey = try Curve25519.Signing.PublicKey(rawRepresentation: publicKeyData)
let data = try JSONEncoder().encode(device)
let object = try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
#expect(publicKey.isValidSignature(signatureData, for: Data(payload.utf8)))
#expect((object["signedAt"] as? NSNumber)?.int64Value == signedAtMs)
}
@Test
func `normalizes metadata with ASCII-only lowercase`() {
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(" İOS ") == "İos")
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(" MAC ") == "mac")
#expect(GatewayDeviceAuthPayload.normalizeMetadataField(nil) == "")
}
}