feat: Adds passkeys support to flutter SDK#858
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughPasskey login, signup, credential exchange, and My Account enrollment are added across the shared Dart APIs, platform interfaces, native handlers, examples, tests, docs, and build configuration. iOS and macOS now share Darwin implementations, and the example apps wire passkey UI through platform credentials. ChangesPasskey authentication and My Account enrollment
✨ Finishing Touches🧪 Generate unit tests (beta)
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
auth0_flutter/example/ios/Runner.xcodeproj/project.pbxproj (2)
127-127:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winDuplicate PBXFileReference entry will corrupt the Xcode project.
Line 127 and line 158 contain identical
PBXFileReferenceentries forCredentialsManagerSSOCredentialsMethodHandlerTests.swift. This duplication will cause Xcode project parsing errors.🔧 Remove the duplicate entry
C16443EA2BA08C71AE68B41B /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; C7412C9A0F502116EC6D51D0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; C8270E3F40357C2A1DB27BB8 /* Pods_Runner_RunnerUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner_RunnerUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A09B2E3F1D4C7891BA000001 /* CredentialsManagerSSOCredentialsMethodHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialsManagerSSOCredentialsMethodHandlerTests.swift; sourceTree = "<group>"; }; B0CA00010000000000000001 /* MyAccountSpies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAccountSpies.swift; sourceTree = "<group>"; };Also applies to: 158-158
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/example/ios/Runner.xcodeproj/project.pbxproj` at line 127, The project.pbxproj contains a duplicate PBXFileReference entry for CredentialsManagerSSOCredentialsMethodHandlerTests.swift (GUID A09B2E3F1D4C7891BA000001) which will break Xcode; remove the redundant PBXFileReference block so only one entry with that GUID and path remains, and then verify any references in PBXGroup or PBXSourcesBuildPhase (or similar sections) point to the remaining GUID to avoid dangling or duplicated references.
29-29:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winDuplicate PBXBuildFile entry will corrupt the Xcode project.
Line 29 and line 49 contain identical
PBXBuildFileentries forCredentialsManagerSSOCredentialsMethodHandlerTests.swift. Xcode project files must have unique object identifiers; duplicates cause project corruption and build failures.🔧 Remove the duplicate entry
A0AC1D0F2E0000000000AC04 /* CredentialsManagerClearApiCredentialsMethodHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0AC1D0F2E0000000000AC03 /* CredentialsManagerClearApiCredentialsMethodHandlerTests.swift */; }; 5C59DA6527FFCF0600365CDB /* AuthAPIHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C59DA6427FFCF0600365CDB /* AuthAPIHandlerTests.swift */; }; 5C59DA6727FFE75600365CDB /* AuthAPILoginUsernameOrEmailMethodHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C59DA6627FFE75600365CDB /* AuthAPILoginUsernameOrEmailMethodHandlerTests.swift */; }; - A09B2E3F1D4C7891BA000002 /* CredentialsManagerSSOCredentialsMethodHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A09B2E3F1D4C7891BA000001 /* CredentialsManagerSSOCredentialsMethodHandlerTests.swift */; }; A128A3E84540D9257B15491D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03C97032096FE7868ACD2E52 /* Pods_Runner.framework */; };Also applies to: 49-49
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/example/ios/Runner.xcodeproj/project.pbxproj` at line 29, The project file contains a duplicate PBXBuildFile entry for CredentialsManagerSSOCredentialsMethodHandlerTests.swift (object ID A09B2E3F1D4C7891BA000002 with fileRef A09B2E3F1D4C7891BA000001) which must be removed; locate the duplicate PBXBuildFile block (the second occurrence around the other PBXBuildFile entries) and delete it so there is only one PBXBuildFile entry for that source file, then scan build phases, PBXGroup and any other references to ensure they point to the remaining object ID (or update them to the kept ID) to avoid dangling or duplicate identifiers.
🧹 Nitpick comments (7)
auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart (1)
1383-1661: ⚡ Quick winAdd null-result failure-path tests for new passkey channel methods.
The new passkey groups validate
PlatformException, but they don’t assert behavior when the method channel returnsnull(a path already covered in other API groups). Adding these cases would keep passkey methods aligned with existing error-contract coverage.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart` around lines 1383 - 1661, The tests for the new passkey channels currently only cover PlatformException paths but are missing the null-return failure-paths; add unit tests that mock mocked.methodCallHandler to return null and assert that MethodChannelAuth0FlutterAuth().passkeyLoginChallenge, .passkeyCredentialExchange, and .passkeySignupChallenge each throw an ApiException when the platform channel result is null, mirroring the existing null-result tests for other API groups and using verify(captureAny) where appropriate to locate the invoked method.auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart (1)
28-31: ⚡ Quick winAvoid exposing internal map references in serialization.
Line 30 returns
authParamsPublicKeyby reference, so callers mutating the returned map can mutate the model state unexpectedly.Suggested change
Map<String, dynamic> toMap() => { 'authSession': authSession, - 'authParamsPublicKey': authParamsPublicKey, + 'authParamsPublicKey': Map<String, dynamic>.from(authParamsPublicKey), };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart` around lines 28 - 31, The toMap() serializer is returning the internal authParamsPublicKey map by reference, exposing mutable state; update toMap() so it returns a defensive copy of authParamsPublicKey (e.g., create a new Map from authParamsPublicKey) instead of the original reference while still returning authSession as before, ensuring callers cannot mutate the model's internal map via the serialized output.auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.kt (1)
55-58: ⚡ Quick winSimplify the Gson serialization.
The double serialization (object → JSON string → Map) is inefficient. Consider using
Gson().toJsonTree(challenge.authParamsPublicKey).asJsonObjector a similar approach to convert directly to a Map structure without the intermediate JSON string step.♻️ Proposed simplification
- val authParamsPublicKey: Map<*, *> = Gson().fromJson( - Gson().toJson(challenge.authParamsPublicKey), - Map::class.java - ) + `@Suppress`("UNCHECKED_CAST") + val authParamsPublicKey = Gson().toJsonTree(challenge.authParamsPublicKey).asJsonObject.asMap() as Map<*, *>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.kt` around lines 55 - 58, The double-serialization in PasskeySignupChallengeApiRequestHandler (creating authParamsPublicKey by Gson().toJson(...) then fromJson(..., Map::class.java)) is inefficient; replace it with a direct conversion such as using Gson().toJsonTree(challenge.authParamsPublicKey).asJsonObject (or Gson().fromJson with a TypeToken<Map<String,Any>>() to get a Map) to avoid the intermediate JSON string — update the authParamsPublicKey assignment in that handler to use the direct JsonTree/TypeToken approach.auth0_flutter/example/ios/Runner/PasskeyAuthenticator.swift (1)
117-139: ⚡ Quick winNil-coalescing to empty
Data()may mask missing assertion fields.If
rawAuthenticatorDataorsignatureisnil, substituting emptyData()will produce an invalid WebAuthn assertion that fails server-side validation with a confusing error. Consider returning aFlutterErrorfor these unexpected nil cases instead.Proposed approach
if let credential = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialAssertion { // Login assertion. + guard let rawAuthenticatorData = credential.rawAuthenticatorData, + let signature = credential.signature else { + result(FlutterError(code: "invalid_assertion", + message: "Missing authenticatorData or signature", + details: nil)) + cleanup() + return + } var response: [String: Any] = [ "clientDataJSON": Self.encodeBase64URL(credential.rawClientDataJSON), - "authenticatorData": - Self.encodeBase64URL(credential.rawAuthenticatorData ?? Data()), - "signature": Self.encodeBase64URL(credential.signature ?? Data()) + "authenticatorData": Self.encodeBase64URL(rawAuthenticatorData), + "signature": Self.encodeBase64URL(signature) ]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/example/ios/Runner/PasskeyAuthenticator.swift` around lines 117 - 139, The code in PasskeyAuthenticator handling ASAuthorizationPlatformPublicKeyCredentialAssertion currently nil-coalesces rawAuthenticatorData and signature to Data(), which can produce invalid assertions; instead validate that credential.rawAuthenticatorData and credential.signature are non-nil (and credential.credentialID is present), and if any are missing call result with a FlutterError (provide a clear code/message and include which field is missing), then call cleanup() and return; only call Self.encodeBase64URL on non-nil values and proceed to build the response when all required fields are present.auth0_flutter_platform_interface/lib/src/auth/passkey_credential.dart (1)
101-108: 💤 Low valueConsider conditionally including nullable fields for consistency.
PasskeyCredential.toMap()includesauthenticatorAttachmentandclientExtensionResultseven when null, whilePasskeyAuthenticatorResponse.toMap()uses conditional inclusion (if (field != null)). While both approaches work for JSON serialization, conditional inclusion produces cleaner output and is more consistent.♻️ Use conditional inclusion for consistency
Map<String, dynamic> toMap() => { 'id': id, 'rawId': rawId, 'type': type, - 'authenticatorAttachment': authenticatorAttachment, + if (authenticatorAttachment != null) 'authenticatorAttachment': authenticatorAttachment, 'response': response.toMap(), - 'clientExtensionResults': clientExtensionResults, + if (clientExtensionResults != null) 'clientExtensionResults': clientExtensionResults, };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter_platform_interface/lib/src/auth/passkey_credential.dart` around lines 101 - 108, PasskeyCredential.toMap() currently always emits authenticatorAttachment and clientExtensionResults even when null; update PasskeyCredential.toMap() to mirror PasskeyAuthenticatorResponse.toMap() by conditionally including 'authenticatorAttachment' and 'clientExtensionResults' only when they are non-null (use inline if checks) so the serialized map is cleaner and consistent with PasskeyAuthenticatorResponse.toMap().auth0_flutter/example/lib/example_app.dart (1)
81-81: 💤 Low valueUse raw string literal for regex pattern.
RegExp('.')should beRegExp(r'.')to properly escape the dot metacharacter. While both work in this masking context (since you want to match any character), the raw string literal is more idiomatic and avoids potential confusion.💡 Use raw string literal
- output = result.idToken.replaceAll(RegExp('.'), '*'); + output = result.idToken.replaceAll(RegExp(r'.'), '*');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/example/lib/example_app.dart` at line 81, The regex pattern used to mask the token uses RegExp('.') which is not an idiomatic raw string; update the call that constructs the masked output (the line assigning to output using result.idToken.replaceAll(RegExp('.'), '*')) to use a raw string literal for the regex, i.e., RegExp(r'.'), so the dot metacharacter is expressed clearly and unambiguously when creating the masked output from result.idToken.auth0_flutter/example/lib/passkey_authenticator.dart (1)
44-45: ⚡ Quick winAdd error handling for platform response structure validation.
The
Map.from()casts assume the platform always returns a well-formed response structure. If the native code returns unexpected data (e.g.,responseis not a Map), this will throw a crypticTypeErrorinstead of a clear error message.🛡️ Add validation or wrap in try-catch
+ if (result['response'] is! Map) { + throw PlatformException( + code: 'invalid_response', + message: 'Platform returned malformed credential response.', + ); + } + final response = Map<String, dynamic>.from( result['response'] as Map<dynamic, dynamic>);Or wrap the entire credential construction in a try-catch to provide a clearer error:
+ try { final response = Map<String, dynamic>.from( result['response'] as Map<dynamic, dynamic>); return PasskeyCredential( id: result['id'] as String, ... ); + } catch (e) { + throw PlatformException( + code: 'invalid_response', + message: 'Failed to parse platform credential response: $e', + ); + }Also applies to: 88-89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/example/lib/passkey_authenticator.dart` around lines 44 - 45, The code assumes result['response'] is a Map and directly calls Map<String, dynamic>.from(...), which will throw a cryptic TypeError for unexpected platform responses; change the logic in the credential construction (the block that assigns response from result['response'] and the similar code around lines 88-89) to first validate the type (e.g., check result['response'] is Map or Map<dynamic,dynamic>) and then convert, or wrap the entire construction in a try-catch that catches TypeError/Exception and throws a clear PlatformException/FormatException with a descriptive message including the unexpected value; ensure the thrown error identifies the offending key ("response") and includes the original value for easier debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.kt`:
- Around line 19-22: The call to requestHandler.handle uses activity!! which can
be null and crash; update Auth0FlutterWebAuthMethodCallHandler so before
creating the MethodCallRequest and calling requestHandler.handle(activity!!,
request, result) you check activity for null (e.g., if (activity == null) {
result.error("ACTIVITY_UNAVAILABLE", "Activity is null; plugin detached", null);
return }). Ensure you still check requestHandler != null and only call
MethodCallRequest.fromCall and requestHandler.handle(activity, request, result)
when activity is non-null, referencing the requestHandler,
MethodCallRequest.fromCall, handle, activity and result symbols to locate the
affected code.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt`:
- Around line 37-40: The code currently does unsafe casts in
PasskeyCredentialExchangeApiRequestHandler (assertHasProperties, authSession,
credentialMap); replace those casts with safe type checks: verify
args["challenge"] is a Map<*, *> and that its "authSession" entry is a String,
and verify args["credential"] is a Map<*, *> before extracting values; on
mismatch throw or return a clear IllegalArgumentException (or call the existing
error handling path) with a message referencing "challenge" or "credential" so
the Flutter caller receives a useful error instead of a ClassCastException.
In
`@auth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swift`:
- Around line 56-64: The Swift handler currently returns a minimal
authParamsPublicKey map (keys: challenge, rpId, userId, userName) which breaks
the cross-platform contract; update the PasskeySignupChallengeMethodHandler to
return the full authParamsPublicKey object exactly as Android does by
serializing challenge.authParamsPublicKey (or the full
PasskeyRegistrationChallenge) into a dictionary including nested fields like
user (id, name, displayName), relyingParty, authenticatorSelection,
pubKeyCredParams, timeout, etc., using the Auth0.swift model properties or
JSONEncoder-to-dictionary conversion so the resulting response dictionary
contains authSession and an authParamsPublicKey subtree matching Android's
shape.
In `@auth0_flutter/example/ios/Runner.xcodeproj/project.pbxproj`:
- Line 1115: The Debug build currently sets CODE_SIGN_STYLE = Manual without
setting DEVELOPMENT_TEAM or PROVISIONING_PROFILE_SPECIFIER, which prevents
device signing; either change the Debug configuration's CODE_SIGN_STYLE to
Automatic (so Xcode manages signing) or retain Manual but add placeholder values
and document that developers must set DEVELOPMENT_TEAM and
PROVISIONING_PROFILE_SPECIFIER locally; update the project.pbxproj Debug block
(look for CODE_SIGN_STYLE, DEVELOPMENT_TEAM, PROVISIONING_PROFILE_SPECIFIER
entries) and apply the chosen approach consistently (also mirror the same change
at the other occurrence noted).
In `@auth0_flutter/example/lib/example_app.dart`:
- Line 256: The dotenv.env['AUTH0_PASSKEY_CONNECTION'] value is nullable but is
passed directly to the connection parameter (at the occurrences around lines
256, 283, 356), so add an explicit null-check and fail-fast or provide a default
before calling the API: replace direct uses of
dotenv.env['AUTH0_PASSKEY_CONNECTION'] with a validated value (e.g., assign to a
local variable passkeyConnection = dotenv.env['AUTH0_PASSKEY_CONNECTION'] ??
throw StateError("AUTH0_PASSKEY_CONNECTION not set") or use a sensible default)
and then pass passkeyConnection into the connection parameter; update all three
occurrences (the calls that set connection:
dotenv.env['AUTH0_PASSKEY_CONNECTION']) to use the validated variable.
---
Outside diff comments:
In `@auth0_flutter/example/ios/Runner.xcodeproj/project.pbxproj`:
- Line 127: The project.pbxproj contains a duplicate PBXFileReference entry for
CredentialsManagerSSOCredentialsMethodHandlerTests.swift (GUID
A09B2E3F1D4C7891BA000001) which will break Xcode; remove the redundant
PBXFileReference block so only one entry with that GUID and path remains, and
then verify any references in PBXGroup or PBXSourcesBuildPhase (or similar
sections) point to the remaining GUID to avoid dangling or duplicated
references.
- Line 29: The project file contains a duplicate PBXBuildFile entry for
CredentialsManagerSSOCredentialsMethodHandlerTests.swift (object ID
A09B2E3F1D4C7891BA000002 with fileRef A09B2E3F1D4C7891BA000001) which must be
removed; locate the duplicate PBXBuildFile block (the second occurrence around
the other PBXBuildFile entries) and delete it so there is only one PBXBuildFile
entry for that source file, then scan build phases, PBXGroup and any other
references to ensure they point to the remaining object ID (or update them to
the kept ID) to avoid dangling or duplicate identifiers.
---
Nitpick comments:
In `@auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart`:
- Around line 28-31: The toMap() serializer is returning the internal
authParamsPublicKey map by reference, exposing mutable state; update toMap() so
it returns a defensive copy of authParamsPublicKey (e.g., create a new Map from
authParamsPublicKey) instead of the original reference while still returning
authSession as before, ensuring callers cannot mutate the model's internal map
via the serialized output.
In `@auth0_flutter_platform_interface/lib/src/auth/passkey_credential.dart`:
- Around line 101-108: PasskeyCredential.toMap() currently always emits
authenticatorAttachment and clientExtensionResults even when null; update
PasskeyCredential.toMap() to mirror PasskeyAuthenticatorResponse.toMap() by
conditionally including 'authenticatorAttachment' and 'clientExtensionResults'
only when they are non-null (use inline if checks) so the serialized map is
cleaner and consistent with PasskeyAuthenticatorResponse.toMap().
In
`@auth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dart`:
- Around line 1383-1661: The tests for the new passkey channels currently only
cover PlatformException paths but are missing the null-return failure-paths; add
unit tests that mock mocked.methodCallHandler to return null and assert that
MethodChannelAuth0FlutterAuth().passkeyLoginChallenge,
.passkeyCredentialExchange, and .passkeySignupChallenge each throw an
ApiException when the platform channel result is null, mirroring the existing
null-result tests for other API groups and using verify(captureAny) where
appropriate to locate the invoked method.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.kt`:
- Around line 55-58: The double-serialization in
PasskeySignupChallengeApiRequestHandler (creating authParamsPublicKey by
Gson().toJson(...) then fromJson(..., Map::class.java)) is inefficient; replace
it with a direct conversion such as using
Gson().toJsonTree(challenge.authParamsPublicKey).asJsonObject (or
Gson().fromJson with a TypeToken<Map<String,Any>>() to get a Map) to avoid the
intermediate JSON string — update the authParamsPublicKey assignment in that
handler to use the direct JsonTree/TypeToken approach.
In `@auth0_flutter/example/ios/Runner/PasskeyAuthenticator.swift`:
- Around line 117-139: The code in PasskeyAuthenticator handling
ASAuthorizationPlatformPublicKeyCredentialAssertion currently nil-coalesces
rawAuthenticatorData and signature to Data(), which can produce invalid
assertions; instead validate that credential.rawAuthenticatorData and
credential.signature are non-nil (and credential.credentialID is present), and
if any are missing call result with a FlutterError (provide a clear code/message
and include which field is missing), then call cleanup() and return; only call
Self.encodeBase64URL on non-nil values and proceed to build the response when
all required fields are present.
In `@auth0_flutter/example/lib/example_app.dart`:
- Line 81: The regex pattern used to mask the token uses RegExp('.') which is
not an idiomatic raw string; update the call that constructs the masked output
(the line assigning to output using result.idToken.replaceAll(RegExp('.'), '*'))
to use a raw string literal for the regex, i.e., RegExp(r'.'), so the dot
metacharacter is expressed clearly and unambiguously when creating the masked
output from result.idToken.
In `@auth0_flutter/example/lib/passkey_authenticator.dart`:
- Around line 44-45: The code assumes result['response'] is a Map and directly
calls Map<String, dynamic>.from(...), which will throw a cryptic TypeError for
unexpected platform responses; change the logic in the credential construction
(the block that assigns response from result['response'] and the similar code
around lines 88-89) to first validate the type (e.g., check result['response']
is Map or Map<dynamic,dynamic>) and then convert, or wrap the entire
construction in a try-catch that catches TypeError/Exception and throws a clear
PlatformException/FormatException with a descriptive message including the
unexpected value; ensure the thrown error identifies the offending key
("response") and includes the original value for easier debugging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b084c441-c964-4dc6-be32-43049e8d9539
📒 Files selected for processing (65)
.github/workflows/main.ymlauth0_flutter/EXAMPLES.mdauth0_flutter/README.mdauth0_flutter/android/build.gradleauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterAuthMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyLoginChallengeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyLoginChallengeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.ktauth0_flutter/darwin/.gitignoreauth0_flutter/darwin/Classes/AuthAPI/AuthAPIHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/MethodHandler.swiftauth0_flutter/darwin/auth0_flutter.podspecauth0_flutter/example/android/app/build.gradleauth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.ktauth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/PasskeyAuthenticator.ktauth0_flutter/example/android/gradle.propertiesauth0_flutter/example/android/settings.gradleauth0_flutter/example/ios/Runner.xcodeproj/project.pbxprojauth0_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcschemeauth0_flutter/example/ios/Runner/AppDelegate.swiftauth0_flutter/example/ios/Runner/PasskeyAuthenticator.swiftauth0_flutter/example/ios/Tests/AuthAPI/AuthAPIHandlerTests.swiftauth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandlerTests.swiftauth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyExtensionsTests.swiftauth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandlerTests.swiftauth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandlerTests.swiftauth0_flutter/example/ios/Tests/AuthAPI/AuthAPISpies.swiftauth0_flutter/example/lib/example_app.dartauth0_flutter/example/lib/passkey_authenticator.dartauth0_flutter/ios/.gitignoreauth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftauth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftauth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftauth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftauth0_flutter/ios/auth0_flutter.podspecauth0_flutter/lib/auth0_flutter.dartauth0_flutter/lib/src/mobile/authentication_api.dartauth0_flutter/macos/.gitignoreauth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftauth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftauth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftauth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftauth0_flutter/macos/auth0_flutter.podspecauth0_flutter/test/mobile/authentication_api_test.dartauth0_flutter/test/mobile/authentication_api_test.mocks.dartauth0_flutter_platform_interface/lib/auth0_flutter_platform_interface.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_exchange_options.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_login_challenge_options.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_signup_challenge_options.dartauth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dartauth0_flutter_platform_interface/lib/src/auth/passkey_credential.dartauth0_flutter_platform_interface/lib/src/auth0_flutter_auth_platform.dartauth0_flutter_platform_interface/lib/src/method_channel_auth0_flutter_auth.dartauth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dartauth0_flutter_platform_interface/test/passkey_credential_test.dart
8ed5dbf to
14de0ce
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (3)
auth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swift (1)
56-64:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftCross-platform contract: authParamsPublicKey structure mismatch persists.
The Swift implementation returns a minimal
authParamsPublicKeywith 4 fields (challenge, rpId, userId, userName), while the Android signup handler serializes the entirechallenge.authParamsPublicKeyobject via Gson (including nested structures likeuser,authenticatorSelection,pubKeyCredParams, etc.). This inconsistency was flagged in previous review and remains unresolved.If Dart/Flutter code accesses nested fields present in Android's response, it will fail on iOS/macOS.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swift` around lines 56 - 64, The iOS/macOS handler AuthAPIPasskeySignupChallengeMethodHandler currently builds authParamsPublicKey manually with only four flat fields, causing a mismatch with Android's serialized challenge.authParamsPublicKey; update the response to serialize and return the full challenge.authParamsPublicKey structure (including nested keys like user, authenticatorSelection, pubKeyCredParams, etc.) instead of the minimal map — locate where authParamsPublicKey is constructed in the handler and replace the manual dictionary with a serialization of challenge.authParamsPublicKey (or build an equivalent nested dictionary matching Android’s shape) so Dart/Flutter consumers receive the same schema.auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.kt (1)
22-22:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winNull-pointer crash risk:
activity!!without guard.Line 22 force-unwraps
activity, which is nullable and set tonullduring detachment. If a method call arrives while the plugin is detached (e.g., during configuration changes), this will throwKotlinNullPointerException.Add a null check and return
result.error(...)instead of crashing. As per coding guidelines, force-unwraps are not acceptable in MethodChannel handlers.🛡️ Proposed fix
override fun onMethodCall(`@NonNull` call: MethodCall, `@NonNull` result: Result) { val requestHandler = requestHandlers.find { it.method == call.method } if (requestHandler != null) { + val activity = this.activity + if (activity == null) { + result.error( + "no_activity", + "Web authentication requires an active Activity context", + null + ) + return + } val request = MethodCallRequest.fromCall(call) - requestHandler.handle(activity!!, request, result) + requestHandler.handle(activity, request, result) } else { result.notImplemented() } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.kt` at line 22, The handler currently force-unwraps the plugin Activity with activity!! in Auth0FlutterWebAuthMethodCallHandler when calling requestHandler.handle, which can throw a KotlinNullPointerException if the plugin is detached; change this to guard for null by checking activity (e.g., if (activity == null) ...) and immediately call result.error with a clear error code/message indicating the plugin is detached (do not throw), otherwise pass the non-null activity to requestHandler.handle; update the MethodChannel handler logic in Auth0FlutterWebAuthMethodCallHandler to use this null-check pattern instead of the force-unwrap.Source: Coding guidelines
auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt (1)
39-40:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winUnsafe casts will cause
ClassCastExceptioncrashes.Lines 39-40 perform unsafe casts without type validation. If the Flutter side passes incorrect types (e.g.,
challengeas a String, orcredentialas a primitive), these will throwClassCastExceptionat runtime instead of returning a clear error to Flutter.This is inconsistent with lines 45 and 47, which correctly use safe casts (
as?). As per coding guidelines, avoid force-casts in MethodChannel handlers —ClassCastExceptionfrom unsafe casts has caused crashes in the past.🛡️ Proposed fix with safe casts
assertHasProperties(listOf("challenge.authSession", "credential"), args) - val authSession = (args["challenge"] as Map<*, *>)["authSession"] as String - val credentialMap = args["credential"] as Map<*, *> + val challengeMap = args["challenge"] as? Map<*, *> + ?: run { + result.error("invalid_argument", "Property 'challenge' must be a Map", null) + return + } + val authSession = challengeMap["authSession"] as? String + ?: run { + result.error("invalid_argument", "Property 'challenge.authSession' must be a String", null) + return + } + val credentialMap = args["credential"] as? Map<*, *> + ?: run { + result.error("invalid_argument", "Property 'credential' must be a Map", null) + return + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt` around lines 39 - 40, Replace the unsafe force-casts for authSession and credentialMap with safe casts and null checks: use safe cast operators (as?) when extracting (args["challenge"] as? Map<*, *>)?["authSession"] as? String and args["credential"] as? Map<*, *>, validate they are non-null, and if either is null return a clear error to the Flutter side instead of allowing a ClassCastException; update the PasskeyCredentialExchangeApiRequestHandler (the variables authSession and credentialMap and any downstream usages) to use the safely-cast values or early-return an error.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart`:
- Around line 21-26: PasskeyChallenge.fromMap currently does unchecked `as`
casts for authSession and authParamsPublicKey which can throw on malformed
input; update this factory to validate types first (use `is String` for
result['authSession'] and `is Map` for result['authParamsPublicKey']),
convert/cast safely (e.g., Map<String,dynamic>.from only after confirming a
Map), and throw a clear ArgumentError or return a well-documented fallback if
validation fails; locate the PasskeyChallenge.fromMap constructor and add these
guards around access to authSession and authParamsPublicKey.
In `@auth0_flutter_platform_interface/lib/src/auth/passkey_credential.dart`:
- Around line 32-40: PasskeyAuthenticatorResponse.fromMap and
PasskeyCredential.fromMap currently perform unchecked `as` casts from a dynamic
map which can raise runtime TypeError if fields are missing or wrong-typed;
update both factory methods to validate types using `is` checks (e.g., ensure
map['clientDataJSON'] is String, map['id']/'rawId' is String, map['response'] is
Map or expected shape) and validate optional fields with `== null || value is
String` (or appropriate types) before casting, and throw a clear ArgumentError
with a descriptive message when required fields are absent or of the wrong type
so callers get explicit errors instead of uncaught TypeError.
In
`@auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.kt`:
- Around line 103-109: Replace the JVM assert(...) and unsafe casts in
PasskeySignupChallengeApiRequestHandlerTest with Hamcrest assertions and safe
nullable casts: capture the success map via the existing captor and use
assertThat(resultMap["authSession"], equalTo("test-auth-session")); cast
authParamsPublicKey with as? to a Map and assertThat(publicKey, notNullValue())
before checking its "challenge" with assertThat(publicKey?.get("challenge"),
equalTo("test-challenge")); similarly cast the "user" entry with as? Map,
assertThat(user, notNullValue()), then assertThat(user?.get("name"),
equalTo("test-user-name")). Ensure you reference the existing captor, resultMap,
publicKey and user variables and Hamcrest matchers already used in the repo.
In
`@auth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swift`:
- Around line 15-22: Replaced implicitly unwrapped optionals in
ReconstructedLoginPasskey (userID, rawAuthenticatorData, signature) with regular
optionals (Data?) and remove force-unwraps in the MethodChannel handler; update
the reconstruction logic (the function that creates ReconstructedLoginPasskey
around lines 148-180) to explicitly unwrap those optionals with guard let (or
return a clear error) before using them so no `!` is used in the handler flow
and the handler returns nil or a descriptive error if any required field is
missing.
In `@auth0_flutter/test/mobile/authentication_api_test.dart`:
- Line 451: The long inline casts like "verify(...).captured.single as
ApiRequest<AuthPasskeyLoginChallengeOptions>" exceed 80 chars; fix by extracting
the complex generic type into a shorter typedef (e.g., alias
ApiRequest<AuthPasskeyLoginChallengeOptions> to a short name) or by splitting
the expression across lines (assign verify(...).captured.single to a local var
and then cast on the next line) for each occurrence (the capture/cast lines at
or near verify(...).captured.single as
ApiRequest<AuthPasskeyLoginChallengeOptions> and the similar casts for other
AuthPasskey*Options in the test file) so each line stays ≤80 chars while keeping
the same runtime behavior.
---
Duplicate comments:
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.kt`:
- Line 22: The handler currently force-unwraps the plugin Activity with
activity!! in Auth0FlutterWebAuthMethodCallHandler when calling
requestHandler.handle, which can throw a KotlinNullPointerException if the
plugin is detached; change this to guard for null by checking activity (e.g., if
(activity == null) ...) and immediately call result.error with a clear error
code/message indicating the plugin is detached (do not throw), otherwise pass
the non-null activity to requestHandler.handle; update the MethodChannel handler
logic in Auth0FlutterWebAuthMethodCallHandler to use this null-check pattern
instead of the force-unwrap.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt`:
- Around line 39-40: Replace the unsafe force-casts for authSession and
credentialMap with safe casts and null checks: use safe cast operators (as?)
when extracting (args["challenge"] as? Map<*, *>)?["authSession"] as? String and
args["credential"] as? Map<*, *>, validate they are non-null, and if either is
null return a clear error to the Flutter side instead of allowing a
ClassCastException; update the PasskeyCredentialExchangeApiRequestHandler (the
variables authSession and credentialMap and any downstream usages) to use the
safely-cast values or early-return an error.
In
`@auth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swift`:
- Around line 56-64: The iOS/macOS handler
AuthAPIPasskeySignupChallengeMethodHandler currently builds authParamsPublicKey
manually with only four flat fields, causing a mismatch with Android's
serialized challenge.authParamsPublicKey; update the response to serialize and
return the full challenge.authParamsPublicKey structure (including nested keys
like user, authenticatorSelection, pubKeyCredParams, etc.) instead of the
minimal map — locate where authParamsPublicKey is constructed in the handler and
replace the manual dictionary with a serialization of
challenge.authParamsPublicKey (or build an equivalent nested dictionary matching
Android’s shape) so Dart/Flutter consumers receive the same schema.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 35bf48bb-d755-459b-8fde-7a60782d6644
⛔ Files ignored due to path filters (26)
auth0_flutter/example/android/app/build.gradleis excluded by!**/example/**auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.ktis excluded by!**/example/**auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/PasskeyAuthenticator.ktis excluded by!**/example/**auth0_flutter/example/android/gradle.propertiesis excluded by!**/example/**auth0_flutter/example/android/settings.gradleis excluded by!**/example/**auth0_flutter/example/ios/Runner.xcodeproj/project.pbxprojis excluded by!**/example/**auth0_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcschemeis excluded by!**/example/**auth0_flutter/example/ios/Runner/AppDelegate.swiftis excluded by!**/example/**auth0_flutter/example/ios/Runner/PasskeyAuthenticator.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyExtensionsTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPISpies.swiftis excluded by!**/example/**auth0_flutter/example/lib/example_app.dartis excluded by!**/example/**auth0_flutter/example/lib/passkey_authenticator.dartis excluded by!**/example/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/test/mobile/authentication_api_test.mocks.dartis excluded by!**/*.mocks.dart
📒 Files selected for processing (39)
.github/workflows/main.ymlauth0_flutter/EXAMPLES.mdauth0_flutter/README.mdauth0_flutter/android/build.gradleauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterAuthMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyLoginChallengeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyLoginChallengeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.ktauth0_flutter/darwin/.gitignoreauth0_flutter/darwin/Classes/AuthAPI/AuthAPIHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/MethodHandler.swiftauth0_flutter/darwin/auth0_flutter.podspecauth0_flutter/ios/.gitignoreauth0_flutter/ios/auth0_flutter.podspecauth0_flutter/lib/auth0_flutter.dartauth0_flutter/lib/src/mobile/authentication_api.dartauth0_flutter/macos/.gitignoreauth0_flutter/macos/auth0_flutter.podspecauth0_flutter/test/mobile/authentication_api_test.dartauth0_flutter_platform_interface/lib/auth0_flutter_platform_interface.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_exchange_options.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_login_challenge_options.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_signup_challenge_options.dartauth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dartauth0_flutter_platform_interface/lib/src/auth/passkey_credential.dartauth0_flutter_platform_interface/lib/src/auth0_flutter_auth_platform.dartauth0_flutter_platform_interface/lib/src/method_channel_auth0_flutter_auth.dartauth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dartauth0_flutter_platform_interface/test/passkey_credential_test.dart
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.kt (1)
17-67: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoffPotential confusion between extension functions.
toMyAccountMethodMap()(line 17) andtoMyAccountPasskeyMethodMap()(line 50) are separate extension functions on different types. The existingtoMyAccountMethodMap()has no branch for passkey types, which could cause confusion if callers aren't aware they need to use the passkey-specific extension.Document the intended usage pattern or consider whether these extensions should be unified if the types share a common base.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.kt` around lines 17 - 67, The two extension functions toMyAccountMethodMap (on AuthenticationMethod) and toMyAccountPasskeyMethodMap (on PasskeyAuthenticationMethod) can be confusing because callers might expect passkeys to be handled by toMyAccountMethodMap; update toMyAccountMethodMap to include a branch for PasskeyAuthenticationMethod that delegates to toMyAccountPasskeyMethodMap (or consolidate both into a single extension if PasskeyAuthenticationMethod shares a common base), and add a short comment documenting the intended usage; reference the functions toMyAccountMethodMap, toMyAccountPasskeyMethodMap and the type PasskeyAuthenticationMethod when making the change.auth0_flutter_platform_interface/test/method_channel_auth0_flutter_my_account_test.dart (1)
76-313:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMultiple line length violations exceed 80 characters.
Lines 222, 246, and several others violate the Dart guideline. Break long parameter lists and test descriptions across multiple lines.
Example violations:
- Line 222: Long parameter list in
MyAccountConfirmEnrollmentOptions- Line 246: Test description exceeds limit
- Line 263: Long parameter list in
MyAccountEnrollPasskeyChallengeOptions🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter_platform_interface/test/method_channel_auth0_flutter_my_account_test.dart` around lines 76 - 313, Several tests contain lines longer than 80 characters; split long test descriptions and multi-argument constructors across multiple lines (use one argument per line and add trailing commas to enable formatter) so they conform to line-length rules. Specifically update the long parameter lists in MyAccountConfirmEnrollmentOptions and MyAccountEnrollPasskeyChallengeOptions within the confirmEnrollment and enrollPasskeyChallenge tests, and break long test description strings (e.g., the test at the long description near enrollPasskey) into shorter strings or wrap the test call across lines; apply the same wrapping pattern to other offending tests (updateAuthenticationMethod, enrollPasskey) and run dart format to ensure all lines are under 80 chars.Source: Coding guidelines
♻️ Duplicate comments (1)
auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt (1)
37-40:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winUse safe casts to prevent ClassCastException.
Lines 39-40 perform unsafe casts that will throw
ClassCastExceptionif Flutter passes incorrect types. Per coding guidelines, ClassCastException from unsafe casts in MethodChannel handlers has caused crashes in the past and must be treated as a bug.Use safe casts with explicit validation:
🛡️ Proposed fix with safe casts
assertHasProperties(listOf("challenge.authSession", "credential"), args) - val authSession = (args["challenge"] as Map<*, *>)["authSession"] as String - val credentialMap = args["credential"] as Map<*, *> + val challengeMap = args["challenge"] as? Map<*, *> + ?: throw IllegalArgumentException("Property 'challenge' must be a Map") + val authSession = challengeMap["authSession"] as? String + ?: throw IllegalArgumentException("Property 'challenge.authSession' must be a String") + val credentialMap = args["credential"] as? Map<*, *> + ?: throw IllegalArgumentException("Property 'credential' must be a Map")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt` around lines 37 - 40, The code currently does unsafe casts for authSession and credentialMap which can throw ClassCastException; replace them with safe casts and explicit null/type checks: retrieve challenge as (args["challenge"] as? Map<*, *>) and then authSession as (challenge["authSession"] as? String) and credentialMap as (args["credential"] as? Map<*, *>), validate that these are non-null and of the expected types, and if validation fails throw or return a clear argument error (consistent with other MethodChannel handlers) rather than allowing a ClassCastException; update the code in PasskeyCredentialExchangeApiRequestHandler (where assertHasProperties, authSession and credentialMap are used) accordingly.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@auth0_flutter_platform_interface/lib/src/myaccount/my_account_enroll_passkey_challenge_options.dart`:
- Around line 8-12: The constructor parameters of
MyAccountEnrollPasskeyChallengeOptions should be marked final: update the class
fields and the MyAccountEnrollPasskeyChallengeOptions constructor to declare
accessToken, userIdentityId, and connection as final (replace any non-final
declarations with final) so the properties are immutable and follow Dart
guidelines; ensure the class members corresponding to these constructor
parameters are final as well.
In
`@auth0_flutter_platform_interface/lib/src/myaccount/my_account_passkey_authentication_method.dart`:
- Around line 85-87: The createdAt parsing currently uses
result['created_at'].toString() which hides type issues; update the code in
my_account_passkey_authentication_method (the createdAt assignment) to cast
explicitly and safely: check if result['created_at'] is a String (or int/num if
you expect epoch) and then call DateTime.parse on that String (or
DateTime.fromMillisecondsSinceEpoch for epoch), otherwise keep null or handle
the type mismatch explicitly so a non-String value fails visibly instead of
being silently stringified.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.kt`:
- Around line 72-75: The code is doing an unnecessary and fragile
double-serialization when building authParamsPublicKey: replace the
Gson().toJson(...)/fromJson(...) round-trip by directly converting or casting
the SDK value to a typed map (e.g. attempt a safe cast like authParamsPublicKey
as? Map<String, Any> or, if it is a JSON element, use
Gson().fromJson(jsonElement, object : TypeToken<Map<String,Any>>() {}.type) to
preserve types), and change the target type from Map<*, *> to a more specific
Map<String, Any> (or an appropriate concrete type) so callers and Kotlin's type
system get safer types; update usages of authParamsPublicKey accordingly and add
a short comment describing why a cast/typed conversion is used if the SDK
already returns a typed object.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyRequestHandler.kt`:
- Around line 108-109: In EnrollPasskeyRequestHandler where PublicKeyCredentials
is constructed, replace the unsafe casts (credentialMap["id"] as String,
credentialMap["rawId"] as String, response["attestationObject"] as String,
response["clientDataJSON"] as String) with safe casts (e.g., using the Kotlin
safe cast operator as? String) and if any required field is null or the wrong
type, return null (or propagate a clear error) from the enclosing method;
validate each value (id, rawId, attestationObject, clientDataJSON) after safe
casting before creating the PublicKeyCredentials instance so you avoid
ClassCastException on bad input.
- Line 95: Replace the unsafe cast "val response = credentialMap[\"response\"]
as Map<*, *>" in the reconstructCredentials function with a safe cast (as?) and
handle the null case by returning null; specifically, change how you read from
credentialMap (variable response) to use "as? Map<*, *>" and if that yields
null, return null from reconstructCredentials (the caller already checks for
null), avoiding throwing a ClassCastException and providing explicit error
handling.
- Around line 47-48: Replace the unsafe casts in EnrollPasskeyRequestHandler
(the lines obtaining request.data["challenge"] and request.data["credential"])
with safe casts (use as? Map<*, *>) and explicit null/type checks: verify each
cast result is non-null and contains expected key types before proceeding, and
handle failures by returning an appropriate error via the MethodChannel
request/result path (or early-return) instead of allowing a ClassCastException
to propagate; update any downstream code that assumes non-null maps to use the
validated variables.
- Around line 86-87: In EnrollPasskeyRequestHandler, replace the unsafe casts
challengeMap["authenticationMethodId"] as String and challengeMap["authSession"]
as String with safe casts (as?) and explicit null/type checks; if either cast
yields null, return or throw a clear error (e.g., invoke the existing
result/error callback) rather than letting a ClassCastException propagate.
Locate the code in the handler method reading challengeMap, change to safe
casts, validate the values' presence and types, and provide a descriptive error
path when validation fails.
---
Outside diff comments:
In
`@auth0_flutter_platform_interface/test/method_channel_auth0_flutter_my_account_test.dart`:
- Around line 76-313: Several tests contain lines longer than 80 characters;
split long test descriptions and multi-argument constructors across multiple
lines (use one argument per line and add trailing commas to enable formatter) so
they conform to line-length rules. Specifically update the long parameter lists
in MyAccountConfirmEnrollmentOptions and MyAccountEnrollPasskeyChallengeOptions
within the confirmEnrollment and enrollPasskeyChallenge tests, and break long
test description strings (e.g., the test at the long description near
enrollPasskey) into shorter strings or wrap the test call across lines; apply
the same wrapping pattern to other offending tests (updateAuthenticationMethod,
enrollPasskey) and run dart format to ensure all lines are under 80 chars.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.kt`:
- Around line 17-67: The two extension functions toMyAccountMethodMap (on
AuthenticationMethod) and toMyAccountPasskeyMethodMap (on
PasskeyAuthenticationMethod) can be confusing because callers might expect
passkeys to be handled by toMyAccountMethodMap; update toMyAccountMethodMap to
include a branch for PasskeyAuthenticationMethod that delegates to
toMyAccountPasskeyMethodMap (or consolidate both into a single extension if
PasskeyAuthenticationMethod shares a common base), and add a short comment
documenting the intended usage; reference the functions toMyAccountMethodMap,
toMyAccountPasskeyMethodMap and the type PasskeyAuthenticationMethod when making
the change.
---
Duplicate comments:
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt`:
- Around line 37-40: The code currently does unsafe casts for authSession and
credentialMap which can throw ClassCastException; replace them with safe casts
and explicit null/type checks: retrieve challenge as (args["challenge"] as?
Map<*, *>) and then authSession as (challenge["authSession"] as? String) and
credentialMap as (args["credential"] as? Map<*, *>), validate that these are
non-null and of the expected types, and if validation fails throw or return a
clear argument error (consistent with other MethodChannel handlers) rather than
allowing a ClassCastException; update the code in
PasskeyCredentialExchangeApiRequestHandler (where assertHasProperties,
authSession and credentialMap are used) accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2e72c4c2-d786-4781-b5b0-e0f597f5d44f
⛔ Files ignored due to path filters (11)
auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.ktis excluded by!**/example/**auth0_flutter/example/ios/Runner.xcodeproj/project.pbxprojis excluded by!**/example/**auth0_flutter/example/ios/Tests/MyAccount/MyAccountEnrollPasskeyChallengeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/MyAccount/MyAccountEnrollPasskeyMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/MyAccount/MyAccountSpies.swiftis excluded by!**/example/**auth0_flutter/example/lib/example_app.dartis excluded by!**/example/**auth0_flutter/ios/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/macos/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/test/mobile/authentication_api_test.mocks.dartis excluded by!**/*.mocks.dart
📒 Files selected for processing (24)
auth0_flutter/EXAMPLES.mdauth0_flutter/README.mdauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyChallengeRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyRequestHandler.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyChallengeRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyRequestHandlerTest.ktauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountExtensions.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountHandler.swiftauth0_flutter/lib/auth0_flutter.dartauth0_flutter/lib/src/mobile/my_account_api.dartauth0_flutter_platform_interface/lib/auth0_flutter_platform_interface.dartauth0_flutter_platform_interface/lib/src/myaccount/auth0_flutter_my_account_platform.dartauth0_flutter_platform_interface/lib/src/myaccount/method_channel_auth0_flutter_my_account.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_enroll_passkey_challenge_options.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_enroll_passkey_options.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_passkey_authentication_method.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_passkey_enrollment_challenge.dartauth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dartauth0_flutter_platform_interface/test/method_channel_auth0_flutter_my_account_test.dart
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (4)
auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart (1)
21-26:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPast comment marked as addressed, but unsafe casts remain.
The previous review flagged unchecked
ascasts at lines 23 and 24-25, which can throw at runtime if the native platform returns malformed data. You confirmed this was addressed, but the current code still performs direct casts withoutischecks or guards. Per Dart coding guidelines, useischecks before casting.🛡️ Recommended fix with type guards
factory PasskeyChallenge.fromMap(final Map<dynamic, dynamic> result) { + final authSession = result['authSession']; + if (authSession is! String) { + throw ArgumentError('authSession must be a String'); + } + final authParamsPublicKey = result['authParamsPublicKey']; + if (authParamsPublicKey is! Map) { + throw ArgumentError('authParamsPublicKey must be a Map'); + } return PasskeyChallenge( - authSession: result['authSession'] as String, - authParamsPublicKey: Map<String, dynamic>.from( - result['authParamsPublicKey'] as Map<dynamic, dynamic>), + authSession: authSession, + authParamsPublicKey: Map<String, dynamic>.from(authParamsPublicKey), ); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart` around lines 21 - 26, PasskeyChallenge.fromMap uses unchecked casts for authSession and authParamsPublicKey that can throw; update the factory to validate types before casting: check result['authSession'] is String and result['authParamsPublicKey'] is Map (or Map<dynamic,dynamic>), only then convert authParamsPublicKey with Map<String, dynamic>.from, and otherwise throw an ArgumentError or return a clear failure. Locate and update the PasskeyChallenge.fromMap factory and the authSession/authParamsPublicKey extraction to use these type guards.Source: Coding guidelines
auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.kt (1)
56-58: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winReuse a single Gson instance instead of creating two per call.
Gson is thread-safe after construction. Creating two instances per invocation just to serialize then deserialize is inefficient. Define
private val gson = Gson()at class level and reuse it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.kt` around lines 56 - 58, PasskeySignupChallengeApiRequestHandler currently constructs two Gson instances when converting challenge.authParamsPublicKey; create a single reusable instance by adding a private val gson = Gson() at the class level and replace both occurrences of Gson() in the authParamsPublicKey conversion with this gson instance so the code uses gson.toJson(...) and gson.fromJson(..., Map::class.java) instead of creating new Gson objects per call.auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.kt (1)
106-109:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winReplace JVM
assert(...)and unsafe casts with Hamcrest assertions and safe casts.Lines 106-109 use Kotlin JVM
assert(...)(which can be disabled at runtime) and unsafeas Map<*, *>casts that can throwClassCastException. Switch to HamcrestassertThatwithequalToandnotNullValue, and use safeas?casts with null checks.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.kt` around lines 106 - 109, In PasskeySignupChallengeApiRequestHandlerTest replace JVM assert(...) and unsafe casts: use Hamcrest assertThat with equalTo/notNullValue instead of assert, and change unsafe casts like resultMap["authParamsPublicKey"] as Map<*, *> to safe casts (as?) with null checks; e.g. retrieve publicKey = resultMap["authParamsPublicKey"] as? Map<*, *> and assertThat(publicKey, notNullValue()), then assertThat(publicKey?.get("challenge"), equalTo("test-challenge")) and for the nested user use safe cast user = publicKey?.get("user") as? Map<*, *> and assertThat(user?.get("name"), equalTo("test-user-name")). Ensure references to resultMap, publicKey, user and the test class PasskeySignupChallengeApiRequestHandlerTest are updated accordingly.auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt (1)
39-41:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winUnsafe casts can throw
ClassCastExceptioninstead of clear error.Lines 39-41 perform unsafe casts (
as Map<*, *>,as String) without type validation. WhileassertHasPropertieschecks presence, it does not validate types. If Flutter passes incorrect types (e.g.,challengeas String), these casts throwClassCastExceptionat runtime instead of a clear error message. Per coding guidelines, this has caused crashes in the past and must be treated as a bug.Replace with safe casts and explicit error handling.
🛡️ Proposed fix with safe casts
assertHasProperties(listOf("challenge.authSession", "credential"), args) - val challenge = args["challenge"] as Map<*, *> - val authSession = challenge["authSession"] as String - val credentialMap = args["credential"] as Map<*, *> + val challenge = args["challenge"] as? Map<*, *> + ?: return result.error( + "INVALID_PARAMETERS", + "Property 'challenge' must be a Map", + null + ) + val authSession = challenge["authSession"] as? String + ?: return result.error( + "INVALID_PARAMETERS", + "Property 'challenge.authSession' must be a String", + null + ) + val credentialMap = args["credential"] as? Map<*, *> + ?: return result.error( + "INVALID_PARAMETERS", + "Property 'credential' must be a Map", + null + )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt` around lines 39 - 41, The code in PasskeyCredentialExchangeApiRequestHandler uses unsafe casts for challenge, authSession, and credentialMap which can throw ClassCastException; replace these with safe casts (using as?) and explicit null/type checks: safely cast args["challenge"] to Map<*, *> and args["credential"] to Map<*, *> and challenge["authSession"] to String, then if any are null or wrong type raise/return a clear IllegalArgumentException or a method-specific error with a descriptive message (e.g., "expected 'challenge' to be Map, got X" / "expected 'authSession' to be String") instead of letting a ClassCastException bubble up; keep existing assertHasProperties but add these runtime type checks before using the variables.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart`:
- Around line 21-26: Replace unsafe unchecked `as` casts in
PasskeyChallenge.fromMap, PasskeyAuthenticatorResponse.fromMap, and
PasskeyCredential.fromMap with explicit runtime type guards: check each required
field with `is` (e.g., verify `result['authSession'] is String`,
`result['clientDataJSON'] is String`, `result['id'] is String`, `result['rawId']
is String`, `result['response'] is Map`, etc.), and for optional fields check
for `null` or the expected type; when a check fails throw an ArgumentError with
a clear message naming the factory (e.g., PasskeyChallenge.fromMap) and the
missing/wrong-typed field; for Map conversions use `is Map` then cast via
Map<String, dynamic>.from after the guard to avoid TypeError at runtime.
In
`@auth0_flutter_platform_interface/lib/src/myaccount/my_account_passkey_enrollment_challenge.dart`:
- Around line 26-33: The PasskeyEnrollmentChallenge.fromMap factory currently
uses unchecked `as` casts that will throw if keys are missing or of the wrong
type; update the factory (PasskeyEnrollmentChallenge.fromMap) to validate that
result['authenticationMethodId'] and result['authSession'] are non-null Strings
(using `is String`) and that result['authParamsPublicKey'] is a Map before
calling Map<String, dynamic>.from, and if any check fails throw a clear
ArgumentError or return a sensible default; reference the fields
authenticationMethodId, authSession, and authParamsPublicKey when performing
these guards so invalid input is detected early and safely.
In `@auth0_flutter/android/build.gradle`:
- Line 77: The build is forcing gson 2.10.1 while com.auth0.android:auth0:3.18.0
brings gson 2.8.9 transitively; decide and make the override explicit: either
exclude Gson from Auth0 (use dependency exclusion on artifact
com.auth0.android:auth0) and keep implementation
'com.google.code.gson:gson:2.10.1', or remove the explicit 2.10.1 and align to
Auth0’s transitive version, and add a comment documenting the chosen resolved
gson version to avoid surprises; locate the gson declaration and the auth0
dependency in android/build.gradle (look for implementation
'com.google.code.gson:gson:2.10.1' and com.auth0.android:auth0:3.18.0) and apply
the exclusion or version alignment accordingly.
In `@auth0_flutter/lib/src/mobile/my_account_api.dart`:
- Around line 104-134: The doc comment for enrollPasskeyChallenge has lines
exceeding the 80-character limit; reflow the long sentences so each line is <=80
characters while preserving wording and sentence breaks. Update the comment
above the Future<PasskeyEnrollmentChallenge> enrollPasskeyChallenge(...) (the
block describing usage, parameters, and required scopes) so all lines wrap to
~80 chars, leaving the method signature and call to
Auth0FlutterMyAccountPlatform.instance.enrollPasskeyChallenge(_createApiRequest(MyAccountEnrollPasskeyChallengeOptions(accessToken:
_accessToken, userIdentityId: userIdentityId, connection: connection)))
unchanged.
---
Duplicate comments:
In `@auth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dart`:
- Around line 21-26: PasskeyChallenge.fromMap uses unchecked casts for
authSession and authParamsPublicKey that can throw; update the factory to
validate types before casting: check result['authSession'] is String and
result['authParamsPublicKey'] is Map (or Map<dynamic,dynamic>), only then
convert authParamsPublicKey with Map<String, dynamic>.from, and otherwise throw
an ArgumentError or return a clear failure. Locate and update the
PasskeyChallenge.fromMap factory and the authSession/authParamsPublicKey
extraction to use these type guards.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.kt`:
- Around line 39-41: The code in PasskeyCredentialExchangeApiRequestHandler uses
unsafe casts for challenge, authSession, and credentialMap which can throw
ClassCastException; replace these with safe casts (using as?) and explicit
null/type checks: safely cast args["challenge"] to Map<*, *> and
args["credential"] to Map<*, *> and challenge["authSession"] to String, then if
any are null or wrong type raise/return a clear IllegalArgumentException or a
method-specific error with a descriptive message (e.g., "expected 'challenge' to
be Map, got X" / "expected 'authSession' to be String") instead of letting a
ClassCastException bubble up; keep existing assertHasProperties but add these
runtime type checks before using the variables.
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.kt`:
- Around line 56-58: PasskeySignupChallengeApiRequestHandler currently
constructs two Gson instances when converting challenge.authParamsPublicKey;
create a single reusable instance by adding a private val gson = Gson() at the
class level and replace both occurrences of Gson() in the authParamsPublicKey
conversion with this gson instance so the code uses gson.toJson(...) and
gson.fromJson(..., Map::class.java) instead of creating new Gson objects per
call.
In
`@auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.kt`:
- Around line 106-109: In PasskeySignupChallengeApiRequestHandlerTest replace
JVM assert(...) and unsafe casts: use Hamcrest assertThat with
equalTo/notNullValue instead of assert, and change unsafe casts like
resultMap["authParamsPublicKey"] as Map<*, *> to safe casts (as?) with null
checks; e.g. retrieve publicKey = resultMap["authParamsPublicKey"] as? Map<*, *>
and assertThat(publicKey, notNullValue()), then
assertThat(publicKey?.get("challenge"), equalTo("test-challenge")) and for the
nested user use safe cast user = publicKey?.get("user") as? Map<*, *> and
assertThat(user?.get("name"), equalTo("test-user-name")). Ensure references to
resultMap, publicKey, user and the test class
PasskeySignupChallengeApiRequestHandlerTest are updated accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 8183a5f9-7b87-4cf1-a04d-d6966fdb6c25
⛔ Files ignored due to path filters (26)
auth0_flutter/example/android/app/build.gradleis excluded by!**/example/**auth0_flutter/example/android/app/src/main/kotlin/com/auth0/auth0_flutter_example/MainActivity.ktis excluded by!**/example/**auth0_flutter/example/ios/Runner.xcodeproj/project.pbxprojis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyExtensionsTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/AuthAPI/AuthAPISpies.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/MyAccount/MyAccountEnrollPasskeyChallengeMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/MyAccount/MyAccountEnrollPasskeyMethodHandlerTests.swiftis excluded by!**/example/**auth0_flutter/example/ios/Tests/MyAccount/MyAccountSpies.swiftis excluded by!**/example/**auth0_flutter/example/lib/example_app.dartis excluded by!**/example/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/ios/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftis excluded by!auth0_flutter/ios/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/macos/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftis excluded by!auth0_flutter/macos/Classes/**auth0_flutter/test/mobile/authentication_api_test.mocks.dartis excluded by!**/*.mocks.dart
📒 Files selected for processing (53)
.github/workflows/main.ymlauth0_flutter/EXAMPLES.mdauth0_flutter/README.mdauth0_flutter/android/build.gradleauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterAuthMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterPlugin.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterWebAuthMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/CredentialsManagerMethodCallHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyLoginChallengeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyChallengeRequestHandler.ktauth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyRequestHandler.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyCredentialExchangeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeyLoginChallengeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/PasskeySignupChallengeApiRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyChallengeRequestHandlerTest.ktauth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/my_account/EnrollPasskeyRequestHandlerTest.ktauth0_flutter/darwin/Classes/AuthAPI/AuthAPIHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyCredentialExchangeMethodHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyExtensions.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeyLoginChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/AuthAPI/AuthAPIPasskeySignupChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/MethodHandler.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountExtensions.swiftauth0_flutter/darwin/Classes/MyAccountAPI/MyAccountHandler.swiftauth0_flutter/darwin/auth0_flutter.podspecauth0_flutter/ios/auth0_flutter.podspecauth0_flutter/lib/auth0_flutter.dartauth0_flutter/lib/src/mobile/authentication_api.dartauth0_flutter/lib/src/mobile/my_account_api.dartauth0_flutter/macos/auth0_flutter.podspecauth0_flutter/test/mobile/authentication_api_test.dartauth0_flutter_platform_interface/lib/auth0_flutter_platform_interface.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_exchange_options.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_login_challenge_options.dartauth0_flutter_platform_interface/lib/src/auth/auth_passkey_signup_challenge_options.dartauth0_flutter_platform_interface/lib/src/auth/passkey_challenge.dartauth0_flutter_platform_interface/lib/src/auth/passkey_credential.dartauth0_flutter_platform_interface/lib/src/auth0_flutter_auth_platform.dartauth0_flutter_platform_interface/lib/src/method_channel_auth0_flutter_auth.dartauth0_flutter_platform_interface/lib/src/myaccount/auth0_flutter_my_account_platform.dartauth0_flutter_platform_interface/lib/src/myaccount/method_channel_auth0_flutter_my_account.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_enroll_passkey_challenge_options.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_enroll_passkey_options.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_passkey_authentication_method.dartauth0_flutter_platform_interface/lib/src/myaccount/my_account_passkey_enrollment_challenge.dartauth0_flutter_platform_interface/test/method_channel_auth0_flutter_auth_test.dartauth0_flutter_platform_interface/test/method_channel_auth0_flutter_my_account_test.dartauth0_flutter_platform_interface/test/passkey_credential_test.dart
The merge-base changed after approval.
1494e9b to
dbbd8fc
Compare
…n at root level of the class to avoid multiple reinstantiation
📋 Changes
PR adds Passkeys support to Flutter SDK
📎 References
https://auth0.com/docs/authenticate/database-connections/passkeys/native-passkeys-api
https://auth0.com/docs/api/myaccount/authentication-methods/create-authentication-method
🎯 Testing
Summary by CodeRabbit
Release Notes
New Features
Documentation