permissionless_passkeys 0.1.0 copy "permissionless_passkeys: ^0.1.0" to clipboard
permissionless_passkeys: ^0.1.0 copied to clipboard

WebAuthn/Passkeys support for permissionless.dart ERC-4337 smart accounts. Enables biometric authentication with Kernel and Safe accounts using P256 signatures.

permissionless_passkeys #

WebAuthn/Passkeys support for permissionless.dart ERC-4337 smart accounts.

Enable biometric authentication (Face ID, Touch ID, Windows Hello) for your smart accounts using P256 signatures.

Features #

  • WebAuthn Smart Accounts - Create passkey-authenticated smart accounts
  • Kernel v0.3.x Support - WebAuthn validator with P256 precompile (RIP-7212)
  • Safe v1.4.1/v1.5.0 Support - Shared WebAuthn signer module
  • Cross-Platform - Works on iOS, Android, Web, macOS
  • Biometric Authentication - Face ID, Touch ID, Windows Hello, Security Keys

Supported Account Types #

Account Version EntryPoint WebAuthn Signer
Kernel v0.3.0, v0.3.1 v0.7 WebAuthn validator module
Safe v1.4.1, v1.5.0 v0.7 Shared WebAuthn signer

Installation #

Add to your pubspec.yaml:

dependencies:
  permissionless_passkeys: ^0.1.0
  permissionless: ^0.2.0  # Required peer dependency

Then run:

flutter pub get

Quick Start #

1. Register a Passkey #

import 'package:permissionless_passkeys/permissionless_passkeys.dart';

// Register a new passkey (triggers biometric prompt)
final credential = await createPasskeyCredential(
  rpId: 'myapp.com',
  rpName: 'My Application',
  userName: 'user@example.com',
);

// Credential can be serialized for storage
final json = credential.toJson();
// Later: final restored = WebAuthnCredential.fromJson(json);

2. Create a WebAuthn Account #

import 'package:permissionless_passkeys/permissionless_passkeys.dart';

// Create a WebAuthn account from the credential
final webAuthnAccount = createWebAuthnAccount(
  credential: credential,
  rpId: 'myapp.com',
);

// Or use the viem-compatible API
final webAuthnAccount = toWebAuthnAccount(
  ToWebAuthnAccountParameters(
    credential: credential,
    rpId: 'myapp.com',
  ),
);

print('Public Key: ${webAuthnAccount.publicKey}');

3. Use with Kernel or Safe Smart Account #

import 'package:permissionless/permissionless.dart';
import 'package:permissionless_passkeys/permissionless_passkeys.dart';

// Use with Kernel v0.3.x (supports WebAuthn validators)
final kernelAccount = createKernelSmartAccount(
  owner: webAuthnAccount,  // WebAuthnAccount IS an AccountOwner
  chainId: BigInt.from(11155111),
  version: KernelVersion.v0_3_1,
);

// Or use with Safe v1.4.1/v1.5.0 (supports WebAuthn shared signer)
final safeAccount = createSafeSmartAccount(
  owners: [webAuthnAccount],  // WebAuthnAccount IS an AccountOwner
  chainId: BigInt.from(11155111),
  version: SafeVersion.v1_4_1,
);

print('Account Address: ${kernelAccount.address.hex}');

4. Sign and Send Transactions #

import 'package:permissionless/permissionless.dart';

// Create clients
final bundler = createPimlicoClient(
  url: 'https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_KEY',
  entryPoint: EntryPointAddresses.v07,
);

final smartAccountClient = SmartAccountClient(
  account: account,
  bundler: bundler,
  publicClient: publicClient,
);

// Send a user operation (triggers biometric prompt for signing)
final hash = await smartAccountClient.sendUserOperation(
  calls: [
    Call(
      to: recipientAddress,
      value: BigInt.from(1000000000000000), // 0.001 ETH
    ),
  ],
  maxFeePerGas: gasPrices.fast.maxFeePerGas,
  maxPriorityFeePerGas: gasPrices.fast.maxPriorityFeePerGas,
);

print('UserOperation hash: $hash');

5. Use the WebAuthn Account Interface (viem-compatible) #

For direct signing without a smart account, use the toWebAuthnAccount API:

import 'package:permissionless_passkeys/permissionless_passkeys.dart';

// Create a WebAuthn account from a credential
final account = createWebAuthnAccount(
  credential: credential,
  rpId: 'myapp.com',
);

// Account properties
print('ID: ${account.id}');           // Base64 credential ID
print('Public Key: ${account.publicKey}'); // Hex: 0x + x(64) + y(64)
print('Type: ${account.type}');       // 'webAuthn'

// Sign a hash (triggers biometric prompt)
final result = await account.sign(hash: userOpHash);

// Result contains:
// - result.signature: Hex signature (r + s, 64 bytes)
// - result.webauthn.authenticatorData: WebAuthn authenticator data
// - result.webauthn.clientDataJSON: Client data JSON string
// - result.webauthn.challengeIndex: Index of challenge in JSON
// - result.webauthn.typeIndex: Index of type in JSON
// - result.raw: Raw Signature object

API Reference #

Types #

WebAuthnCredential

Wrapper for WebAuthn credential data:

// Create from a PassKeyPublicKey (after registration)
final credential = WebAuthnCredential.fromPublicKey(passKeyPublicKey);

// Or use the factory function
final credential = await createPasskeyCredential(
  rpId: 'myapp.com',
  rpName: 'My App',
  userName: 'user@example.com',
);

// Convert to/from JSON for storage
final json = credential.toJson();
final restored = WebAuthnCredential.fromJson(json);

// Get public key as hex (64 bytes: x || y)
final pubKeyHex = credential.publicKeyHex;

WebAuthnAccount

viem-compatible account interface for passkey signing:

// Create from credential
final account = createWebAuthnAccount(
  credential: credential,
  rpId: 'myapp.com',
);

// Or use the viem-style function
final account = toWebAuthnAccount(
  ToWebAuthnAccountParameters(
    credential: credential,
    rpId: 'myapp.com',
  ),
);

// Properties
account.id         // Base64 credential ID
account.publicKey  // Hex public key (0x + x + y)
account.type       // 'webAuthn'

// Sign a hash
final result = await account.sign(hash: '0x...');
// Returns WebAuthnSignReturnType with signature and metadata

Encoding Functions #

Kernel Signatures

// Encode a WebAuthn signature for Kernel
final encoded = encodeKernelWebAuthnSignature(
  authenticatorData: '0x...',
  clientDataJSON: '{"type":"webauthn.get",...}',
  responseTypeLocation: BigInt.from(1),
  r: BigInt.parse('...'),
  s: BigInt.parse('...'),
  usePrecompile: true,
);

// Get dummy signature for gas estimation
final dummy = getDummyKernelWebAuthnSignature(usePrecompile: true);

Safe Signatures

// Encode a WebAuthn signature for Safe
final encoded = encodeSafeWebAuthnSignature(
  authenticatorData: '0x...',
  clientDataFields: '"challenge":"..."',
  r: BigInt.parse('...'),
  s: BigInt.parse('...'),
  validAfter: BigInt.zero,
  validUntil: BigInt.zero,
);

// Get dummy signature for gas estimation
final dummy = getDummySafeWebAuthnSignature();

Platform Setup #

WebAuthn requires platform-specific configuration. See the example app README for detailed setup instructions for:

  • iOS (Associated Domains)
  • Android (App Links)
  • Web (HTTPS)
  • macOS (Entitlements)

P256 Precompile (RIP-7212) #

For gas-efficient signature verification, use chains with the P256 precompile. The library supports two detection methods:

  • Dynamic detection (recommended): isRip7212Supported(publicClient) probes the precompile via eth_call with a known-valid P256 test vector. Works on any chain, results are cached per chain ID.
  • Static detection: shouldUseP256Precompile(chainId:) checks against a curated list of 67 known-supported chain IDs.

Kernel accounts automatically use dynamic detection when a publicClient is configured, falling back to the static list otherwise.

import 'package:permissionless/permissionless.dart';

// Dynamic: probe any chain via RPC
final supported = await isRip7212Supported(publicClient);

// Static: check against known chain IDs
final supported = shouldUseP256Precompile(chainId: chainId);

Without the precompile, signature verification falls back to Solidity-based P256, which uses ~800k gas vs ~3.5k with the precompile (~6.9k on mainnet).

Example #

See the example/ directory for a complete Flutter app demonstrating:

  • Passkey registration with biometrics
  • Kernel and Safe account creation
  • Transaction encoding
  • Credential export/import

Run the example:

cd example
flutter pub get
flutter run

Testing #

Run the unit tests:

dart test

The package includes comprehensive tests for:

  • Signature encoding (Kernel and Safe formats)
  • Credential serialization
  • Account configuration
  • Address computation

Architecture #

lib/
├── permissionless_passkeys.dart   # Public exports
└── src/
    ├── accounts/
    │   ├── webauthn_account.dart         # WebAuthnAccount abstract class
    │   └── to_webauthn_account.dart      # toWebAuthnAccount factory
    ├── encoding/
    │   ├── kernel_encoding.dart          # Kernel signature ABI
    │   └── safe_encoding.dart            # Safe signature ABI
    ├── types/
    │   └── webauthn_credential.dart      # Credential wrapper
    └── factory.dart                      # Convenience factory functions

License #

MIT License - see LICENSE for details.

0
likes
150
points
109
downloads

Publisher

verified publisherfuse.io

Weekly Downloads

WebAuthn/Passkeys support for permissionless.dart ERC-4337 smart accounts. Enables biometric authentication with Kernel and Safe accounts using P256 signatures.

Homepage
Repository (GitHub)
View/report issues

Topics

#account-abstraction #erc4337 #passkeys #webauthn #smart-accounts

Documentation

API reference

License

MIT (license)

Dependencies

flutter, permissionless, web3_signers, web3dart

More

Packages that depend on permissionless_passkeys