feat: Passkey enrollment support#857
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (3)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR implements passkey enrollment for the Auth0 Flutter SDK, adding a complete cross-platform flow for requesting enrollment challenges and submitting credentials. The feature spans Dart models, platform interface contracts, method-channel wiring, and native Android/iOS implementations with corresponding tests and documentation. ChangesMy Account Passkey Enrollment
Sequence DiagramsequenceDiagram
participant App as Client App
participant MyAccountApi as MyAccountApi
participant MethodChannel as Method Channel
participant AndroidHandler as Android Handler
participant iOSHandler as iOS Handler
participant SDK as Auth0 SDK
App->>MyAccountApi: enrollPasskeyChallenge(userIdentityId?, connection?)
MyAccountApi->>MethodChannel: invokeMapRequest(myAccount#enrollPasskeyChallenge)
alt Android
MethodChannel->>AndroidHandler: handle()
AndroidHandler->>SDK: passkeyEnrollmentChallenge(userIdentityId, connection)
SDK-->>AndroidHandler: PasskeyEnrollmentChallenge
AndroidHandler-->>MethodChannel: success(challenge.toMap())
else iOS/macOS
MethodChannel->>iOSHandler: handle()
iOSHandler->>SDK: passkeyEnrollmentChallenge(userIdentityId, connection)
SDK-->>iOSHandler: PasskeyEnrollmentChallenge
iOSHandler-->>MethodChannel: callback(challenge.asDictionary())
end
MethodChannel-->>MyAccountApi: challenge map
MyAccountApi-->>App: PasskeyEnrollmentChallenge
App->>App: Present OS passkey UI
App->>MyAccountApi: enrollPasskey(challenge, credential)
MyAccountApi->>MethodChannel: invokeMapRequest(myAccount#enrollPasskey)
alt Android
MethodChannel->>AndroidHandler: handle()
AndroidHandler->>SDK: enroll(credential, challenge)
SDK-->>AndroidHandler: PasskeyAuthenticationMethod
AndroidHandler-->>MethodChannel: success(method.toMap())
else iOS/macOS
MethodChannel->>iOSHandler: handle()
iOSHandler->>SDK: enroll(passkey, challenge)
SDK-->>iOSHandler: PasskeyAuthenticationMethod
iOSHandler-->>MethodChannel: callback(method.asDictionary())
end
MethodChannel-->>MyAccountApi: method map
MyAccountApi-->>App: MyAccountPasskeyAuthenticationMethod
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
auth0_flutter/EXAMPLES.md (1)
1600-1608: 💤 Low valueConsider including
authenticatorAttachmentfor consistency.The passkey enrollment example constructs a
PasskeyCredentialwithout theauthenticatorAttachmentfield, but the login example (lines 1133-1142) includesauthenticatorAttachment: 'platform'. For consistency and completeness, consider adding this field to the enrollment example or documenting that it's optional.📝 Suggested addition
final credential = PasskeyCredential( id: '...', rawId: '...', type: 'public-key', + authenticatorAttachment: 'platform', response: PasskeyAuthenticatorResponse( clientDataJSON: '...', attestationObject: '...', ), );🤖 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/EXAMPLES.md` around lines 1600 - 1608, The enrollment example for PasskeyCredential is missing the optional authenticatorAttachment property used in the login example; update the PasskeyCredential instantiation (symbol: PasskeyCredential) to include authenticatorAttachment: 'platform' (or document its optionality) so the example matches the login snippet and is consistent with PasskeyAuthenticatorResponse usage; ensure the field is added alongside id, rawId, type and response in the example.auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.kt (1)
69-81: ⚡ Quick winConsider refactoring the double Gson serialization.
The double serialization
Gson().fromJson(Gson().toJson(authParamsPublicKey), Map::class.java)is inefficient and loses type safety. IfauthParamsPublicKeyis already a compatible map-like structure, consider direct casting or a more targeted conversion. If the Auth0 Android SDK returns a specific type, you could map its properties directly to avoid the round-trip overhead and the looseMap<*, *>typing.🤖 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 69 - 81, The PasskeyEnrollmentChallenge.toMyAccountPasskeyChallengeMap function currently uses a double Gson serialize/deserialize round-trip (Gson().fromJson(Gson().toJson(authParamsPublicKey), Map::class.java)) which is inefficient and loses type information; replace this with a direct conversion by inspecting the runtime type of authParamsPublicKey (e.g., if it's already a Map or JSONObject) and cast it safely (or map its known properties into a new Kotlin Map) so you avoid the Gson round-trip and use a typed Map<String, Any?> for "authParamsPublicKey" when returning the buildMap; update any necessary null checks/casts to preserve safety.
🤖 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/request_handlers/my_account/EnrollPasskeyRequestHandler.kt`:
- Around line 96-106: The Response construction in EnrollPasskeyRequestHandler
(fields attestationObject, authenticatorData, signature, userHandle) currently
falls back to empty strings; instead remove those defensive defaults so missing
required fields fail the cast/parse or add explicit validation that throws a
clear exception when any of Response.attestationObject,
Response.authenticatorData, Response.signature, or Response.userHandle is
null/blank; update the code around the Response(...) creation to either use
non-null casts (to let Kotlin throw) or validate and throw a descriptive
IllegalArgumentException identifying the missing field(s) so callers receive a
clear error rather than silent empty values.
---
Nitpick comments:
In
`@auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/MyAccountExtensions.kt`:
- Around line 69-81: The
PasskeyEnrollmentChallenge.toMyAccountPasskeyChallengeMap function currently
uses a double Gson serialize/deserialize round-trip
(Gson().fromJson(Gson().toJson(authParamsPublicKey), Map::class.java)) which is
inefficient and loses type information; replace this with a direct conversion by
inspecting the runtime type of authParamsPublicKey (e.g., if it's already a Map
or JSONObject) and cast it safely (or map its known properties into a new Kotlin
Map) so you avoid the Gson round-trip and use a typed Map<String, Any?> for
"authParamsPublicKey" when returning the buildMap; update any necessary null
checks/casts to preserve safety.
In `@auth0_flutter/EXAMPLES.md`:
- Around line 1600-1608: The enrollment example for PasskeyCredential is missing
the optional authenticatorAttachment property used in the login example; update
the PasskeyCredential instantiation (symbol: PasskeyCredential) to include
authenticatorAttachment: 'platform' (or document its optionality) so the example
matches the login snippet and is consistent with PasskeyAuthenticatorResponse
usage; ensure the field is added alongside id, rawId, type and response in the
example.
🪄 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: 8ca081d1-8e90-4982-9ba3-2a08f18f1d3a
📒 Files selected for processing (31)
auth0_flutter/EXAMPLES.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/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/example/android/app/strings.xml.exampleauth0_flutter/example/ios/Runner.xcodeproj/project.pbxprojauth0_flutter/example/ios/Tests/MyAccount/MyAccountEnrollPasskeyChallengeMethodHandlerTests.swiftauth0_flutter/example/ios/Tests/MyAccount/MyAccountEnrollPasskeyMethodHandlerTests.swiftauth0_flutter/example/ios/Tests/MyAccount/MyAccountSpies.swiftauth0_flutter/example/lib/example_app.dartauth0_flutter/ios/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftauth0_flutter/ios/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftauth0_flutter/lib/auth0_flutter.dartauth0_flutter/lib/src/mobile/my_account_api.dartauth0_flutter/macos/Classes/MyAccountAPI/MyAccountEnrollPasskeyChallengeMethodHandler.swiftauth0_flutter/macos/Classes/MyAccountAPI/MyAccountEnrollPasskeyMethodHandler.swiftauth0_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_my_account_test.dart
e87b7a3 to
7e4ece3
Compare
7e4ece3 to
9508675
Compare
…erties in passkeys request handlers
📋 Changes
Adds passkey enrollment to the My Account API for iOS (16.6+) and Android (API 28+), letting a signed-in user register a new passkey as an MFA authentication method. This extends the existing
MyAccountApi(introduced in #835) with the two-step WebAuthn registration flow, following Auth0's My Account API.The SDK requests the enrollment challenge and submits the resulting credential; presenting the OS passkey-creation UI is left to the integrating app (the SDK is intentionally free of any authenticator/UI dependency), consistent with the existing passkey login/signup flows.
New public API (
auth0.myAccount(accessToken: ...)→MyAccountApi):enrollPasskeyChallenge({String? userIdentityId, String? connection})→PasskeyEnrollmentChallenge— requests a WebAuthn registration challenge.enrollPasskey({required PasskeyEnrollmentChallenge challenge, required PasskeyCredential credential})→MyAccountPasskeyAuthenticationMethod— submits the app-supplied attestation to complete enrollment.Both require a My Account access token (audience
https://{domain}/me/) with thecreate:me:authentication_methodsscope.New types (exported from
auth0_flutterandauth0_flutter_platform_interface):PasskeyEnrollmentChallenge—authenticationMethodId,authSession,authParamsPublicKey(WebAuthn creation options).MyAccountPasskeyAuthenticationMethod— the enrolled method (id,type,keyId,publicKey,userHandle,credentialDeviceType,credentialBackedUp,aaguid,relyingPartyId,transports,createdAt, …).MyAccountEnrollPasskeyChallengeOptionsandMyAccountEnrollPasskeyOptions.PasskeyCredential/PasskeyAuthenticatorResponsetypes from the passkey login/signup work.Platform interface / method channel:
Auth0FlutterMyAccountPlatformgainsenrollPasskeyChallenge(...)andenrollPasskey(...).myAccount#enrollPasskeyChallengeandmyAccount#enrollPasskeyon theauth0.com/auth0_flutter/my_accountchannel.Native implementation:
darwin/):MyAccountEnrollPasskeyChallengeMethodHandlerandMyAccountEnrollPasskeyMethodHandler(registered inMyAccountHandler, withMyAccountExtensionsmapping helpers). Mirrored intoios/andmacos/via the source symlinks.EnrollPasskeyChallengeRequestHandlerandEnrollPasskeyRequestHandler(registered inAuth0FlutterPlugin, withMyAccountExtensionsmapping helpers).📎 References
🎯 Testing
Automated:
method_channel_auth0_flutter_my_account_test.dartcovers both new channel methods (argument mapping + result parsing forPasskeyEnrollmentChallengeandMyAccountPasskeyAuthenticationMethod).MyAccountEnrollPasskeyChallengeMethodHandlerTests,MyAccountEnrollPasskeyMethodHandlerTests(withMyAccountSpies).EnrollPasskeyChallengeRequestHandlerTest,EnrollPasskeyRequestHandlerTest.Manual (end-to-end on physical devices):
https://{domain}/me/and theme:scopes.Verified on a physical iPhone (iOS 16.6+) and a physical Android device (API 28+).
Summary by CodeRabbit
Release Notes
New Features
Documentation