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
+5 -19
View File
@@ -12,33 +12,19 @@ extension ApprovalRequest {
}
var locationSummary: String {
"Berlin, DE"
locationSummaryText ?? "Location not required"
}
var deviceSummary: String {
switch kind {
case .signIn:
"Safari on Berlin iPhone"
case .accessGrant:
"Chrome on iPad Pro"
case .elevatedAction:
"Berlin MacBook Pro"
}
deviceSummaryText ?? "Trusted passport device"
}
var networkSummary: String {
switch kind {
case .signIn:
"Home Wi-Fi"
case .accessGrant:
"Shared office Wi-Fi"
case .elevatedAction:
"Ethernet"
}
networkSummaryText ?? source
}
var ipSummary: String {
risk == .elevated ? "84.187.12.44" : "84.187.12.36"
ipSummaryText ?? "n/a"
}
var trustColor: Color {
@@ -66,7 +52,7 @@ extension ApprovalRequest {
}
var expiresAt: Date {
createdAt.addingTimeInterval(risk == .elevated ? 180 : 300)
expiresAtDate ?? createdAt.addingTimeInterval(risk == .elevated ? 180 : 300)
}
}
+22 -24
View File
@@ -231,27 +231,31 @@ struct NotificationCenterView: View {
struct DevicesView: View {
@ObservedObject var model: AppViewModel
@State private var isPairingCodePresented = false
private var devices: [DevicePresentation] {
guard let session else { return [] }
let current = DevicePresentation(
name: session.deviceName,
systemImage: symbolName(for: session.deviceName),
lastSeen: .now,
isCurrent: true,
isTrusted: true
)
if !model.devices.isEmpty {
return model.devices.map { device in
DevicePresentation(
name: device.label,
systemImage: symbolName(for: device.label),
lastSeen: device.lastSeenAt ?? session.pairedAt,
isCurrent: device.isCurrent,
isTrusted: true
)
}
}
let others = [
DevicePresentation(name: "Phil's iPad Pro", systemImage: "ipad", lastSeen: .now.addingTimeInterval(-60 * 18), isCurrent: false, isTrusted: true),
DevicePresentation(name: "Berlin MacBook Pro", systemImage: "laptopcomputer", lastSeen: .now.addingTimeInterval(-60 * 74), isCurrent: false, isTrusted: true),
DevicePresentation(name: "Apple Watch", systemImage: "applewatch", lastSeen: .now.addingTimeInterval(-60 * 180), isCurrent: false, isTrusted: false)
return [
DevicePresentation(
name: session.deviceName,
systemImage: symbolName(for: session.deviceName),
lastSeen: .now,
isCurrent: true,
isTrusted: true
)
]
let count = max((model.profile?.deviceCount ?? 1) - 1, 0)
return [current] + Array(others.prefix(count))
}
private var session: AuthSession? {
@@ -274,10 +278,9 @@ struct DevicesView: View {
Section {
VStack(spacing: 12) {
Button("Pair another device") {
isPairingCodePresented = true
}
.buttonStyle(PrimaryActionStyle())
Text("Start new device pairing from your idp.global web session, then scan the fresh pairing QR in this app.")
.font(.footnote)
.foregroundStyle(.secondary)
Button("Sign out everywhere") {
model.signOut()
@@ -288,11 +291,6 @@ struct DevicesView: View {
}
}
.navigationTitle("Devices")
.sheet(isPresented: $isPairingCodePresented) {
if let session {
OneTimePasscodeSheet(session: session)
}
}
}
private func symbolName(for deviceName: String) -> String {