From 6e77bbc6b6ad622d7d54df8768c0d7106f19526f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 25 Nov 2024 16:44:16 +0100 Subject: [PATCH 1/6] Update passkeys guide --- pages/sdk/signers/passkeys.mdx | 158 +++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 66 deletions(-) diff --git a/pages/sdk/signers/passkeys.mdx b/pages/sdk/signers/passkeys.mdx index ca5be6e6..cdd5b5ca 100644 --- a/pages/sdk/signers/passkeys.mdx +++ b/pages/sdk/signers/passkeys.mdx @@ -4,7 +4,9 @@ import { Callout, Steps, Tabs } from 'nextra/components' In this guide, you will learn how to create a Passkey signer that can be added as a Safe owner and used to initialize any of the kits from the Safe\{Core\} SDK. -**Note:** Please always use a combination of passkeys and other authentication methods to ensure the security of your users' assets. + + Please always use a combination of passkeys and other authentication methods to ensure the security of your users' assets. + ## Prerequisites @@ -39,101 +41,125 @@ In this guide, you will learn how to create a Passkey signer that can be added a - ### Imports +### Imports - Here are the necessary imports for this guide. +Here are the necessary imports for this guide. - {/* */} +{/* */} - ```typescript - import { extractPasskeyData } from '@safe-global/protocol-kit' - ``` +```typescript +import Safe from '@safe-global/protocol-kit' +``` - {/* */} +{/* */} - In addition, you will need to import a web3 library of your choice to use in the "Get the provider and signer" section. In this guide, we are using `viem`. +In addition, you will need to import a web3 library of your choice to use in the "Get the provider and signer" section. In this guide, we are using `viem`. - ### Create a passkey +### Create a passkey - Firstly, you need to generate a passkey credential using the WebAuthn API in a supporting browser environment. +Firstly, you need to generate a passkey credential using the [WebAuthn API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_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' - } - }) - ``` +```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, you need to create a new object with the `PasskeyArgType` type that will contain the `rawId` and the `coordinates` information. +After generating the `passkeyCredential` object, you need to create the signer. This signer will be an `PasskeyArgType` object containing the `rawId` and the `coordinates` information. - {/* */} +{/* */} - ```typescript - if (!passkeyCredential) { - throw Error('Passkey creation failed: No credential was returned.') - } +```typescript +if (!passkeyCredential) { + throw Error('Passkey creation failed: No credential was returned.') +} - const passkey = await extractPasskeyData(passkeyCredential) - ``` +const passkeySigner = await Safe.createPasskeySigner(passkeyCredential) +``` - {/* */} +{/* */} - 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. +At this point, it's critical to securely store the information in the `passkeySigner` 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. - ### Get the provider and signer +### Get the provider and signer - Once the passkey is created, you can get the `provider` and `signer`, which is the externally-owned account of the user that was derived from its credentials. + Once the passkey is created, you need the `provider` and `signer` props that the Safe\{Core\} SDK require to instantiate the kits. + [How to initialize the Protocol kit](https://docs.safe.global/reference-sdk-protocol-kit/initialization/init) + + The `provider` (eip1193 compatible or rpc) can be obtained by using `viem` in several ways: {/* */} - + - You can instantiate the provider using `viem` and the following imports: - - ```typescript - import { createWalletClient, http } from 'viem' - import { sepolia } from 'viem/chains' - ``` + ```typescript + import { createWalletClient, http } from 'viem' + import { sepolia } from 'viem/chains' - ```typescript - const provider = createWalletClient({ - chain: sepolia, - transport: http('https://rpc.ankr.com/eth_sepolia') - }) + const walletClient = createWalletClient({ + chain: sepolia, + transport: http('https://rpc.ankr.com/eth_sepolia') + }) - const signer = passkey + const provider = { request: walletClient.request } + const signer = passkeySigner ``` + + + ```typescript + import { sepolia } from 'viem/chains' + + const provider = sepolia.rpcUrls.default.http[0] + const signer = passkeySigner + ``` {/* */} - With the `provider` and `signer` you are ready to instantiate any of the kits from the Safe\{Core\} SDK and set up or use this signer as a Safe owner. +### Instantiate SDK + +With the `provider` and `signer` you are ready to instantiate any of the kits from the Safe\{Core\} SDK and set up or use this signer as a Safe owner. + +For example, you can instantiate the `protocol-kit` as follows and sign a transaction with the passkey signer: + +{/* */} + +```typescript +const protocolKit = await Safe.init({ provider, signer, safeAddress }) + +const transaction = { to: '0x1234', value: '0x0', data: '0x' } +const safeTransaction = await protocolKit.createTransaction({ transactions: [transaction] }) +const signedSafeTransaction = await protocolKit.signTransaction(safeTransaction) +``` + +{/* */} From 8d1417fa67e26cc2fd49ec0e59f24051ec44ed15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 25 Nov 2024 17:04:29 +0100 Subject: [PATCH 2/6] Update protocol-kit guide --- pages/sdk/protocol-kit/reference/safe.mdx | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pages/sdk/protocol-kit/reference/safe.mdx b/pages/sdk/protocol-kit/reference/safe.mdx index 61589ee5..70480dd7 100644 --- a/pages/sdk/protocol-kit/reference/safe.mdx +++ b/pages/sdk/protocol-kit/reference/safe.mdx @@ -1081,3 +1081,32 @@ const signedMessage = await protocolKit.signMessage(signedMessage, SigningMethod const signedMessage = await protocolKit.signMessage(signedMessage, SigningMethod.ETH_SIGN) const signedMessage = await protocolKit.signMessage(signedMessage, SigningMethod.SAFE_SIGNATURE, parentSafeAddress). ``` + +## Passkeys + +### `createPasskeySigner` + +Creates a new passkey signer using a [WebAuthn credential](https://developer.mozilla.org/en-US/docs/Web/API/Credential). + +```typescript +const rpcUrl = "https://..." +const credential = window.navigator.credentials.create({ ... }) + +const passkeySigner = await Safe.createPasskeySigner(credential) + +const protocolKit = await Safe.init({ + provider: rpcURL, + signer: passkeySigner, + safeAddress +}) +``` + +Return an object of type `PasskeyArgType` that should be stored securely containing: + +- `rawId`: The rawId of the credential. +- `coordinates`: The coordinates of the credential. The coordinates are used to sign using Safe smart contracts + +You can add these optional properties to the resulting object before instantiating the protocol kit: +- `customVerifierAddress`: The custom verifier address. This is optional and only needed if the Safe is using a custom verifier. +- `getFn`: The function used to obtain the credential. This is optional and only necessary if you are creating the credential in environments where the WebAuthN API does not exist, such as React Native. + From 1256db70282f7eedb7fa6414eb82479b2cfa006b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 25 Nov 2024 17:06:25 +0100 Subject: [PATCH 3/6] Vale --- pages/sdk/protocol-kit/reference/safe.mdx | 2 +- pages/sdk/signers/passkeys.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/sdk/protocol-kit/reference/safe.mdx b/pages/sdk/protocol-kit/reference/safe.mdx index 70480dd7..b37a9424 100644 --- a/pages/sdk/protocol-kit/reference/safe.mdx +++ b/pages/sdk/protocol-kit/reference/safe.mdx @@ -1103,7 +1103,7 @@ const protocolKit = await Safe.init({ Return an object of type `PasskeyArgType` that should be stored securely containing: -- `rawId`: The rawId of the credential. +- `rawId`: The `rawId` of the credential. - `coordinates`: The coordinates of the credential. The coordinates are used to sign using Safe smart contracts You can add these optional properties to the resulting object before instantiating the protocol kit: diff --git a/pages/sdk/signers/passkeys.mdx b/pages/sdk/signers/passkeys.mdx index cdd5b5ca..4f39fd2c 100644 --- a/pages/sdk/signers/passkeys.mdx +++ b/pages/sdk/signers/passkeys.mdx @@ -111,7 +111,7 @@ At this point, it's critical to securely store the information in the `passkeySi Once the passkey is created, you need the `provider` and `signer` props that the Safe\{Core\} SDK require to instantiate the kits. - [How to initialize the Protocol kit](https://docs.safe.global/reference-sdk-protocol-kit/initialization/init) + [How to initialize the Protocol Kit](https://docs.safe.global/reference-sdk-protocol-kit/initialization/init) The `provider` (eip1193 compatible or rpc) can be obtained by using `viem` in several ways: {/* */} From 112306b73fd46f0d54c8249f3e0445de71bfc946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 25 Nov 2024 17:07:12 +0100 Subject: [PATCH 4/6] Vale --- pages/sdk/protocol-kit/reference/safe.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/sdk/protocol-kit/reference/safe.mdx b/pages/sdk/protocol-kit/reference/safe.mdx index b37a9424..2ac89036 100644 --- a/pages/sdk/protocol-kit/reference/safe.mdx +++ b/pages/sdk/protocol-kit/reference/safe.mdx @@ -1106,7 +1106,7 @@ Return an object of type `PasskeyArgType` that should be stored securely contain - `rawId`: The `rawId` of the credential. - `coordinates`: The coordinates of the credential. The coordinates are used to sign using Safe smart contracts -You can add these optional properties to the resulting object before instantiating the protocol kit: +You can add these optional properties to the resulting object before instantiating the Protocol Kit: - `customVerifierAddress`: The custom verifier address. This is optional and only needed if the Safe is using a custom verifier. - `getFn`: The function used to obtain the credential. This is optional and only necessary if you are creating the credential in environments where the WebAuthN API does not exist, such as React Native. From cd7761e4a0bbe62419f749b84fe0eca3d1cd09f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 25 Nov 2024 17:12:48 +0100 Subject: [PATCH 5/6] add some tabs --- pages/sdk/signers/passkeys.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pages/sdk/signers/passkeys.mdx b/pages/sdk/signers/passkeys.mdx index 4f39fd2c..15693a6a 100644 --- a/pages/sdk/signers/passkeys.mdx +++ b/pages/sdk/signers/passkeys.mdx @@ -41,17 +41,17 @@ In this guide, you will learn how to create a Passkey signer that can be added a -### Imports + ### Imports -Here are the necessary imports for this guide. + Here are the necessary imports for this guide. -{/* */} + {/* */} -```typescript -import Safe from '@safe-global/protocol-kit' -``` + ```typescript + import Safe from '@safe-global/protocol-kit' + ``` -{/* */} + {/* */} In addition, you will need to import a web3 library of your choice to use in the "Get the provider and signer" section. In this guide, we are using `viem`. From fcd9b568d46ccf0e701ca5aaa07776774bb0c9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yago=20P=C3=A9rez=20V=C3=A1zquez?= Date: Mon, 25 Nov 2024 17:14:29 +0100 Subject: [PATCH 6/6] Some tabs --- pages/sdk/signers/passkeys.mdx | 116 ++++++++++++++++----------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/pages/sdk/signers/passkeys.mdx b/pages/sdk/signers/passkeys.mdx index 15693a6a..d0f99065 100644 --- a/pages/sdk/signers/passkeys.mdx +++ b/pages/sdk/signers/passkeys.mdx @@ -53,61 +53,61 @@ In this guide, you will learn how to create a Passkey signer that can be added a {/* */} -In addition, you will need to import a web3 library of your choice to use in the "Get the provider and signer" section. In this guide, we are using `viem`. + In addition, you will need to import a web3 library of your choice to use in the "Get the provider and signer" section. In this guide, we are using `viem`. -### Create a passkey + ### Create a passkey -Firstly, you need to generate a passkey credential using the [WebAuthn API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API) in a supporting browser environment. + Firstly, you need to generate a passkey credential using the [WebAuthn API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_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' - } -}) -``` + ```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, you need to create the signer. This signer will be an `PasskeyArgType` object containing the `rawId` and the `coordinates` information. + After generating the `passkeyCredential` object, you need to create the signer. This signer will be an `PasskeyArgType` object containing the `rawId` and the `coordinates` information. -{/* */} + {/* */} -```typescript -if (!passkeyCredential) { - throw Error('Passkey creation failed: No credential was returned.') -} + ```typescript + if (!passkeyCredential) { + throw Error('Passkey creation failed: No credential was returned.') + } -const passkeySigner = await Safe.createPasskeySigner(passkeyCredential) -``` + const passkeySigner = await Safe.createPasskeySigner(passkeyCredential) + ``` -{/* */} + {/* */} -At this point, it's critical to securely store the information in the `passkeySigner` 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. + At this point, it's critical to securely store the information in the `passkeySigner` 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. -### Get the provider and signer + ### Get the provider and signer Once the passkey is created, you need the `provider` and `signer` props that the Safe\{Core\} SDK require to instantiate the kits. @@ -143,25 +143,25 @@ At this point, it's critical to securely store the information in the `passkeySi {/* */} -### Instantiate SDK - -With the `provider` and `signer` you are ready to instantiate any of the kits from the Safe\{Core\} SDK and set up or use this signer as a Safe owner. - -For example, you can instantiate the `protocol-kit` as follows and sign a transaction with the passkey signer: - -{/* */} + ### Instantiate SDK -```typescript -const protocolKit = await Safe.init({ provider, signer, safeAddress }) + With the `provider` and `signer` you are ready to instantiate any of the kits from the Safe\{Core\} SDK and set up or use this signer as a Safe owner. -const transaction = { to: '0x1234', value: '0x0', data: '0x' } -const safeTransaction = await protocolKit.createTransaction({ transactions: [transaction] }) -const signedSafeTransaction = await protocolKit.signTransaction(safeTransaction) -``` + For example, you can instantiate the `protocol-kit` as follows and sign a transaction with the passkey signer: -{/* */} + {/* */} - + ```typescript + const protocolKit = await Safe.init({ provider, signer, safeAddress }) + + const transaction = { to: '0x1234', value: '0x0', data: '0x' } + const safeTransaction = await protocolKit.createTransaction({ transactions: [transaction] }) + const signedSafeTransaction = await protocolKit.signTransaction(safeTransaction) + ``` + + {/* */} + + ## Recap and further reading