Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

createOneAuthConnection gives the host application one connection lifecycle for two distinct signer types:

  • an app-origin passkey smart account authenticated by OneAuthClient; or
  • an external EIP-1193 wallet selected and connected by the host application's wallet UI.

The host owns the choice. The SDK calls selectConnection() every time connect() runs and opens only the selected branch. It never tries the other branch after cancellation or failure.

Live preview

This demo calls the public wallet_connect provider method. It opens an explicit choice between the user's 1auth passkey account and an external wallet.

Choose a passkey or external wallet

Use eth_requestAccounts instead for the normal passkey-only authentication flow. Use eth_accounts to passively read the currently authorized account without opening any connection UI.

Complete example

This example uses a simple host-owned selector and an injected wallet such as MetaMask or Rabby. In a production application, replace window.confirm with your own account selector and injected-provider discovery UI.

import {
  createOneAuthConnection,
  type OneAuthConnectionSelection,
} from '@rhinestone/1auth';
import type { EIP1193Provider } from 'viem';
 
declare global {
  interface Window {
    ethereum?: EIP1193Provider;
  }
}
 
async function selectConnection(): Promise<OneAuthConnectionSelection> {
  return window.confirm('Use your app passkey?')
    ? 'passkey'
    : 'external_wallet';
}
 
const connection = createOneAuthConnection({
  clientId: 'my-app',
  selectConnection,
  connectExternalWallet: async () => {
    const provider = window.ethereum;
    if (!provider) throw new Error('No injected wallet found');
 
    const accounts = await provider.request({
      method: 'eth_requestAccounts',
    }) as `0x${string}`[];
    const chainIdHex = await provider.request({
      method: 'eth_chainId',
    }) as string;
 
    if (!accounts[0]) throw new Error('The wallet returned no account');
 
    return {
      provider,
      address: accounts[0],
      chainId: Number.parseInt(chainIdHex, 16),
    };
  },
});
 
const session = await connection.connect();
 
if (session.signerType === 'passkey') {
  // App-origin 1auth smart account. Sends route through 1auth intents.
  console.log(session.accountAddress);
} else {
  // Host-connected EOA. Requests route to the selected external wallet.
  console.log(session.chainId);
}
 
// Use the returned wrapper for both session types, not the injected provider.
await session.provider.request({
  method: 'eth_sendTransaction',
  params: [{
    from: session.accountAddress,
    to: '0x1111111111111111111111111111111111111111',
    value: '0x1',
  }],
});

connectExternalWallet() must return the provider, selected address, and current numeric chain ID:

interface OneAuthExternalWalletConnection {
  provider: EIP1193Provider;
  address: `0x${string}`;
  chainId: number;
}

The provider returned in the session is the 1auth EIP-1193 wrapper, not the raw injected provider. Keep using the session provider so routing remains bound to the selected signer and account.

Session union

connect() and getSession() return a discriminated union:

type OneAuthConnectionSession =
  | {
      signerType: 'passkey';
      accountAddress: `0x${string}`;
      provider: OneAuthProvider;
      chainId: number;
      autoConnected: boolean;
    }
  | {
      signerType: 'eoa';
      accountAddress: `0x${string}`;
      provider: OneAuthProvider;
      chainId: number;
      autoConnected: boolean;
    };

Branch on signerType, not on the address shape. Both variants use an Ethereum address, but only the passkey variant sends transactions through 1auth intents. The EOA variant forwards supported account, signing, transaction, and chain requests to the host-connected external provider.

New sessions returned by connect() have autoConnected: false. At construction time, getSession() can restore a persisted app-origin passkey identity for the configured storageKey; external-wallet provider connections are host-owned and are not reconstructed from passkey storage.

Request routing and account safety

The active session determines all later provider routing:

  • Passkey signing and transaction requests use the app-origin passkey account and 1auth intent path.
  • EOA requests use the external provider returned by connectExternalWallet().
  • eth_accounts exposes only the selected account.
  • External wallet account, chain, and disconnect events are mirrored through the returned wrapper.

For methods that name a signer, including personal_sign, typed-data signing, eth_sendTransaction, and wallet_sendCalls, the SDK fills a missing signer with session.accountAddress. If a request explicitly supplies a different address, it rejects with ACCOUNT_MISMATCH instead of forwarding the request to the wallet.

Reconnect, switch, and disconnect

Call connect() again whenever the user asks to connect or switch accounts. selectConnection() runs again, so the host can choose either branch:

const unsubscribe = connection.subscribe((session) => {
  // A new session is published after a successful connect or switch.
  // null is published after disconnect.
  renderConnection(session);
});
 
const nextSession = await connection.connect();
 
await connection.disconnect();
unsubscribe();

A successful switch replaces the active signer before connect() resolves. Switching away from a passkey clears its provider session; switching external wallets clears the previous external routing. Later requests therefore use only the newly selected signer.

disconnect() clears the active passkey or external-wallet routing, removes persisted passkey connection state, mirrors provider disconnect events, and publishes null to subscribers. Calling it with no active session is a no-op.

Cancellation and failure

selectConnection() and connectExternalWallet() are host callbacks, so they can reject with the host's cancellation error. A selector result other than 'passkey' or 'external_wallet' is rejected with code USER_CANCELLED. An invalid external connection is rejected with code EXTERNAL_WALLET_INVALID.

Cancellation or failure leaves the previous successful session active. The unselected branch is not invoked. For example, a cancelled external-wallet flow does not open passkey authentication, and failed passkey authentication does not call connectExternalWallet().

Configuration

You can construct the client from ordinary OneAuthClient configuration or provide an existing client:

const connection = createOneAuthConnection({
  client: appOriginClient,
  selectConnection,
  connectExternalWallet,
});
OptionTypeRequiredDescription
selectConnection() => 'passkey' | 'external_wallet' | Promise<...>YesHost-owned signer selector, called for every connect().
connectExternalWallet() => OneAuthExternalWalletConnection | Promise<...>YesOpens the host wallet UI and returns its EIP-1193 provider, selected address, and numeric chain ID.
clientOneAuthClientNoReuses an existing client instead of constructing one.
webauthn{ rpId?: string }NoShared RP ID when constructing the client.
defaultChainIdnumberNoChain reported for passkey sessions. Defaults to Base mainnet (8453).
storageKeystringNoStorage key for the app-origin passkey identity. Defaults to "1auth-user".
closeOnCloseOnStatusNoPasskey intent dialog completion status.
waitForHashbooleanNoWhether passkey intent sends wait for a transaction hash.
hashTimeoutMsnumberNoPasskey intent hash polling timeout.
hashIntervalMsnumberNoPasskey intent hash polling interval.