I’m implementing Sign in with Apple / Passkeys using ASAuthorizationControllerDelegate and handling errors via:
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithError error: Error
) {
guard let authError = error as? ASAuthorizationError else {
print("Unknown error:", error)
return
}
switch authError.code {
case .canceled:
print("User canceled authentication")
case .failed:
print("Authorization failed (generic)")
case .invalidResponse:
print("Invalid response from Apple")
case .notHandled:
print("Authorization not handled")
case .unknown:
print("Unknown authorization error")
@unknown default:
print("Future unknown error")
}
}
However, I’m consistently seeing .canceled in all of the following situations:
• User taps the ❌ button → .canceled (expected)
• User cancels during Face ID → .canceled
• Face ID fails and falls back to device passcode
• User enters the wrong passcode → still .canceled
I never receive .failed for passcode failures or biometric fallback failures — only .canceled.
Questions
- Is this the expected behavior for ASAuthorizationController?
- Why does a failed passcode attempt result in .canceled instead of .failed?
- Is there any supported way to differentiate user cancellation vs authentication failure (Face ID / passcode) using this API?
- When should .failed actually be expected to occur?