Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions packages/passkeys/passkeys/lib/authenticator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ class PasskeyAuthenticator {
final bool debugMode;

/// Returns true only if passkeys are supported by the platform.
@Deprecated('Use PasskeyAvailability.isAvailable instead. '
'This method will be removed in a future release.')
@Deprecated(
'Use PasskeyAvailability.isAvailable instead. '
'This method will be removed in a future release.',
)
Future<bool> canAuthenticate() {
return _platform.canAuthenticate();
}
Expand Down Expand Up @@ -86,7 +88,13 @@ class PasskeyAuthenticator {
case 'ios-security-key-timeout':
throw TimeoutException(e.message);
default:
rethrow;
if (e.code.startsWith('android-unhandled')) {
throw UnhandledAuthenticatorException(e.code, e.message, e.details);
} else if (e.code.startsWith('ios-unhandled')) {
throw UnhandledAuthenticatorException(e.code, e.message, e.details);
} else {
rethrow;
}
}
}
}
Expand Down Expand Up @@ -229,8 +237,10 @@ class PasskeyAuthenticator {
if (!base64UrlRegex.hasMatch(input)) return false;

try {
String normalized =
input.padRight(input.length + (4 - input.length % 4) % 4, '=');
String normalized = input.padRight(
input.length + (4 - input.length % 4) % 4,
'=',
);
base64Url.decode(normalized);

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,10 @@ public void onError(CreateCredentialException e) {
} else if (Objects.equals(e.getMessage(), "Unable to get sync account.")) {
platformException = new Messages.FlutterError("android-sync-account-not-available",
e.getMessage(), SYNC_ACCOUNT_NOT_AVAILABLE_ERROR);
} else if (Objects.equals(e.getMessage(),
"One of the excluded credentials exists on the local device")) {
} else if (e.getMessage() != null && e.getMessage().toLowerCase().contains("excluded credentials")) {
// Match excluded credentials error by substring instead of exact match,
// as the error message may vary across Android / Google Play Services versions.
// Also handles TYPE_INVALID_STATE_ERROR with excluded credentials message.
platformException = new Messages.FlutterError("exclude-credentials-match",
e.getMessage(), EXCLUDE_CREDENTIALS_MATCH_ERROR);
} else if (Objects.equals(e.getMessage(), "[15] Flow has timed out.")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ extension FlutterError: Error {
}
break
default:
code = "unknown"
// ASAuthorizationError code 1006 (.matchedExcludedCredential, iOS 18+)
// indicates the device already holds a credential listed in excludeCredentials.
if error.code.rawValue == 1006 {
code = "exclude-credentials-match"
} else {
code = "unknown"
}
break
}

Expand Down