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
:required
orpreffered
.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
: APassKeysOptions
object.
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 theknownCredentials
when 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 optional
This returns a PassKeyPair
object that contains the following information:
authData.b64Credential
: The credential ID in base64 format.authData.rawCredential
:Uint8List
representation of the credential ID.authData.publicKey
: A tuple containing the x and y coordinates of the public key asUint256
instances.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 aPassKeyPair
instance from a JSON string.toJson()
: Returns a JSON string representation of thePassKeyPair
instance.
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
:Uint8List
representation of the credential ID.signature
: A tuple containing ther
ands
values of the signature asUint256
instances.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 thePassKeySignature
instance 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);