flutter_passkey_service 0.0.5
flutter_passkey_service: ^0.0.5 copied to clipboard
A comprehensive Flutter plugin for seamless Passkey (WebAuthn) integration on iOS and Android. Enable passwordless authentication with biometric security.
Flutter Passkey Service - WebAuthn FIDO2 Passwordless Authentication #
A robust, production-ready Flutter plugin for integrating Passkeys (WebAuthn/FIDO2) passwordless authentication on iOS and Android. Transform user authentication with biometric security and eliminate passwords.
📖 Table of Contents #
- Features
- Platform Support
- Installation
- Domain Verification Setup
- Usage Guide
- Advanced Usage
- Security Considerations
- Contributing & Support
- License
✨ Features #
- Passwordless Authentication: Secure biometric and device-based authentication.
- Cross-Platform: Unifies iOS AuthenticationServices and Android Credential Manager APIs.
- Cross-Device Sync: Auto-sync across devices via iCloud Keychain and Google Password Manager.
- WebAuthn Compliant: Full compliance with W3C WebAuthn standards.
- Advanced Extensions: Native support for PRF (derive symmetric Key Encryption Keys) and Large Blob (store data directly on the passkey).
- Type-Safe API: Reliable Flutter-to-native communication generated with Pigeon.
- JSON Serialization: Easy conversion to and from server JSON responses.
📋 Platform Support #
| Platform | Minimum Version | Supported Features |
|---|---|---|
| iOS | 16.0+ | Touch ID, Face ID, Device Passcode, External Authenticators, Resident Keys |
| Android | API 28+ (9.0) | Fingerprint, Face Unlock, Device PIN, External Authenticators, Resident Keys |
🚀 Installation #
Add flutter_passkey_service to your pubspec.yaml:
dependencies:
flutter_passkey_service: ^0.0.3
Run:
flutter pub get
🔧 Domain Verification Setup #
⚠️ Important: Passkeys require cryptographic proof that your app is tied to a specific web domain. Domain verification is mandatory.
iOS Setup (Apple App Site Association) #
-
Add Capability: In Xcode, go to your target's Signing & Capabilities, add Associated Domains, and enter
webcredentials:yourdomain.com. -
Host Association File: Create an
apple-app-site-associationfile (no.jsonextension) and host it athttps://yourdomain.com/.well-known/apple-app-site-association.{ "webcredentials": { "apps": ["TEAMID.com.yourcompany.yourapp"] } }(Ensure Response Content-Type is
application/json)
Android Setup (Digital Asset Links) #
-
Get SHA256 Fingerprint: Obtain the SHA256 signature of your release and debug keystores.
-
Host Asset Links File: Create an
assetlinks.jsonfile and host it athttps://yourdomain.com/.well-known/assetlinks.json.[{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"] } }](Ensure Response Content-Type is
application/json)
💻 Usage Guide #
1. Registration Flow #
Create a new Passkey credential for the user. Usually, you request creation options from your backend.
import 'package:flutter_passkey_service/flutter_passkey_service.dart';
Future<void> registerPasskey() async {
try {
final options = FlutterPasskeyService.createRegistrationOptions(
challenge: 'base64url-encoded-challenge-from-server',
rpName: 'Your App Name',
rpId: 'yourdomain.com', // Must match verified domain
userId: 'user-unique-id',
username: 'user@example.com',
displayName: 'John Doe',
);
// Perform biometric authentication to create the Passkey
final response = await FlutterPasskeyService.register(options);
// Send `response` back to your server to store the public key
print('Registration successful: ${response.id}');
} on PasskeyException catch (e) {
print('Registration failed: ${e.message}');
}
}
2. Authentication Flow #
Authenticate a user with an existing Passkey.
Future<void> authenticate() async {
try {
final request = FlutterPasskeyService.createAuthenticationOptions(
challenge: 'base64url-encoded-challenge-from-server',
rpId: 'yourdomain.com', // Must match verified domain
);
// Prompt biometric authentication
final response = await FlutterPasskeyService.authenticate(request);
// Send `response` back to your server to verify the signature
print('Authentication successful: ${response.id}');
} on PasskeyException catch (e) {
print('Authentication failed: ${e.message}');
}
}
3. Working with Server JSON #
Often, your server will generate the WebAuthn options directly as JSON. The plugin natively supports parsing these.
// Register
final serverRegistrationJson = await backend.getRegistrationOptions();
final registerOptions = FlutterPasskeyService.createRegistrationOptionsFromJson(serverRegistrationJson);
final regResponse = await FlutterPasskeyService.register(registerOptions);
// Authenticate
final serverAuthJson = await backend.getAuthenticationOptions();
final authOptions = FlutterPasskeyService.createAuthenticationOptionsFromJson(serverAuthJson);
final authResponse = await FlutterPasskeyService.authenticate(authOptions);
You can also export options back to JSON for debugging:
print(registerOptions.toJsonString());
4. Error Handling #
The plugin provides a unified PasskeyException with typed errors.
try {
await FlutterPasskeyService.authenticate(request);
} on PasskeyException catch (e) {
switch (e.errorType) {
case PasskeyErrorType.userCancelled:
print('User cancelled the biometric prompt');
break;
case PasskeyErrorType.noCredentialsAvailable:
print('No passkeys found for this site.');
break;
case PasskeyErrorType.platformNotSupported:
print('Passkeys are not supported on this OS version.');
break;
case PasskeyErrorType.domainNotAssociated:
print('Domain verification failed. Check assetlinks.json / apple-app-site-association.');
break;
default:
print('Unhandled passkey error: ${e.message}');
}
}
5. WebAuthn Extensions (PRF & Large Blob) #
PRF (Key Encryption Key) The PRF extension allows you to derive a symmetric key (KEK) during authentication, tied strictly to the passkey. This is perfect for encrypting local offline game saves or profiles.
// 1. Enable PRF during Registration
final regOptions = FlutterPasskeyService.createRegistrationOptions(
/* ... */
enablePrf: true,
);
// 2. Derive Key during Authentication
final authOptions = FlutterPasskeyService.createAuthenticationOptionsFromJson(serverAuthJson);
// Send your salt to derive the KEK
authOptions.extensions = AuthGenerateOptionExtension(
prf: PrfExtensionInput(eval: {'first': 'base64url-encoded-salt-here'})
);
final response = await FlutterPasskeyService.authenticate(authOptions);
final derivedKey = response.clientExtensionResults?.prf?.results?['first'];
Large Blob Storage The Large Blob extension lets you store up to 1KB of arbitrary data directly within the passkey hardware.
// 1. Enable Large Blob Support during Registration
final regOptions = FlutterPasskeyService.createRegistrationOptions(
/* ... */
enableLargeBlob: true,
);
// 2. Write Data during Authentication
final authOptionsWrite = FlutterPasskeyService.createAuthenticationOptions(
/* ... */
largeBlobWrite: Uint8List.fromList('Hello World'.codeUnits),
);
await FlutterPasskeyService.authenticate(authOptionsWrite);
// 3. Read Data during Authentication
final authOptionsRead = FlutterPasskeyService.createAuthenticationOptions(
/* ... */
largeBlobRead: true,
);
final response = await FlutterPasskeyService.authenticate(authOptionsRead);
final blobData = response.clientExtensionResults?.largeBlob?.blob;
📚 Tutorials & Articles #
To get an in-depth understanding of the transition to passwordless logins and see a complete conceptual walkthrough of this plugin, check out this comprehensive guide:
🏗️ Advanced Usage #
For granular control, you can define custom options using the typed model classes directly:
final customOptions = RegisterGenerateOptionData(
challenge: '...',
rp: RegisterGenerateOptionRp(name: 'App', id: 'domain.com'),
user: RegisterGenerateOptionUser(id: 'user', name: 'user', displayName: 'User'),
pubKeyCredParams: [
RegisterGenerateOptionPublicKeyParams(alg: -7, type: 'public-key'), // ES256
RegisterGenerateOptionPublicKeyParams(alg: -257, type: 'public-key'), // RS256
],
timeout: 60000,
attestation: 'direct',
authenticatorSelection: RegisterGenerateOptionAuthenticatorSelection(
residentKey: 'required',
userVerification: 'required',
authenticatorAttachment: 'platform',
),
);
🔐 Security Considerations #
- Server-Side Verification: This plugin only handles the client-side component of WebAuthn. You MUST securely verify the cryptographic signatures on your backend.
- Challenge Generation: Challenges must be generated server-side using cryptographically secure random number generators to prevent replay attacks.
- HTTPS: Apple and Google require your associated domain to be served over secure HTTPS.
- Verification Result: Always use
clientDataJSON,authenticatorData, andsignatureto securely verify the passkey login or credential registration.
🤝 Contributing & Support #
- Repository: GitHub
- Issue Tracker: Report a bug or request a feature
- Contributions are welcome! Read the
CONTRIBUTING.mdfor guidelines.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.