Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions docs/pages/wallets/auth/email-otp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import { useSendOTP, useVerifyOTP } from '@zerodev/wallet-react'
function EmailOTPAuth() {
const [email, setEmail] = useState('')
const [code, setCode] = useState('')
const [otpId, setOtpId] = useState<string | null>(null)
const [otp, setOtp] = useState<{
otpId: string
otpEncryptionTargetBundle: string
} | null>(null)

const { address, isConnected } = useAccount()
const { disconnectAsync } = useDisconnect()
Expand All @@ -37,7 +40,7 @@ function EmailOTPAuth() {
)
}

if (!otpId) {
if (!otp) {
return (
<div>
<input
Expand All @@ -49,7 +52,7 @@ function EmailOTPAuth() {
<button
onClick={async () => {
const result = await sendOTP.mutateAsync({ email })
setOtpId(result.otpId)
setOtp(result)
}}
disabled={sendOTP.isPending || !email}
>
Expand All @@ -70,7 +73,7 @@ function EmailOTPAuth() {
onChange={(e) => setCode(e.target.value)}
/>
<button
onClick={() => verifyOTP.mutateAsync({ code, otpId })}
onClick={() => verifyOTP.mutateAsync({ code, ...otp })}
disabled={verifyOTP.isPending || !code}
>
{verifyOTP.isPending ? 'Verifying...' : 'Verify Code'}
Expand All @@ -83,8 +86,8 @@ function EmailOTPAuth() {

## How it works

1. **Send code**: `useSendOTP` sends a one-time verification code to the provided email address. It returns an `otpId` that identifies this verification attempt.
1. **Send code**: `useSendOTP` sends a one-time verification code to the provided email address. It returns an `otpId` and `otpEncryptionTargetBundle` that identify and bind this verification attempt.

2. **Verify code**: `useVerifyOTP` takes the `otpId` and the code the user entered. If the code is valid, the SDK authenticates and creates a session.
2. **Verify code**: `useVerifyOTP` takes the `otpId`, `otpEncryptionTargetBundle`, and the code the user entered. If the code is valid, the SDK authenticates and creates a session.

After verification, the Wagmi connector is connected and the user's address is available via `useAccount`.
12 changes: 6 additions & 6 deletions docs/pages/wallets/auth/google-oauth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
This page covers the web flow. For Expo / React Native, see [Google OAuth on React Native](/wallets/react-native/google-oauth).
:::

Google OAuth lets users sign in with their Google account using a popup-based flow. The SDK handles the popup, redirect, and session creation automatically.
Google OAuth lets users sign in with their Google account using a popup-based flow. The SDK handles the popup, redirect back to your app, and session creation automatically.

By default, the Google OAuth flow uses a Zerodev-managed Google account for authentication. This allows you to get started quickly without any additional configuration. You can customize the OAuth flow by providing your own Google OAuth client ID and secret in the developer dashboard.

Expand Down Expand Up @@ -66,14 +66,14 @@ function GoogleAuth() {

## How it works

1. **Open popup**: `useAuthenticateOAuth` opens a popup window to the KMS backend's OAuth endpoint.
1. **Request login URL**: `useAuthenticateOAuth` asks the backend for a Google OAuth login URL tied to the wallet's current public key.

2. **Google sign-in**: The backend initiates the Google OAuth flow with PKCE. The user signs in with their Google account.
2. **Verify login URL**: Before opening the popup, the SDK verifies the returned Google login URL so the OAuth flow stays bound to the expected wallet public key.

3. **Backend callback**: Google redirects back to the backend, which exchanges the auth code for tokens and sets a session cookie.
3. **Open popup**: The SDK opens a popup to that verified Google login URL and polls the popup while the user signs in with Google.

4. **Popup redirect**: The backend redirects the popup to your app with `?oauth_success=true`.
4. **Popup redirect**: After a successful sign-in, the popup returns to your app's origin with `?oauth_success=true&session_id=...`.

5. **Auto-detect**: The SDK detects the success parameter. If running in a popup, it sends a `postMessage` to the opener window and closes. The main window then calls the backend's auth endpoint (reading the session cookie) to get a session.
5. **Complete auth**: The SDK reads `session_id` from the popup URL, completes wallet authentication with it, and then connects the Wagmi connector.

After the flow completes, the Wagmi connector is connected and the user's address is available via `useAccount`.
17 changes: 12 additions & 5 deletions docs/pages/wallets/auth/magic-link.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ function MagicLinkLogin() {
email,
redirectURL: `${window.location.origin}/verify`,
})
// Store otpId for the verify page
// Store both values for the verify page
sessionStorage.setItem('magicLinkOtpId', result.otpId)
sessionStorage.setItem(
'magicLinkOtpEncryptionTargetBundle',
result.otpEncryptionTargetBundle,
)
}}
disabled={sendMagicLink.isPending || !email}
>
Expand Down Expand Up @@ -78,9 +82,12 @@ function VerifyMagicLink() {
useEffect(() => {
const code = searchParams.get('code')
const otpId = sessionStorage.getItem('magicLinkOtpId')
const otpEncryptionTargetBundle = sessionStorage.getItem(
'magicLinkOtpEncryptionTargetBundle',
)

if (code && otpId && !isConnected) {
verifyMagicLink.mutateAsync({ otpId, code })
if (code && otpId && otpEncryptionTargetBundle && !isConnected) {
verifyMagicLink.mutateAsync({ otpId, code, otpEncryptionTargetBundle })
}
}, [searchParams])

Expand All @@ -102,10 +109,10 @@ function VerifyMagicLink() {

## How it works

1. **Send link**: `useSendMagicLink` sends an email with a magic link pointing to your `redirectURL`. It returns an `otpId` that you need for verification.
1. **Send link**: `useSendMagicLink` sends an email with a magic link pointing to your `redirectURL`. It returns an `otpId` and `otpEncryptionTargetBundle` that you need for verification.

2. **User clicks link**: The user clicks the link in their email, which redirects them to your app with a `code` query parameter.

3. **Verify**: Your verify page calls `useVerifyMagicLink` with the `otpId` and `code`. If valid, the SDK authenticates and creates a session.
3. **Verify**: Your verify page calls `useVerifyMagicLink` with the `otpId`, `otpEncryptionTargetBundle`, and `code`. If valid, the SDK authenticates and creates a session.

After verification, the Wagmi connector is connected and the user's address is available via `useAccount`.
145 changes: 145 additions & 0 deletions docs/pages/wallets/connector-options.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Connector Options [Configure the zeroDevWallet connector]

:::danger[IMPORTANT]
**USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time.
:::

The `zeroDevWallet` connector is configured through the options below. Only `projectId` and `chains` are required — on the web, everything else falls back to a sensible default.

```tsx
import { zeroDevWallet } from '@zerodev/wallet-react'
import { sepolia } from 'wagmi/chains'

zeroDevWallet({
projectId: 'YOUR_ZERODEV_PROJECT_ID',
chains: [sepolia],
})
```

## Options

### projectId

`string`

**Required.** Your ZeroDev project ID, from the [ZeroDev Dashboard](https://dashboard.zerodev.app/).

### chains

`readonly Chain[]`

**Required.** The viem/wagmi chains the wallet supports. Usually the same array you pass to Wagmi's `createConfig`.

### mode

`'EOA' | '4337' | '7702'`

The account mode exposed to Wagmi. Defaults to `'7702'`.

- `'7702'` — An EOA delegated to a Kernel smart account via [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702). The address equals the EOA address; transactions are bundled as UserOperations and can be sponsored.
- `'4337'` — A counterfactual [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) Kernel smart account. The address differs from the EOA and the account is deployed on the first UserOperation; transactions are bundled as UserOperations.
- `'EOA'` — A plain EOA. Transactions are sent directly via the chain's RPC, with no bundler and no sponsorship.

### rpId

`string | undefined`

The relying party ID — the domain used for [passkeys](/wallets/auth/passkeys#configure-rp-id-optional), as defined by the [WebAuthn standard](https://web.dev/articles/webauthn-rp-id). On the web it defaults to `window.location.hostname`, so you only need to set it to share passkeys across subdomains.

### autoRefreshSession

`boolean | undefined`

Whether the SDK refreshes the session before it expires. Defaults to `true`. See [Session Management](/wallets/session-management#auto-refresh-configuration).

### sessionWarningThreshold

`number | undefined`

How long before expiry (in milliseconds) the session is refreshed when `autoRefreshSession` is enabled. Defaults to `60000` (60 seconds).

### autoInitialize

`boolean | (() => boolean) | undefined`

Whether the connector initializes automatically on mount. When `false`, the connector still initializes lazily on connect. Accepts a function for conditional initialization.

### sessionStorage

`StorageAdapter | undefined`

Storage adapter for the wallet SDK's session backend. This is where the auth/session layer stores its session records. On the web it defaults to the browser's `localStorage`. Provide a custom [`StorageAdapter`](/wallets/session-management#custom-storage) for alternative storage.

### persistStorage

`CreateStoreOptions['storage'] | undefined`

Storage used to persist the connector store across page reloads. This is a separate layer from `sessionStorage`. If omitted, the connector store uses its own default web persistence behavior rather than automatically reusing `sessionStorage`.

### Storage model on web

There are two storage layers on the web:

- `sessionStorage` stores the wallet SDK's auth/session data.
- `persistStorage` stores the ZeroDev connector's persisted state for rehydration.

Most apps can leave both unset and use the built-in web defaults:

```tsx
zeroDevWallet({
projectId: 'YOUR_ZERODEV_PROJECT_ID',
chains: [sepolia],
})
```

If you want both layers to use the same browser storage implementation, set them both explicitly:

```tsx
const browserStorage = {
getItem: (key: string) => window.localStorage.getItem(key),
setItem: (key: string, value: string) =>
window.localStorage.setItem(key, value),
removeItem: (key: string) => window.localStorage.removeItem(key),
}

zeroDevWallet({
projectId: 'YOUR_ZERODEV_PROJECT_ID',
chains: [sepolia],
sessionStorage: browserStorage,
persistStorage: browserStorage,
})
```

### apiKeyStamper

`ApiKeyStamper | Promise<ApiKeyStamper> | undefined`

Advanced. Signs the SDK's requests with an API key. On the web it defaults to an IndexedDB-backed stamper.

### passkeyStamper

`PasskeyStamper | Promise<PasskeyStamper> | undefined`

Advanced. Signs passkey operations. On the web it defaults to a WebAuthn stamper.

### organizationId

`string | undefined`

Advanced. Overrides the sub-organization the SDK operates against.

### proxyBaseUrl

`string | undefined`

Advanced. Base URL for a custom backend proxy in front of the ZeroDev wallet API.

### aaUrl

`string | undefined`

Advanced. Overrides the account-abstraction bundler/paymaster URL.

## React Native

On React Native there are no platform defaults, so `rpId`, `apiKeyStamper`, and `sessionStorage` are **required** and configured through the adapters the SDK ships for Expo. See [React Native Configuration](/wallets/react-native/configuration).
87 changes: 87 additions & 0 deletions docs/pages/wallets/hooks/use-authenticators.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import QueryResult from '../shared/query-result.mdx'

# useAuthenticators [Hook for getting the authenticated user's linked authenticators]

:::danger[IMPORTANT]
**USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time.
:::

## Import

```tsx
import { useAuthenticators } from '@zerodev/wallet-react'
```

## Usage

```tsx
import { useAuthenticators } from '@zerodev/wallet-react'
import { useAccount } from 'wagmi'

function UserProfile() {
const { isConnected } = useAccount()
const { data, isLoading } = useAuthenticators()

if (!isConnected) return null
if (isLoading) return <p>Loading authenticators...</p>

const email = data?.emailContacts?.[0]?.email

return <p>Email: {email ?? 'Not available'}</p>
}
```

## Parameters

```tsx
import { type Config } from 'wagmi'
import { type UseQueryOptions } from '@tanstack/react-query'
```

### config

`Config | undefined`

Optional Wagmi config. If omitted, the hook uses the nearest Wagmi config from context.

### query

`Omit<UseQueryOptions<GetAuthenticatorsReturnType>, 'queryKey' | 'queryFn'> | undefined`

Optional TanStack Query options for the authenticators query.

## Return Types

[TanStack Query docs](https://tanstack.com/query/v5/docs/react/reference/useQuery)

### data

`GetAuthenticatorsReturnType | undefined`

The authenticated user's linked authenticators, grouped by type. The query requires an active ZeroDev wallet session.

#### oauths

`OAuthAuthenticator[] | null`

OAuth identities linked to the user.

#### passkeys

`PasskeyAuthenticator[] | null`

Passkey authenticators registered for the user.

#### emailContacts

`EmailContact[] | null`

Email contacts associated with the user. This is where email OTP and Magic Link addresses appear.

#### apiKeys

`ApiKeyAuthenticator[] | null`

API-key authenticators associated with the user.

<QueryResult />
Loading