Skip to content

[iOS/Android] Duplicate passkey registration not mapped to ExcludeCredentialsCanNotBeRegisteredException #232

@shuuun

Description

@shuuun

Description

When a user attempts to register a passkey that already exists on the device (duplicate registration), both iOS and Android fail to map the native error to exclude-credentials-match, causing it to surface as a generic PlatformException instead of ExcludeCredentialsCanNotBeRegisteredException.

Current Behavior

iOS

PlatformException(unknown, The operation couldn't be completed.
(com.apple.AuthenticationServices.AuthorizationError error 1006.), , null)

Android

PlatformException(android-unhandled:
androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION/androidx.credentials.TYPE_INVALID_STATE_ERROR,
One of the excluded credentials exists on the local device.,
One of the excluded credentials exists on the local device., null)

In both cases, the error is not caught by PasskeyAuthenticator and falls through to the generic catch block.

Expected Behavior

The error should be mapped to exclude-credentials-match and result in ExcludeCredentialsCanNotBeRegisteredException on both platforms.

Root Cause

iOS — ErrorExtension.swift

Only WKErrorDomain with code 8 is mapped to exclude-credentials-match:

https://github.com/corbado/flutter-passkeys/blob/main/packages/passkeys/passkeys_darwin/darwin/Classes/ErrorExtension.swift

if (error.domain == "WKErrorDomain" && error.code == 8) {
    code = "exclude-credentials-match"
}

The native ASAuthorizationError domain with code 1006 (matchedExcludedCredential) is not handled.

Android — MessageHandler.java

The existing check matches the error message:

if (Objects.equals(e.getMessage(),
    "One of the excluded credentials exists on the local device")) {
    platformException = new Messages.FlutterError("exclude-credentials-match", ...);
}

However, when the error comes as TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION with sub-type TYPE_INVALID_STATE_ERROR, it appears to be caught by a different branch and classified as android-unhandled before reaching this check.

Suggested Fix

iOS

Add a condition for the ASAuthorizationError domain in ErrorExtension.swift:

} else if (error.domain == "com.apple.AuthenticationServices.AuthorizationError" && error.code == 1006) {
    code = "exclude-credentials-match"
}

Android

Ensure that TYPE_INVALID_STATE_ERROR with the excluded credentials message is also mapped to exclude-credentials-match.

Environment

  • passkeys: 2.18.0
  • Flutter 3.x

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions