flutter_passkey_service 0.0.3
flutter_passkey_service: ^0.0.3 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 #
๐ The complete Flutter solution for Passkey authentication - A robust, production-ready plugin for integrating Passkeys (WebAuthn/FIDO2) passwordless authentication in Flutter apps on iOS 16.0+ and Android API 28+.
โจ Transform user authentication with biometric security, eliminate passwords, and provide a seamless cross-device experience that users love.
๐ Table of Contents #
- ๐ฏ Why Choose Flutter Passkey Service?
- ๐ฑ Live Demo
- โจ Key Features
- ๐ Comparison with Alternatives
- ๐ง Feature Matrix
- ๐ Platform Support
- ๐ Quick Start
- ๐ Comprehensive Guide
- ๐๏ธ Advanced Configuration
- ๐ง Platform Requirements
- ๐งช Testing
- ๐ Security Considerations
- ๐ค Contributing
- ๐ Support
- ๐ Resources
๐ฏ Why Choose Flutter Passkey Service? #
- ๐ Eliminate Passwords Forever - Replace vulnerable passwords with unphishable biometric authentication
- ๐ฑ Native Platform Integration - Built on iOS AuthenticationServices and Android Credential Manager APIs
- ๐ Cross-Device Sync - Passkeys automatically sync across user devices via iCloud Keychain and Google Password Manager
- โก Lightning Fast Setup - Get running in minutes with our streamlined API and comprehensive guides
- ๐ก๏ธ Enterprise Security - WebAuthn compliant with FIDO2 certification for maximum security
- ๐จ Developer Experience - Type-safe API generated with Pigeon, comprehensive error handling, and detailed documentation
๐ฑ Live Demo #
See it in action! Check out our interactive demo app to experience passkey authentication firsthand.
iOS Demo | Android Demo |
---|---|
![]() |
![]() |
Touch ID/Face ID authentication | Biometric authentication on Android |
๐ธ Screenshots coming soon - We're preparing visual demos to showcase the seamless user experience.
โจ Key Features #
- ๐ Passwordless Authentication - Secure biometric and device-based authentication
- ๐ฑ Cross-Platform Support - Native implementation for iOS 16.0+ and Android API 28+
- ๐ก๏ธ WebAuthn Compliant - Full compliance with W3C WebAuthn standards
- ๐ Cross-Device Sync - Passkeys sync across user's devices via platform providers
- ๐ Type-Safe API - Generated with Pigeon for reliable Flutter-to-native communication
- ๐ฏ Easy Integration - Simple, developer-friendly API with comprehensive error handling
- ๐ Well Documented - Complete API documentation with examples
๐ Why Flutter Passkey Service vs Alternatives? #
Feature | Flutter Passkey Service | Other Solutions | Traditional Auth |
---|---|---|---|
Security | โ Unphishable biometric | โ ๏ธ Varies | โ Password vulnerable |
User Experience | โ One-tap auth | โ ๏ธ Multi-step | โ Type passwords |
Cross-Device Sync | โ Automatic via OS | โ Manual setup | โ Manual everywhere |
Platform Integration | โ Native iOS/Android APIs | โ ๏ธ Wrapper libraries | โ Web-only |
Type Safety | โ Pigeon-generated | โ ๏ธ Manual types | โ Standard HTTP |
Maintenance | โ Active development | โ ๏ธ Varies | โ Constant security updates |
Setup Complexity | ๐ก Medium (domain setup) | ๐ด High (SDK + backend) | ๐ข Low (username/password) |
Long-term Viability | โ Industry standard | โ ๏ธ Depends on vendor | โ Being phased out |
๐ง Feature Matrix #
Feature | iOS | Android | Description |
---|---|---|---|
Touch ID | โ | โ | Fingerprint authentication on iOS |
Face ID | โ | โ | Facial recognition on iOS |
Fingerprint | โ | โ | Fingerprint sensors on Android |
Face Unlock | โ | โ | Face recognition on Android |
Device PIN | โ | โ | Fallback to device passcode |
Cross-Device Sync | โ iCloud Keychain | โ Google Password Manager | Automatic credential sync |
External Authenticators | โ | โ | USB/NFC security keys |
Resident Keys | โ | โ | Credentials stored on device |
User Verification | โ | โ | Biometric confirmation required |
๐ Platform Support #
Platform | Minimum Version | Features |
---|---|---|
iOS | 16.0+ | Touch ID, Face ID, Device Passcode |
Android | API 28+ (Android 9.0) | Fingerprint, Face unlock, Device PIN |
Flutter | 3.3.0+ | Full feature support |
๐ Quick Start #
1๏ธโฃ Installation #
Add flutter_passkey_service
to your pubspec.yaml
:
dependencies:
flutter_passkey_service: ^0.0.3
Install the package:
flutter pub get
2๏ธโฃ Lightning Quick Demo #
Want to see it work immediately? Copy this minimal example:
import 'package:flutter/material.dart';
import 'package:flutter_passkey_service/flutter_passkey_service.dart';
class PasskeyDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Passkey Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => _registerPasskey(),
child: Text('๐ Register Passkey'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => _authenticate(),
child: Text('๐ Authenticate'),
),
],
),
),
);
}
Future<void> _registerPasskey() async {
try {
final options = FlutterPasskeyService.createRegistrationOptions(
challenge: 'demo-challenge-${DateTime.now().millisecondsSinceEpoch}',
rpName: 'Demo App',
rpId: 'yourdomain.com', // Replace with your domain
userId: 'demo-user',
username: 'demo@example.com',
displayName: 'Demo User',
);
final response = await FlutterPasskeyService.register(options);
print('โ
Registration successful: ${response.id}');
} on PasskeyException catch (e) {
print('โ Registration failed: ${e.message}');
}
}
Future<void> _authenticate() async {
try {
final request = FlutterPasskeyService.createAuthenticationOptions(
challenge: 'auth-challenge-${DateTime.now().millisecondsSinceEpoch}',
rpId: 'yourdomain.com', // Replace with your domain
);
final response = await FlutterPasskeyService.authenticate(request);
print('โ
Authentication successful: ${response.id}');
} on PasskeyException catch (e) {
print('โ Authentication failed: ${e.message}');
}
}
}
3๏ธโฃ Essential Platform Setup #
โ ๏ธ Important: Domain verification is required for production use. The demo above works for testing, but you'll need to set up domain association for real apps.
iOS Setup
-
Minimum Requirements: iOS 16.0+
-
Add Capability: In Xcode, add "Associated Domains" capability
- Open your project in Xcode
- Select your target โ "Signing & Capabilities"
- Click "+" and add "Associated Domains"
-
Configure Domain: Add your domain with
webcredentials
prefix:webcredentials:yourdomain.com
-
Domain Verification: Create an
apple-app-site-association
file on your server:{ "webcredentials": { "apps": ["TEAMID.com.yourcompany.yourapp"] } }
- Host at:
https://yourdomain.com/.well-known/apple-app-site-association
- No file extension required
- Content-Type:
application/json
- Verification Tool: Apple App Site Association Validator
- Host at:
Android Setup
-
Minimum Requirements: Android API 28+ (Android 9.0)
-
Add Dependencies: The plugin automatically includes required dependencies
-
Configure Digital Asset Links: Create an
assetlinks.json
file:[{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": ["SHA256_FINGERPRINT_OF_YOUR_APP"] } }]
-
Host Asset Links File:
- Upload to:
https://yourdomain.com/.well-known/assetlinks.json
- Content-Type:
application/json
- Must be accessible via HTTPS
- Upload to:
-
Get SHA256 Fingerprint:
# For debug keystore keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android # For release keystore keytool -list -v -keystore /path/to/your/keystore.jks -alias your_key_alias
-
Verification Tools:
4๏ธโฃ Working with Server JSON #
The library provides convenient methods to work with JSON data from your server:
import 'package:flutter_passkey_service/flutter_passkey_service.dart';
class ServerIntegration {
/// Register with server-provided JSON options
Future<bool> registerWithServerOptions() async {
try {
// Get registration options from your server
final serverResponse = await getRegistrationOptionsFromServer();
// Method 1: Create from JSON Map
final options = FlutterPasskeyService.createRegistrationOptionsFromJson(serverResponse);
// Method 2: Create from JSON String (if server returns string)
// final options = FlutterPasskeyService.createRegistrationOptionsFromJsonString(jsonString);
final result = await FlutterPasskeyService.register(options);
// Send result to server for verification
return await sendRegistrationResultToServer(result);
} on PasskeyException catch (e) {
print('Registration failed: ${e.message}');
return false;
}
}
/// Authenticate with server-provided JSON options
Future<bool> authenticateWithServerOptions() async {
try {
// Get authentication options from your server
final serverResponse = await getAuthenticationOptionsFromServer();
// Create from JSON
final request = FlutterPasskeyService.createAuthenticationOptionsFromJson(serverResponse);
final result = await FlutterPasskeyService.authenticate(request);
// Send result to server for verification
return await sendAuthenticationResultToServer(result);
} on PasskeyException catch (e) {
print('Authentication failed: ${e.message}');
return false;
}
}
/// Example server responses that the JSON methods can handle
Future<Map<String, dynamic>> getRegistrationOptionsFromServer() async {
// Your server should return something like this:
return {
"challenge": "base64url-encoded-challenge",
"rp": {
"name": "My App",
"id": "example.com"
},
"user": {
"id": "user-123",
"name": "user@example.com",
"displayName": "John Doe"
},
"pubKeyCredParams": [
{"alg": -7, "type": "public-key"}, // ES256
{"alg": -257, "type": "public-key"} // RS256
],
"timeout": 60000,
"attestation": "none",
"excludeCredentials": [], // Optional: exclude existing credentials
"authenticatorSelection": {
"residentKey": "preferred",
"userVerification": "required",
"requireResidentKey": false,
"authenticatorAttachment": "platform"
},
"extensions": {
"credProps": true
}
};
}
Future<Map<String, dynamic>> getAuthenticationOptionsFromServer() async {
// Your server should return something like this:
return {
"challenge": "base64url-encoded-challenge",
"rpId": "example.com",
"allowCredentials": [
{
"id": "credential-id-1",
"type": "public-key",
"transports": ["internal", "hybrid"]
}
],
"timeout": 60000,
"userVerification": "required"
};
}
Future<bool> sendRegistrationResultToServer(CreatePasskeyResponseData result) async {
// Send to your server for verification
// Implementation depends on your backend
return true;
}
Future<bool> sendAuthenticationResultToServer(GetPasskeyAuthenticationResponseData result) async {
// Send to your server for verification
// Implementation depends on your backend
return true;
}
}
5๏ธโฃ JSON Serialization Support #
You can also convert options back to JSON for debugging or server communication:
// Convert options to JSON for debugging
final options = FlutterPasskeyService.createRegistrationOptions(/*...*/);
final jsonMap = options.toJson();
final jsonString = options.toJsonString();
print('Registration options: $jsonString');
// Same for authentication options
final authOptions = FlutterPasskeyService.createAuthenticationOptions(/*...*/);
final authJson = authOptions.toJson();
6๏ธโฃ Production-Ready Example #
Once you've set up domain verification, here's a production-ready implementation:
import 'package:flutter_passkey_service/flutter_passkey_service.dart';
class PasskeyAuthService {
static const String rpId = 'yourdomain.com'; // Your verified domain
static const String rpName = 'Your App Name';
/// Register a new passkey for the user
Future<bool> registerPasskey({
required String userId,
required String username,
required String displayName,
required String serverChallenge, // Get from your backend
}) async {
try {
final options = FlutterPasskeyService.createRegistrationOptions(
challenge: serverChallenge,
rpName: rpName,
rpId: rpId,
userId: userId,
username: username,
displayName: displayName,
);
final response = await FlutterPasskeyService.register(options);
// Send response to your server for verification and storage
final success = await _sendToServer('/register', response);
if (success) {
print('โ
Passkey registered successfully');
return true;
}
} on PasskeyException catch (e) {
_handlePasskeyError(e);
} catch (e) {
print('โ Unexpected error: $e');
}
return false;
}
/// Authenticate user with their passkey
Future<bool> authenticate({
required String serverChallenge, // Get from your backend
List<String>? allowedCredentials, // Optional: restrict to specific credentials
}) async {
try {
final request = FlutterPasskeyService.createAuthenticationOptions(
challenge: serverChallenge,
rpId: rpId,
allowedCredentialIds: allowedCredentials,
);
final response = await FlutterPasskeyService.authenticate(request);
// Send response to your server for verification
final success = await _sendToServer('/authenticate', response);
if (success) {
print('โ
Authentication successful');
return true;
}
} on PasskeyException catch (e) {
_handlePasskeyError(e);
} catch (e) {
print('โ Unexpected error: $e');
}
return false;
}
/// Handle passkey-specific errors with user-friendly messages
void _handlePasskeyError(PasskeyException e) {
switch (e.errorType) {
case PasskeyErrorType.userCancelled:
print('๐ซ User cancelled the operation');
break;
case PasskeyErrorType.noCredentialsAvailable:
print('๐ฑ No passkeys available - please register first');
break;
case PasskeyErrorType.platformNotSupported:
print('โ ๏ธ Passkeys not supported on this device');
break;
case PasskeyErrorType.domainNotAssociated:
print('๐ Domain verification failed - check your setup');
break;
default:
print('โ Authentication failed: ${e.message}');
}
}
/// Send authentication response to your backend
Future<bool> _sendToServer(String endpoint, dynamic response) async {
// Implement your server communication here
// This should verify the response and return success/failure
return true; // Placeholder
}
}
๐ Comprehensive Guide #
Domain Verification Setup #
Proper domain verification is essential for passkey functionality. Both platforms require your app to be associated with your web domain.
iOS Domain Verification (Apple App Site Association)
-
Create the Association File:
{ "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp", "TEAMID.com.yourcompany.yourapp.staging" ] } }
-
Host the File:
- URL:
https://yourdomain.com/.well-known/apple-app-site-association
- Important: No
.json
file extension! - Content-Type:
application/json
- Must be served over HTTPS
- Must return HTTP 200 status
- URL:
-
Find Your Team ID:
- Go to Apple Developer Account
- Navigate to "Membership" section
- Copy your Team ID (10-character string)
-
Verify Association:
# Test your association file curl -v https://yourdomain.com/.well-known/apple-app-site-association
Android Domain Verification (Digital Asset Links)
-
Get Your App's SHA256 Fingerprint:
# Debug keystore (for development) keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android | grep SHA256 # Release keystore (for production) keytool -list -v -keystore /path/to/release-key.keystore -alias release-key-alias | grep SHA256 # From APK file keytool -printcert -jarfile app-release.apk | grep SHA256
-
Create Asset Links File:
[ { "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": [ "AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78" ] } } ]
-
Host the File:
- URL:
https://yourdomain.com/.well-known/assetlinks.json
- Content-Type:
application/json
- Must be served over HTTPS
- Must return HTTP 200 status
- URL:
-
Verify Asset Links:
# Test your asset links file curl -v https://yourdomain.com/.well-known/assetlinks.json
- Use Google's Tester
- Use Asset Links Generator
Common Domain Verification Issues
Issue | Solution |
---|---|
File not found (404) | Ensure files are in /.well-known/ directory |
HTTPS required | Both files must be served over HTTPS only |
Wrong content-type | Set Content-Type: application/json |
Invalid JSON | Validate JSON syntax |
Wrong package name | Must match your app's package identifier exactly |
Case sensitivity | Package names and fingerprints are case-sensitive |
Caching issues | Clear CDN/server cache after updating files |
Testing Domain Verification
// Test domain association in your Flutter app
void testDomainVerification() async {
try {
// This will fail if domain verification is not set up correctly
final request = FlutterPasskeyService.createAuthenticationOptions(
challenge: 'test-challenge',
rpId: 'yourdomain.com', // Must match your verified domain
);
print('Domain verification appears to be working');
} catch (e) {
print('Domain verification issue: $e');
}
}
Registration Flow #
The passkey registration process involves creating a new credential for the user:
Future<CreatePasskeyResponseData> registerUser({
required String username,
required String userId,
required String challenge,
}) async {
// 1. Create registration options
final options = RegisterGenerateOptionData(
challenge: challenge, // Base64URL encoded challenge from server
rp: RegisterGenerateOptionRp(
name: 'Your App Name',
id: 'yourdomain.com',
),
user: RegisterGenerateOptionUser(
id: userId, // Unique user identifier
name: username,
displayName: 'Display Name',
),
pubKeyCredParams: [
RegisterGenerateOptionPublicKeyParams(alg: -7, type: 'public-key'), // ES256
RegisterGenerateOptionPublicKeyParams(alg: -257, type: 'public-key'), // RS256
],
timeout: 60000,
attestation: 'none',
excludeCredentials: [], // Previously registered credentials to exclude
authenticatorSelection: RegisterGenerateOptionAuthenticatorSelection(
residentKey: 'preferred',
userVerification: 'required',
requireResidentKey: false,
authenticatorAttachment: 'platform',
),
extensions: RegisterGenerateOptionExtension(credProps: true),
);
// 2. Perform registration
final response = await FlutterPasskeyService.register(options);
// 3. Send to server for verification and storage
// response contains: id, rawId, type, authenticatorAttachment,
// response (attestationObject, clientDataJSON), clientExtensionResults
return response;
}
Authentication Flow #
Authenticate users with their existing passkeys:
Future<GetPasskeyAuthenticationResponseData> authenticateUser({
required String challenge,
List<String>? allowedCredentialIds,
}) async {
// 1. Create authentication request
final request = AuthGenerateOptionResponseData(
rpId: 'yourdomain.com',
challenge: challenge, // Base64URL encoded challenge from server
allowCredentials: allowedCredentialIds?.map((id) =>
AuthGenerateOptionAllowCredential(
id: id,
type: 'public-key',
transports: ['internal', 'hybrid'],
)
).toList() ?? [],
timeout: 60000,
userVerification: 'required',
);
// 2. Perform authentication
final response = await FlutterPasskeyService.authenticate(request);
// 3. Send to server for verification
// response contains: id, rawId, type, authenticatorAttachment,
// response (clientDataJSON, authenticatorData, signature, userHandle)
return response;
}
Error Handling #
The plugin provides comprehensive error handling through PasskeyException
:
try {
await FlutterPasskeyService.register(options);
} on PasskeyException catch (e) {
switch (e.errorType) {
case PasskeyErrorType.userCancelled:
showMessage('User cancelled the operation');
break;
case PasskeyErrorType.noCredentialsAvailable:
showMessage('No passkeys available for this account');
break;
case PasskeyErrorType.invalidParameters:
showMessage('Invalid request parameters');
break;
case PasskeyErrorType.platformNotSupported:
showMessage('Passkeys not supported on this device');
break;
default:
showMessage('Authentication failed: ${e.message}');
}
}
Available Error Types #
Error Type | Description |
---|---|
invalidParameters |
Invalid or missing parameters |
userCancelled |
User cancelled the operation |
userTimeout |
Operation timed out |
noCredentialsAvailable |
No credentials available for authentication |
credentialNotFound |
Specified credential not found |
platformNotSupported |
Platform doesn't support passkeys |
domainNotAssociated |
Domain not associated with app |
invalidResponse |
Invalid response received |
systemError |
System-level error occurred |
networkError |
Network-related error |
unknownError |
Unknown error occurred |
๐๏ธ Advanced Configuration #
๐ Complete API Reference #
Core Methods
Method | Description | Parameters | Returns |
---|---|---|---|
register(options) |
Register a new passkey | RegisterGenerateOptionData |
CreatePasskeyResponseData |
authenticate(request) |
Authenticate with passkey | AuthGenerateOptionResponseData |
GetPasskeyAuthenticationResponseData |
Helper Methods (Traditional)
Method | Description | Use Case |
---|---|---|
createRegistrationOptions() |
Create registration options manually | When building options programmatically |
createAuthenticationOptions() |
Create authentication options manually | When building options programmatically |
JSON Helper Methods (New! ๐)
Method | Description | Use Case |
---|---|---|
createRegistrationOptionsFromJson(Map) |
Create from JSON Map | Server returns JSON object |
createRegistrationOptionsFromJsonString(String) |
Create from JSON String | Server returns JSON string |
createAuthenticationOptionsFromJson(Map) |
Create from JSON Map | Server returns JSON object |
createAuthenticationOptionsFromJsonString(String) |
Create from JSON String | Server returns JSON string |
Extension Methods
Method | Description | Use Case |
---|---|---|
options.toJson() |
Convert to JSON Map | Debugging, logging, server communication |
options.toJsonString() |
Convert to JSON String | API requests, storage |
Usage Examples
// Traditional approach
final options = FlutterPasskeyService.createRegistrationOptions(
challenge: challenge,
rpName: 'My App',
rpId: 'example.com',
userId: 'user-123',
username: 'user@example.com',
);
// New JSON approach - from server response
final serverJson = await getRegistrationOptionsFromServer();
final options = FlutterPasskeyService.createRegistrationOptionsFromJson(serverJson);
// Convert back to JSON for debugging
print('Options: ${options.toJsonString()}');
Custom Registration Options #
final customOptions = RegisterGenerateOptionData(
challenge: challenge,
rp: RegisterGenerateOptionRp(name: 'App', id: 'domain.com'),
user: RegisterGenerateOptionUser(id: 'user', name: 'username', displayName: 'User'),
pubKeyCredParams: [
RegisterGenerateOptionPublicKeyParams(alg: -7, type: 'public-key'),
],
timeout: 120000, // 2 minutes
attestation: 'direct', // Request attestation
excludeCredentials: [
RegisterGenerateOptionExcludeCredential(
id: 'existing-credential-id',
type: 'public-key',
transports: ['internal'],
),
],
authenticatorSelection: RegisterGenerateOptionAuthenticatorSelection(
residentKey: 'required', // Force resident key
userVerification: 'preferred',
requireResidentKey: true,
authenticatorAttachment: 'cross-platform', // Allow external authenticators
),
extensions: RegisterGenerateOptionExtension(credProps: true),
);
Server Integration #
Challenge Generation
// Generate a cryptographically secure challenge on your server
String generateChallenge() {
final bytes = List<int>.generate(32, (i) => Random.secure().nextInt(256));
return base64Url.encode(bytes);
}
Verification
// Verify registration response on server
bool verifyRegistration(CreatePasskeyResponseData response, String challenge) {
// 1. Decode and verify clientDataJSON
// 2. Verify challenge matches
// 3. Verify origin matches your domain
// 4. Parse and verify attestationObject
// 5. Store credential for future authentication
return true; // Simplified
}
// Verify authentication response on server
bool verifyAuthentication(GetPasskeyAuthenticationResponseData response, String challenge) {
// 1. Decode and verify clientDataJSON
// 2. Verify challenge matches
// 3. Verify origin matches your domain
// 4. Verify signature using stored public key
// 5. Update credential sign count
return true; // Simplified
}
๐ง Platform Requirements #
iOS #
- Minimum Version: iOS 16.0+
- Frameworks: AuthenticationServices
- Capabilities: Associated Domains
- Features: Touch ID, Face ID, Device Passcode support
Android #
- Minimum Version: Android 9.0 (API 28)+
- Dependencies: Credential Manager API
- Features: Biometric authentication, Device PIN support
- Requirements: Google Play Services
๐งช Testing #
Unit Testing #
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_passkey_service/flutter_passkey_service.dart';
void main() {
group('FlutterPasskeyService', () {
test('createRegistrationOptions returns valid options', () {
final options = FlutterPasskeyService.createRegistrationOptions(
challenge: 'test-challenge',
rpName: 'Test App',
rpId: 'test.com',
userId: 'user-123',
username: 'test@example.com',
);
expect(options.challenge, 'test-challenge');
expect(options.rp.name, 'Test App');
expect(options.user.id, 'user-123');
});
});
}
Integration Testing #
import 'package:integration_test/integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Passkey Integration Tests', () {
testWidgets('registration flow', (tester) async {
// Test complete registration flow
// Note: Requires physical device and user interaction
});
});
}
๐ Security Considerations #
- Challenge Generation: Always generate challenges server-side using cryptographically secure methods
- Origin Verification: Verify the origin in clientDataJSON matches your domain
- Timeout Handling: Implement appropriate timeouts for user operations
- Error Messages: Avoid exposing sensitive information in error messages
- Credential Storage: Store public keys and metadata securely on your server
- Sign Count: Track and validate signature counter to prevent replay attacks
๐ค Contributing #
We welcome contributions! Please read our Contributing Guidelines for details on:
- Code of Conduct
- Development setup
- Pull request process
- Issue reporting
๐ License #
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Support #
- Documentation: API Reference
- Issues: GitHub Issues
- Discussions: GitHub Discussions
๐ Resources #
WebAuthn & Passkeys #
- WebAuthn Specification
- Passkeys Overview
- Passkeys.dev - Community resources and guides
Platform Documentation #
- iOS AuthenticationServices
- Android Credential Manager
- iOS Passkeys Developer Guide
- Android Passkeys Implementation Guide
Domain Verification Tools #
- Apple App Site Association Validator
- Google Digital Asset Links Tester
- Digital Asset Links Generator
- Apple Team ID Lookup
Testing & Debugging #
- WebAuthn.io - Test WebAuthn implementations
- Passkeys Debugger - Debug WebAuthn flows
- Yubico WebAuthn Demo - Test various scenarios
Security Resources #
- FIDO Alliance - Security specifications and guidelines
- WebAuthn Security Considerations
- OWASP Authentication Guide
๐ Success Stories #
"Implementing passkeys with Flutter Passkey Service reduced our authentication friction by 90% and completely eliminated password-related support tickets." - Developer testimonial
"The type-safe API and comprehensive documentation made integration seamless. Our users love the one-tap authentication." - Mobile team lead
๐ Keywords & Tags #
flutter
passkey
passkeys
webauthn
fido2
passwordless
authentication
biometric
security
ios
android
face-id
touch-id
fingerprint
credential-manager
dart
mobile-auth
two-factor
2fa
mfa
multi-factor
secure-login
mobile-security
๐ Star History #
โญ Star this repository if Flutter Passkey Service helped you build better authentication!
๐ What's Next? #
- ๐ Analytics and metrics integration
- ๐ Advanced credential management
- ๐ Web platform support
- ๐ฑ watchOS and wear OS support
- ๐จ UI components and themes
- ๐ Backend SDK integrations
๐ Adoption #
Flutter Passkey Service is trusted by developers building:
- ๐ฆ Fintech applications - Secure banking and payment apps
- ๐ฅ Healthcare platforms - HIPAA-compliant patient portals
- ๐ข Enterprise solutions - Internal business applications
- ๐ E-commerce apps - Streamlined checkout experiences
- ๐ฎ Gaming platforms - Quick and secure user onboarding
Made with โค๏ธ for the Flutter community | Keywords: Flutter Passkey WebAuthn FIDO2 Biometric Authentication Passwordless Security iOS Android