feat(ios): device enrollment flow + silent cert rotation (B3)
Wire the SecureEnclave enroll library into a real flow (login->bearer->CSR->
/device/enroll->keychain identity), presented on the existing mTLS path; add a
rotation scheduler. Atomic keychain replace (add-before-delete); renew body is
{csr}-only; renewal-failing surfaced in the UI. ClientTLS 48 tests pass.
This commit is contained in:
@@ -29,6 +29,11 @@ struct SessionListScreen: View {
|
||||
/// settings-like surface and is present in every empty state, so this is the
|
||||
/// nav idiom that makes the orphaned screen reachable.
|
||||
var onDeviceCert: () -> Void = {}
|
||||
/// B3 · "自动获取证书" entry — presents `EnrollmentScreen` (the zero-`.p12`
|
||||
/// enrollment flow: login → Secure-Enclave key + CSR → device cert). Same
|
||||
/// host-menu surface as `onDeviceCert`; that screen imports a `.p12`, this
|
||||
/// one obtains a hardware-bound cert with no file at all.
|
||||
var onEnroll: () -> Void = {}
|
||||
/// T-iOS-28 (additive slot) · shared thumbnail pipeline: one cache + one
|
||||
/// render-concurrency gate across ALL rows (scrolling must never spawn
|
||||
/// unbounded offscreen terminals). `@State` keeps it stable across body
|
||||
@@ -219,6 +224,12 @@ struct SessionListScreen: View {
|
||||
} label: {
|
||||
Label(ScreenCopy.addHost, systemImage: "plus")
|
||||
}
|
||||
Button {
|
||||
onEnroll()
|
||||
} label: {
|
||||
Label(ScreenCopy.enroll, systemImage: "checkmark.shield")
|
||||
}
|
||||
.accessibilityIdentifier("sessions.enroll")
|
||||
Button {
|
||||
onDeviceCert()
|
||||
} label: {
|
||||
@@ -355,6 +366,7 @@ private enum ScreenCopy {
|
||||
static let kill = "结束"
|
||||
static let addHost = "配对新主机"
|
||||
static let deviceCert = "设备证书"
|
||||
static let enroll = "自动获取证书"
|
||||
static let hostMenuFallback = "主机"
|
||||
static let notPairedTitle = "还没有配对的主机"
|
||||
static let notPairedHint = "先配对你电脑上的 web-terminal(扫码或手输地址),会话会出现在这里。"
|
||||
|
||||
Reference in New Issue
Block a user