Skip to content

Commit b302365

Browse files
authored
Merge pull request #655 from MultiColourPixel/main
Support FIDO2 authentication with devices that don’t have a PIN code
2 parents 17f3d36 + 259ad07 commit b302365

5 files changed

Lines changed: 56 additions & 14 deletions

File tree

Xcodes.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,7 @@
14931493
repositoryURL = "https://github.com/kinoroy/LibFido2Swift.git";
14941494
requirement = {
14951495
kind = upToNextMinorVersion;
1496-
minimumVersion = 0.1.0;
1496+
minimumVersion = 0.1.4;
14971497
};
14981498
};
14991499
CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */ = {

Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Xcodes/Backend/AppState.swift

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,17 @@ class AppState: ObservableObject {
305305
}
306306

307307
func handleTwoFactorOption(_ option: TwoFactorOption, authOptions: AuthOptionsResponse, serviceKey: String, sessionID: String, scnt: String) {
308-
self.presentedSheet = .twoFactor(.init(
309-
option: option,
310-
authOptions: authOptions,
311-
sessionData: AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt)
312-
))
308+
let sessionData = AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt)
309+
310+
if option == .securityKey, fido2DeviceIsPresent() && !fido2DeviceNeedsPin() {
311+
createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions)
312+
} else {
313+
self.presentedSheet = .twoFactor(.init(
314+
option: option,
315+
authOptions: authOptions,
316+
sessionData: sessionData
317+
))
318+
}
313319
}
314320

315321
func requestSMS(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) {
@@ -355,9 +361,9 @@ class AppState: ObservableObject {
355361
.store(in: &cancellables)
356362
}
357363

358-
var fido2: FIDO2?
359-
360-
func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) {
364+
private lazy var fido2 = FIDO2()
365+
366+
func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String?, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) {
361367
self.presentedSheet = .securityKeyTouchToConfirm
362368

363369
guard let fsaChallenge = authOptions.fsaChallenge else {
@@ -379,8 +385,6 @@ class AppState: ObservableObject {
379385

380386
Task {
381387
do {
382-
let fido2 = FIDO2()
383-
self.fido2 = fido2
384388
let response = try fido2.respondToChallenge(args: ChallengeArgs(rpId: rpId, validCredentials: validCreds, devPin: pinCode, challenge: challenge, origin: origin))
385389

386390
Task { @MainActor in
@@ -407,13 +411,31 @@ class AppState: ObservableObject {
407411
// we don't have to show an error
408412
// because the sheet will already be dismissed
409413
} catch {
414+
Task { @MainActor in
415+
authError = error
416+
}
417+
}
418+
}
419+
}
420+
421+
func fido2DeviceIsPresent() -> Bool {
422+
fido2.hasDeviceAttached()
423+
}
424+
425+
func fido2DeviceNeedsPin() -> Bool {
426+
do {
427+
return try fido2.deviceHasPin()
428+
} catch {
429+
Task { @MainActor in
410430
authError = error
411431
}
432+
433+
return true
412434
}
413435
}
414436

415437
func cancelSecurityKeyAssertationRequest() {
416-
self.fido2?.cancel()
438+
self.fido2.cancel()
417439
}
418440

419441
private func handleAuthenticationFlowCompletion(_ completion: Subscribers.Completion<Error>) {

Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ struct SignInSecurityKeyPinView: View {
3232
Button("Cancel", action: { isPresented = false })
3333
.keyboardShortcut(.cancelAction)
3434
Spacer()
35+
36+
Button("PIN not set", action: submitWithoutPinCode)
37+
3538
ProgressButton(isInProgress: appState.isProcessingAuthRequest,
3639
action: submitPinCode) {
3740
Text("Continue")
@@ -50,6 +53,10 @@ struct SignInSecurityKeyPinView: View {
5053
func submitPinCode() {
5154
appState.createAndSubmitSecurityKeyAssertationWithPinCode(pin, sessionData: sessionData, authOptions: authOptions)
5255
}
56+
57+
func submitWithoutPinCode() {
58+
appState.createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions)
59+
}
5360
}
5461

5562
#Preview {

Xcodes/Resources/Localizable.xcstrings

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,16 @@
237237
}
238238
}
239239
},
240+
"%@ (%@)" : {
241+
"localizations" : {
242+
"en" : {
243+
"stringUnit" : {
244+
"state" : "new",
245+
"value" : "%1$@ (%2$@)"
246+
}
247+
}
248+
}
249+
},
240250
"%@ %@ %@" : {
241251
"localizations" : {
242252
"ar" : {
@@ -17907,6 +17917,9 @@
1790717917
}
1790817918
}
1790917919
}
17920+
},
17921+
"PIN not set" : {
17922+
1791017923
},
1791117924
"Platforms" : {
1791217925
"localizations" : {

0 commit comments

Comments
 (0)