Install
New features and improvements
- Unified provider + modal flows: Built-in UI for Passkey, Wallet, Email/SMS OTP, and OAuth (Google, Apple, Facebook, X, Discord).
- Automatic OAuth redirect handling: The provider completes OAuth redirects on page load; no manual token injection.
- First-class export/import: Secure export/import via Turnkey iframes with simple handlers.
- External wallet connection: Connect MetaMask, Phantom, WalletConnect providers for Ethereum/Solana.
- Session lifecycle management: Auto-refresh with
beforeSessionExpiry
/onSessionExpired
callbacks. - UI customization: Theme overrides, dark mode, border radius/background blur, and modal placement.
Breaking changes
Provider and hooks
Old Approach (Manual SDK) Previously,@turnkey/sdk-react
exposed a TurnkeyProvider
and a custom TurnkeyContext
;
you manually managed multiple clients (passkeyClient
, iframeClient
, walletClient
, indexedDbClient
).
useTurnkey()
)
Use @turnkey/react-wallet-kit
’s TurnkeyProvider
and useTurnkey
hook, which returns state (session
, user
, wallets
, etc.),
high-level helpers (auth, export/import, signing, external wallets), and all TurnkeyClientMethods
from @turnkey/core
.
- Single provider + hook replaces multiple manually managed clients
- High-level helpers for auth/export/import/signing/external wallets
- All core client methods available via the hook
Configuration
Old Approach (Manual SDK iframe config) Previously, auth flows used iframe-specific configuration (e.g.,iframeUrl
) and manual wiring for auth flows. OAuth often required redirect handling and token injection.
New Approach (Hook-Based Provider config)
Use the provider config with auth
, ui
, wallet configuration, and optional export/import iframe URLs. Built-in modal flows replace iframe-specific auth settings.
- Replace iframe-specific auth settings with
auth.*
and built-in modal flows - Centralize OAuth settings via
auth.oauthConfig
; redirect handling is automatic - Configure wallets/chains and UI theming directly on the provider
Authentication
- Manual client selection/injection has been replaced by a single modal-driven flow (
handleLogin
) and dedicated OAuth helpers. Stop callinginjectCredentialBundle
on iframes; the provider manages sessions and completes OAuth automatically.
OAuth
Old Approach (Manual SDK) Previously, you manually parsedcode/state
from the URL, exchanged for a token, and injected credentials into an iframe.
New Approach (Hook-Based: useTurnkey()
)
Use a dedicated helper to trigger the OAuth flow. The provider completes redirects automatically on load.
- No manual URL parsing/token exchange/injection
- Single helper per provider; popup or full-page redirect
- Redirect completion handled automatically by the provider
Passkeys
Old Approach (Manual SDK) Previously, passkey login used a read/write session with a freshly generated public key. Passkey signup first created a WebAuthn credential (challenge + attestation) and then created a new sub-organization/user using that credential.useTurnkey()
)
Use the built-in helpers to trigger passkey login and sign-up flows.
- No manual public key management or session type selection
- WebAuthn challenge/attestation handled by helpers and proxy
- Provider manages session creation and storage
OTP (SMS & Email)
Old Approach (Manual SDK + Server Actions) Previously, OTP auth required:- A server action to initialize OTP (and optionally create a sub-org if none exists) using the parent org API key.
- A server action to verify OTP and exchange the verification token for a session (read/write), stamped by the server key.
- Client code to fetch the device public key and then call those server actions.
useTurnkey()
)
The provider handles proxy calls; you no longer need to stamp server-side with the parent org key, nor manually exchange verification tokens. Client code initializes OTP and completes it with completeOtp
.
Initialize (client):
- Server-stamped flows (parent org API key) are no longer required for standard OTP; the provider’s proxy methods handle the secure exchange.
- You call
proxyInitOtp
andcompleteOtp
directly from the client; the SDK manages session creation and storage. - Optional sub-org creation can be passed via
createSubOrgParams
during completion.
Wallet Authentication (Ethereum/Solana)
Old Approach (Manual SDK + Server Actions) Previously, wallet authentication required:- Configuring Turnkey with a Wallet interface (e.g.,
EthereumWallet
) and wrapping your app with the provider. - Deriving a public key from the user’s external wallet (for Ethereum this involves a signMessage prompt).
- Optionally creating a sub-organization (sign-up) on the server using the parent org API key pair.
- Creating a read/write session via
loginWithWallet
, bound to a browser-managed IndexedDB API key.
useTurnkey()
)
Use the hook-based helpers to trigger wallet authentication flows. The provider abstracts provider discovery, public key derivation, and session creation/storage. A single call will prompt the external wallet for any required signatures and establish a Turnkey session.
Key differences:
- No manual
walletClient.getPublicKey()
or message signing to derive a public key - No
SessionType
or manual IndexedDB session management; the provider manages session lifecycle - One-liners for “Continue with Wallet” (auto sign in or sign up), or explicit Sign Up / Sign In
- Works for Ethereum and Solana; pass
Chain.Ethereum
orChain.Solana
togetWalletProviders
and choose a provider
Passkeys
Add passkey
Before:Remove passkey
Before:Wallets
List wallets
Old Approach (Manual SDK Calls) Previously, wallet listing relied on manually instantiating a Turnkey client and invoking SDK methods to fetch both wallets and their accounts.useTurnkey()
)
The new method utilizes the useTurnkey
React hook, which abstracts data fetching, session management, and provides ready-to-use wallet/account lists and actions.
- No manual wallet/account fetch + merge
- Hook provides wallets and accounts with provider-managed session
- Simpler state consumption via
useTurnkey()
Creating Wallets and Accounts
Old Approach (Manual SDK Calls) Previously, creating wallets and accounts involved calling SDK methods directly (e.g.,createWallet
, createWalletAccounts
) and then refetching wallets to reflect changes.
Additionally, when creating a new Ethereum account you would often compute the next default account at a specific index via a helper (e.g., defaultEthereumAccountAtIndex(index)
) and pass that into createWalletAccounts
.
useTurnkey()
)
Use useTurnkey()
helpers createWallet
and createWalletAccounts
.
The provider manages session and state and refreshing wallets; you only invoke the actions.
Export
Old Approach (Manual SDK + iframe) Previously, exporting required manually initializing an export iframe and orchestrating export API calls and decryption within the iframe.useTurnkey()
)
Use the built-in export handlers that open a modal and perform the export/iframe flow for you.
- Modal handlers replace manual iframe client orchestration
- No direct bundle injection/extraction steps
- Provider/session context handled automatically
Import
Old Approach (Manual SDK + iframe)useTurnkey()
)
Signing
Transactions
Old Approach Previously, transactions were signed usingethers
with TurnkeySigner
from @turnkey/ethers
, wired to the @turnkey/sdk-react
client.
You manually constructed the signer/provider and invoked sendTransaction
.
useTurnkey()
’s signing helpers:
signTransaction
: signs and returns a signature (you broadcast separately).signAndSendTransaction
: signs and broadcasts, returning the on-chain transaction hash.
rpcUrl
is required when using embedded wallets (to broadcast via your chosen RPC).
For external wallets (e.g., MetaMask, Phantom), rpcUrl
is not required and will be ignored.
Key differences:
- No
TurnkeySigner
or manual provider wiring - Pass an unsigned transaction and account; SDK stamps and sends
- Session/state handled by the provider
Messages
Old Approach Messages were signed viaethers
using TurnkeySigner
from @turnkey/ethers
, with manual signer/provider wiring against the @turnkey/sdk-react
client.
useTurnkey()
’s signMessage
to sign directly with a selected wallet account. For a modal-driven UX, handleSignMessage
opens a confirmation dialog.