replace mock passport flows with live server integration
CI / test (push) Has been cancelled

Switch the app to the real passport enrollment, dashboard, device, alert, and challenge APIs so it can pair with idp.global and act on server-backed state instead of demo data.
This commit is contained in:
2026-04-20 13:21:39 +00:00
parent 271d9657bf
commit a298b5e421
7 changed files with 1136 additions and 48 deletions
+158
View File
@@ -181,6 +181,19 @@ struct DashboardSnapshot {
let profile: MemberProfile
let requests: [ApprovalRequest]
let notifications: [AppNotification]
let devices: [PassportDeviceRecord]
init(
profile: MemberProfile,
requests: [ApprovalRequest],
notifications: [AppNotification],
devices: [PassportDeviceRecord] = []
) {
self.profile = profile
self.requests = requests
self.notifications = notifications
self.devices = devices
}
}
struct SignInResult {
@@ -217,6 +230,8 @@ struct AuthSession: Identifiable, Hashable, Codable {
let id: UUID
let deviceName: String
let originHost: String
let serverURL: String
let passportDeviceID: String
let pairedAt: Date
let tokenPreview: String
let pairingCode: String
@@ -227,6 +242,8 @@ struct AuthSession: Identifiable, Hashable, Codable {
id: UUID = UUID(),
deviceName: String,
originHost: String,
serverURL: String = "",
passportDeviceID: String = "",
pairedAt: Date,
tokenPreview: String,
pairingCode: String,
@@ -236,12 +253,41 @@ struct AuthSession: Identifiable, Hashable, Codable {
self.id = id
self.deviceName = deviceName
self.originHost = originHost
self.serverURL = serverURL
self.passportDeviceID = passportDeviceID
self.pairedAt = pairedAt
self.tokenPreview = tokenPreview
self.pairingCode = pairingCode
self.pairingTransport = pairingTransport
self.signedGPSPosition = signedGPSPosition
}
private enum CodingKeys: String, CodingKey {
case id
case deviceName
case originHost
case serverURL
case passportDeviceID
case pairedAt
case tokenPreview
case pairingCode
case pairingTransport
case signedGPSPosition
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(UUID.self, forKey: .id) ?? UUID()
deviceName = try container.decode(String.self, forKey: .deviceName)
originHost = try container.decode(String.self, forKey: .originHost)
serverURL = try container.decodeIfPresent(String.self, forKey: .serverURL) ?? "https://\(originHost)"
passportDeviceID = try container.decodeIfPresent(String.self, forKey: .passportDeviceID) ?? ""
pairedAt = try container.decode(Date.self, forKey: .pairedAt)
tokenPreview = try container.decode(String.self, forKey: .tokenPreview)
pairingCode = try container.decode(String.self, forKey: .pairingCode)
pairingTransport = try container.decodeIfPresent(PairingTransport.self, forKey: .pairingTransport) ?? .manual
signedGPSPosition = try container.decodeIfPresent(SignedGPSPosition.self, forKey: .signedGPSPosition)
}
}
enum ApprovalRequestKind: String, CaseIterable, Hashable, Codable {
@@ -320,6 +366,8 @@ enum ApprovalStatus: String, Hashable, Codable {
struct ApprovalRequest: Identifiable, Hashable, Codable {
let id: UUID
let serverID: String
let hintID: String
let title: String
let subtitle: String
let source: String
@@ -327,10 +375,20 @@ struct ApprovalRequest: Identifiable, Hashable, Codable {
let kind: ApprovalRequestKind
let risk: ApprovalRisk
let scopes: [String]
let requiresLocation: Bool
let challenge: String?
let signingPayload: String?
let deviceSummaryText: String?
let locationSummaryText: String?
let networkSummaryText: String?
let ipSummaryText: String?
let expiresAtDate: Date?
var status: ApprovalStatus
init(
id: UUID = UUID(),
serverID: String = UUID().uuidString,
hintID: String = UUID().uuidString,
title: String,
subtitle: String,
source: String,
@@ -338,9 +396,19 @@ struct ApprovalRequest: Identifiable, Hashable, Codable {
kind: ApprovalRequestKind,
risk: ApprovalRisk,
scopes: [String],
requiresLocation: Bool = false,
challenge: String? = nil,
signingPayload: String? = nil,
deviceSummaryText: String? = nil,
locationSummaryText: String? = nil,
networkSummaryText: String? = nil,
ipSummaryText: String? = nil,
expiresAtDate: Date? = nil,
status: ApprovalStatus
) {
self.id = id
self.serverID = serverID
self.hintID = hintID
self.title = title
self.subtitle = subtitle
self.source = source
@@ -348,9 +416,62 @@ struct ApprovalRequest: Identifiable, Hashable, Codable {
self.kind = kind
self.risk = risk
self.scopes = scopes
self.requiresLocation = requiresLocation
self.challenge = challenge
self.signingPayload = signingPayload
self.deviceSummaryText = deviceSummaryText
self.locationSummaryText = locationSummaryText
self.networkSummaryText = networkSummaryText
self.ipSummaryText = ipSummaryText
self.expiresAtDate = expiresAtDate
self.status = status
}
private enum CodingKeys: String, CodingKey {
case id
case serverID
case hintID
case title
case subtitle
case source
case createdAt
case kind
case risk
case scopes
case requiresLocation
case challenge
case signingPayload
case deviceSummaryText
case locationSummaryText
case networkSummaryText
case ipSummaryText
case expiresAtDate
case status
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(UUID.self, forKey: .id) ?? UUID()
serverID = try container.decodeIfPresent(String.self, forKey: .serverID) ?? id.uuidString
hintID = try container.decodeIfPresent(String.self, forKey: .hintID) ?? serverID
title = try container.decode(String.self, forKey: .title)
subtitle = try container.decode(String.self, forKey: .subtitle)
source = try container.decode(String.self, forKey: .source)
createdAt = try container.decode(Date.self, forKey: .createdAt)
kind = try container.decode(ApprovalRequestKind.self, forKey: .kind)
risk = try container.decode(ApprovalRisk.self, forKey: .risk)
scopes = try container.decode([String].self, forKey: .scopes)
requiresLocation = try container.decodeIfPresent(Bool.self, forKey: .requiresLocation) ?? false
challenge = try container.decodeIfPresent(String.self, forKey: .challenge)
signingPayload = try container.decodeIfPresent(String.self, forKey: .signingPayload)
deviceSummaryText = try container.decodeIfPresent(String.self, forKey: .deviceSummaryText)
locationSummaryText = try container.decodeIfPresent(String.self, forKey: .locationSummaryText)
networkSummaryText = try container.decodeIfPresent(String.self, forKey: .networkSummaryText)
ipSummaryText = try container.decodeIfPresent(String.self, forKey: .ipSummaryText)
expiresAtDate = try container.decodeIfPresent(Date.self, forKey: .expiresAtDate)
status = try container.decode(ApprovalStatus.self, forKey: .status)
}
var scopeSummary: String {
if scopes.isEmpty {
return "No proof details listed"
@@ -420,6 +541,8 @@ enum AppNotificationKind: String, Hashable, Codable {
struct AppNotification: Identifiable, Hashable, Codable {
let id: UUID
let serverID: String
let hintID: String
let title: String
let message: String
let sentAt: Date
@@ -428,6 +551,8 @@ struct AppNotification: Identifiable, Hashable, Codable {
init(
id: UUID = UUID(),
serverID: String = UUID().uuidString,
hintID: String = UUID().uuidString,
title: String,
message: String,
sentAt: Date,
@@ -435,12 +560,45 @@ struct AppNotification: Identifiable, Hashable, Codable {
isUnread: Bool
) {
self.id = id
self.serverID = serverID
self.hintID = hintID
self.title = title
self.message = message
self.sentAt = sentAt
self.kind = kind
self.isUnread = isUnread
}
private enum CodingKeys: String, CodingKey {
case id
case serverID
case hintID
case title
case message
case sentAt
case kind
case isUnread
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(UUID.self, forKey: .id) ?? UUID()
serverID = try container.decodeIfPresent(String.self, forKey: .serverID) ?? id.uuidString
hintID = try container.decodeIfPresent(String.self, forKey: .hintID) ?? serverID
title = try container.decode(String.self, forKey: .title)
message = try container.decode(String.self, forKey: .message)
sentAt = try container.decode(Date.self, forKey: .sentAt)
kind = try container.decode(AppNotificationKind.self, forKey: .kind)
isUnread = try container.decode(Bool.self, forKey: .isUnread)
}
}
struct PassportDeviceRecord: Identifiable, Hashable, Codable {
let id: String
let label: String
let platform: String
let lastSeenAt: Date?
let isCurrent: Bool
}
enum AppError: LocalizedError, Equatable {