mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-16 17:31:42 +00:00
* 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 <68980965+BunsDev@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
105 lines
3.4 KiB
Swift
105 lines
3.4 KiB
Swift
import Foundation
|
|
import OpenClawProtocol
|
|
|
|
public enum GatewayDeviceAuthPayload {
|
|
public static func buildConnectCompatibilityPayload(
|
|
deviceId: String,
|
|
clientId: String,
|
|
clientMode: String,
|
|
role: String,
|
|
scopes: [String],
|
|
signedAtMs: Int,
|
|
token: String?,
|
|
nonce: String) -> String
|
|
{
|
|
// Managed gateways deployed before v3 metadata payload support still
|
|
// verify v2 signatures. Swift connect signers temporarily omit signed
|
|
// metadata until managed and supported self-managed gateways verify v3.
|
|
let scopeString = scopes.joined(separator: ",")
|
|
let authToken = token ?? ""
|
|
return [
|
|
"v2",
|
|
deviceId,
|
|
clientId,
|
|
clientMode,
|
|
role,
|
|
scopeString,
|
|
String(signedAtMs),
|
|
authToken,
|
|
nonce,
|
|
].joined(separator: "|")
|
|
}
|
|
|
|
public static func buildV3(
|
|
deviceId: String,
|
|
clientId: String,
|
|
clientMode: String,
|
|
role: String,
|
|
scopes: [String],
|
|
signedAtMs: Int,
|
|
token: String?,
|
|
nonce: String,
|
|
platform: String?,
|
|
deviceFamily: String?) -> String
|
|
{
|
|
let scopeString = scopes.joined(separator: ",")
|
|
let authToken = token ?? ""
|
|
let normalizedPlatform = self.normalizeMetadataField(platform)
|
|
let normalizedDeviceFamily = self.normalizeMetadataField(deviceFamily)
|
|
return [
|
|
"v3",
|
|
deviceId,
|
|
clientId,
|
|
clientMode,
|
|
role,
|
|
scopeString,
|
|
String(signedAtMs),
|
|
authToken,
|
|
nonce,
|
|
normalizedPlatform,
|
|
normalizedDeviceFamily,
|
|
].joined(separator: "|")
|
|
}
|
|
|
|
static func normalizeMetadataField(_ value: String?) -> String {
|
|
guard let value else { return "" }
|
|
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if trimmed.isEmpty {
|
|
return ""
|
|
}
|
|
// Keep cross-runtime normalization deterministic (TS/Swift/Kotlin):
|
|
// lowercase ASCII A-Z only for auth payload metadata fields.
|
|
var output = String()
|
|
output.reserveCapacity(trimmed.count)
|
|
for scalar in trimmed.unicodeScalars {
|
|
let codePoint = scalar.value
|
|
if codePoint >= 65, codePoint <= 90, let lowered = UnicodeScalar(codePoint + 32) {
|
|
output.unicodeScalars.append(lowered)
|
|
} else {
|
|
output.unicodeScalars.append(scalar)
|
|
}
|
|
}
|
|
return output
|
|
}
|
|
|
|
public static func signedDeviceDictionary(
|
|
payload: String,
|
|
identity: DeviceIdentity,
|
|
signedAtMs: Int,
|
|
nonce: String) -> [String: OpenClawProtocol.AnyCodable]?
|
|
{
|
|
guard let signature = DeviceIdentityStore.signPayload(payload, identity: identity),
|
|
let publicKey = DeviceIdentityStore.publicKeyBase64Url(identity)
|
|
else {
|
|
return nil
|
|
}
|
|
return [
|
|
"id": OpenClawProtocol.AnyCodable(identity.deviceId),
|
|
"publicKey": OpenClawProtocol.AnyCodable(publicKey),
|
|
"signature": OpenClawProtocol.AnyCodable(signature),
|
|
"signedAt": OpenClawProtocol.AnyCodable(signedAtMs),
|
|
"nonce": OpenClawProtocol.AnyCodable(nonce),
|
|
]
|
|
}
|
|
}
|