PassKey Signers
Passkeys signers are one of the available signers available in the web3_signers package, and is built on top of the passkeys library from corbado. Only signers that conform to the multi-signer-interface (MSI) can be used with variance.
import 'package:web3_signers/web3_signers.dart';Passkey signers conform to the MSI and allow you to sign payloads using your device's passkeys. They fall under the secp256r1 category and can be verified on-chain using a P256 Verifier.
Creating a Passkey Options
To create a Passkey Options object, you need to provide the following information:
namespace: The relying party ID (domain name), e.g.,"variance.space".name: The relying party name, e.g.,"variance".origin: The relying party origin, e.g.,"https://variance.space".userVerification:requiredorpreffered.requireResidentKey: a boolean value that specifies if the authenticator should require a resident key.,sharedWebauthnSigner: address of safe shared WebAuthn signer.
final options = PassKeysOptions(
name: "variance",
namespace: "variance.space",
origin: "https://variance.space",
userVerification: "required",
requireResidentKey: true,
sharedWebauthnSigner: EthereumAddress.fromHex("0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9"));Creating a Passkey Signer
To create a Passkey Options object, you need to provide the following information:
options: APassKeysOptionsobject.
You can optionally provide:
knownCredentials: A set of known credential IDs (defaults to an empty set). If you already know the credential IDs created for the user, you can pass theknownCredentialswhen creating the Passkey signer. This enables the authenticator to filter the passkeys presented to the user for signing operations.
final PassKeySigner pkpSigner = PassKeySigner(
options, knownCredentials: {...} // knownCredential is optional
);Registering a New Passkey
To register a new passkey, you can use the register method:
PassKeyPair pkp = await pkpSigner.register("user@variance.space", "test user");
// Username is required, display name is optionalThis returns a PassKeyPair object that contains the following information:
authData.b64Credential: The credential ID in base64 format.authData.rawCredential:Uint8Listrepresentation of the credential ID.authData.publicKey: A tuple containing the x and y coordinates of the public key asUint256instances.username: The username of the registered user.displayName: The display name of the registered user.authData.aaGUID: The Authenticator Attestation GUID.registrationTime: The timestamp of when the passkey was registered.
The PassKeyPair class provides methods to convert its instance to and from JSON format:
PassKeyPair.fromJson(String source): Constructs aPassKeyPairinstance from a JSON string.toJson(): Returns a JSON string representation of thePassKeyPairinstance.
Signing with Passkeys
There are three methods for signing a payload using the Passkey signer:
Using personalSign
personalSign returns a Uint8List which is an encoded representation of the PassKeySignature object needed on-chain. To extract individual values, you need to abi.decode() it. The signed challenge is excluded from this object, and it is assumed that your relying party is aware of this challenge, which should be Base64Url encoded.
final sig = await pkpSigner.personalSign(Uint8List(32));Using signToEc
Similar to personalSign, signToEc conforms to the MSI and returns an instance of MsgSignature containing the r, s, and v values of the signature. Effectively, v is 0.
final sig = await pkpSigner.signToEc(Uint8List(32));Using signToPasskeySignature
This method is not part of the MSI but is called internally by personalSign and signToEc, and it returns the raw PassKeySignature object.
final sig = await pkpSigner.signToPasskeySignature(Uint8List(32));For each of the above methods, you can pass an index if you have knownCredentials. This prompts the authenticator to sign specifically with a particular credential. For example:
await pkpSigner.signToPasskeySignature(Uint8List(32), 2); // Signing with knownCredential at index 2
PassKey Signature
The PassKeySignature class represents the signature generated by signing a payload with a passkey. It has the following properties:
b64Credential: The signed credential ID in base64 format.rawCredential:Uint8Listrepresentation of the credential ID.signature: A tuple containing therandsvalues of the signature asUint256instances.authData: The authenticator data as aUint8List.clientDataJSON: The client data JSON string.challengePos: The position of the challenge in the client data JSON string.userId: The user ID.
The PassKeySignature class also provides a method to convert its instance to a Uint8List using ABI encoding:
toUint8List(): Returns the ABI-encoded representation of thePassKeySignatureinstance as aUint8List.
Usage
final pkpSigner = PassKeySigner(options: passkeyOptions);
final walletFactory = SmartWalletFactory(chain, pkpSigner);
final keypair = await pkpSigner.register(username, displayName);
final salt = Uint256.zero;
final wallet = await walletFactory.createSafeAccountWithPasskey(keypair, salt, passkeyOptions.sharedWebauthnSigner);
print("wallet created ${wallet.address.hex} ");By default the RIP-7212 precompile is used for signature verifications. but you can change it by adding an optional verifier.
final wallet = await walletFactory.createSafeAccountWithPasskey(
keypair,
salt,
passkeyOptions.sharedWebauthnSigner,
p256Verifier);