Skip to content

Commit

Permalink
docs: Add Passkeys section, overview and guide (#498)
Browse files Browse the repository at this point in the history
* Add Passkeys section

* Add Passkeys overview

* Add passkeys guide

* Create missing pages
  • Loading branch information
germartinez authored Jun 7, 2024
1 parent 3a999f6 commit 7a2ebe4
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 1 deletion.
17 changes: 16 additions & 1 deletion pages/home/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,20 @@
"4337-overview": "Overview",
"4337-safe": "Safe and ERC-4337",
"4337-supported-networks": "Supported Networks",
"4337-guides": "Guides"
"4337-guides": "Guides",
"-- Passkeys": {
"type": "separator",
"title": "Passkeys"
},
"passkeys-overview": "Overview",
"passkeys-safe": "Safe and Passkeys",
"passkeys-supported-networks": "Supported Networks",
"passkeys-guides": "Guides",
"passkeys-tutorials": "Tutorials",
"passkeys-faq": {
"title": "FAQ",
"theme": {
"toc": false
}
}
}
1 change: 1 addition & 0 deletions pages/home/passkeys-faq.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# FAQ
3 changes: 3 additions & 0 deletions pages/home/passkeys-guides/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"safe-sdk": "Passkeys with the Safe{Core} SDK"
}
179 changes: 179 additions & 0 deletions pages/home/passkeys-guides/safe-sdk.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Passkeys with the Safe\{Core\} SDK

This guide will teach you how to create and execute multiple Safe transactions grouped in a batch from a Safe Smart Account that uses a passkey as an owner. To have a good user experience, we will use an [ERC-4337 compatible Safe](../4337-guides/safe-sdk.mdx) with sponsored transactions using Pimlico infrastructure. During this guide, we will create a new passkey, add it to the Safe as an owner, and use it to sign the user operations.

This guide uses [Pimlico](https://pimlico.io) as the service provider, but any other provider compatible with the ERC-4337 can be used.

## Prerequisites

- [Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
- A [Pimlico account](https://dashboard.pimlico.io) and an API key.
- Passkeys feature is available only in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS), in some or all [supporting browsers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API#browser_compatibility).

## Install dependencies

```bash
yarn add @safe-global/relay-kit
yarn add @safe-global/protocol-kit
```

## Steps

### Imports

Here are all the necessary imports for the script we implement in this guide.

```typescript
import { Safe4337Pack } from '@safe-global/relay-kit'
import { PasskeyArgType } from '@safe-global/protocol-kit'
```

### Create a passkey

Firstly, we need to generate a passkey credential using the WebAuthn API in a supporting browser environment.

```typescript
const RP_NAME = 'Safe Smart Account'
const USER_DISPLAY_NAME = 'User display name'
const USER_NAME = 'User name'

const passkeyCredential = await navigator.credentials.create({
publicKey: {
pubKeyCredParams: [
{
alg: -7,
type: 'public-key'
}
],
challenge: crypto.getRandomValues(new Uint8Array(32)),
rp: {
name: RP_NAME
},
user: {
displayName: USER_DISPLAY_NAME,
id: crypto.getRandomValues(new Uint8Array(32)),
name: USER_NAME
},
timeout: 60_000,
attestation: 'none',
},
})
```

After generating the `passkeyCredential` object, we need to create a new object with the `PasskeyArgType` type that will contain the `rawId` and the `publicKey` information.

```typescript
const passkeyObject = passkeyCredential as PublicKeyCredential
const attestationResponse =
passkeyObject.response as AuthenticatorAttestationResponse

const rawId = passkeyObject.rawId
const publicKey = attestationResponse.getPublicKey()

const passkey: PasskeyArgType = {
rawId,
publicKey
}
```

At this point, it's critical to securely store the information in the `passkey` object in a persistent service. Losing access to this data will result in the user being unable to access their passkey and, therefore, their Safe Smart Account.

### Initialize the Safe4337Pack

Once the passkey is created and secured, we can use the `Safe4337Pack` class exported from the Relay Kit to create, sign, and submit Safe user operations.

To instantiate this class, the static `init()` method allows connecting existing Safe accounts (as long as they have the `Safe4337Module` enabled) or setting a custom configuration to deploy a new Safe account at the time where the first Safe transaction is submitted. For this guide, we will deploy a new Safe account, configure the paymaster options to get all the transactions sponsored and connect our passkey to add it as the only owner.

```typescript
const PIMLICO_API_KEY = // ...
const RPC_URL = 'https://rpc.ankr.com/eth_sepolia'

const safe4337Pack = await Safe4337Pack.init({
provider: RPC_URL,
rpcUrl: RPC_URL,
signer: passkey,
bundlerUrl: `https://api.pimlico.io/v1/sepolia/rpc?apikey=${PIMLICO_API_KEY}`,
paymasterOptions: {
isSponsored: true,
paymasterUrl: `https://api.pimlico.io/v2/sepolia/rpc?apikey=${PIMLICO_API_KEY}`,
paymasterAddress: '0x...',
paymasterTokenAddress: '0x...',
sponsorshipPolicyId // Optional value to set the sponsorship policy id from Pimlico
},
options: {
owners: [],
threshold: 1
}
})
```

### Create a user operation

To create a Safe user operation, use the `createTransaction()` method, which takes the array of transactions to execute and returns a `SafeOperation` object.

```typescript
// Define the transactions to execute
const transaction1 = { to, data, value }
const transaction2 = { to, data, value }

// Build the transaction array
const transactions = [transaction1, transaction2]

// Create the SafeOperation with all the transactions
const safeOperation = await safe4337Pack.createTransaction({ transactions })
```

The `safeOperation` object has the `data` and `signatures` properties, which contain all the information about the transaction batch and the signatures of the Safe owners, respectively.

### Sign a user operation

Before sending the user operation to the bundler, the `safeOperation` object must be signed with the connected passkey. The user is now requested to authenticate with the associated device and sign in with a biometric sensor, PIN, or gesture.

The `signSafeOperation()` method, which receives a `SafeOperation` object, generates a signature that will be checked when the `Safe4337Module` validates the user operation.

```typescript
const signedSafeOperation = await safe4337Pack.signSafeOperation(
safeTransaction
)
```

### Submit the user operation

Once the `safeOperation` object is signed with the passkey, we can call the `executeTransaction()` method to submit the user operation to the bundler.

```typescript
const userOperationHash = await safe4337Pack.executeTransaction({
executable: signedSafeOperation
})
```

### Check the transaction status

To check the transaction status, we can use the `getTransactionReceipt()` method, which returns the transaction receipt after it's executed.

```typescript
let userOperationReceipt = null

while (!userOperationReceipt) {
// Wait 2 seconds before checking the status again
await new Promise((resolve) => setTimeout(resolve, 2000))
userOperationReceipt = await safe4337Pack.getUserOperationReceipt(
userOperationHash
)
}
```

In addition, we can use the `getUserOperationByHash()` method with the returned hash to retrieve the user operation object we sent to the bundler.

```typescript
const userOperationPayload = await safe4337Pack.getUserOperationByHash(
userOperationHash
)
```

## Recap and further reading

After following this guide, we are able to deploy a new ERC-4337 compatible Safe Smart Account setup with a passkey and create, sign, and execute Safe transactions signing them with the passkey. Learn more about passkeys and how Safe supports them in detail by following these links:

- [Safe\{Core\} SDK demo](https://github.com/5afe/passkey-sdk-demo-dapp)
- [Safe Passkeys contracts](https://github.com/safe-global/safe-modules/tree/main/modules/passkey)
54 changes: 54 additions & 0 deletions pages/home/passkeys-overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Grid } from '@mui/material'
import CustomCard from '../../components/CustomCard'

# What are passkeys?

Passkeys are a standard authentication method designed to avoid using traditional passwords, providing a more secure and user-friendly experience.

Passkeys are based on public and private key pairs to secure user authentication. The public key is stored on the server side, while the private key is secured in the user's device. The user is authenticated by proving ownership of the private key, usually with biometric sensors, without extracting it from the device at any time. This method ensures that sensitive information remains protected and reduces the risk of credential theft.

## Why do we need passkeys?

Passkeys offer significant security improvements over traditional passwords. In the context of web3, where secure key management is paramount, passkeys provide an efficient alternative to seed phrases, which are often considered both a security liability and a subpar user experience.

<Grid
container
spacing={2}
display='flex'
alignContent='flex-start'
mt={3}
>
<Grid item xs={12} md={4}>
<CustomCard
title={'Key Management'}
description={'Seed phrases were already an improvement compared to storing plain-text private keys. It must be stored securely - offline or on a secure device - which can be challenging for most users. Passkeys, conversely, eliminate the need for users to remember complex seed phrases. They ensure the user \'s private key remains secure even if a server is compromised.'}
url=""
newTab={false}
/>
</Grid>
<Grid item xs={12} md={4}>
<CustomCard
title={'User Experience'}
description={'Passkeys allow users to sign in to accounts with a biometric sensor, PIN, or gesture, streamlining the authentication process. Since users don\'t have to type anything - neither passwords nor usernames - the authentication flow becomes seamless, with little to no interruption in the user journey, often leading to better conversion and increased usage of the service implementing it.'}
url=""
newTab={false}
/>
</Grid>
<Grid item xs={12} md={4}>
<CustomCard
title={'Resilience'}
description={'Passkeys are stored in a device secure element, which ensures that the data they store can not be easily accessible to the internet or the rest of the outside world. However, since passkeys can be synced across multiple devices, the security and access to a given service are not tied to a specific smartphone or computer.'}
url=""
newTab={false}
/>
</Grid>
</Grid>

Safe offers the capability to sign into your wallet using passkeys by implementing a dedicated module that verifies the integrity of the key provided.

## Further reading

- [Passkeys.io demo](https://www.passkeys.io)
- [The official W3C standard](https://www.w3.org/TR/webauthn)
- [WebAuthn API specification](https://webauthn.wtf/how-it-works/basics)
- [Passkeys 101 by FIDO Alliance](https://fidoalliance.org/passkeys) (the original authors of the specification)
Empty file added pages/home/passkeys-safe.mdx
Empty file.
1 change: 1 addition & 0 deletions pages/home/passkeys-supported-networks.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Supported Networks

0 comments on commit 7a2ebe4

Please sign in to comment.