> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedded wallet password & security

> Protect embedded wallets with a user password — set it during setup, unlock on a new device, and let users change it.

## Prerequisites

Before this page: have an embedded wallet created for the user (see [Post-login user setup](https://docs.dynamic.xyz/docs/javascript/building-ui/post-auth-user-setup.md), which creates the wallet after login).

## What you'll build

When your project protects embedded wallets with a password, you own three moments:

* **Set** the password the first time the wallet is created.
* **Unlock** the wallet when the user returns on a new device or session.
* **Change** the password later from a settings screen.

`isPasswordRequiredForWaasWallets` tells you whether any of this applies — skip these screens entirely when it returns `false`.

## Setting the password during setup

Right after the wallet is created, if a password is required, prompt for one and set it.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import { getDefaultClient } from '@dynamic-labs-sdk/client';
> import {
>   isPasswordRequiredForWaasWallets,
>   setWaasWalletAccountPassword,
> } from '@dynamic-labs-sdk/client/waas';
> import type { WalletAccount } from '@dynamic-labs-sdk/client';
>
> async function protectWallet(walletAccount: WalletAccount) {
>   // isPasswordRequiredForWaasWallets throws if no user is authenticated, so
>   // only call it once the user is signed in.
>   if (!getDefaultClient().user) return;
>   if (!isPasswordRequiredForWaasWallets()) return;
>
>   // promptForNewPassword is your implementation — collect + confirm a password.
>   const password = await promptForNewPassword();
>   await setWaasWalletAccountPassword({ password, walletAccount });
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import { isPasswordRequiredForWaasWallets } from '@dynamic-labs-sdk/client/waas';
> import type { WalletAccount } from '@dynamic-labs-sdk/client';
> import {
>   useSetWaasWalletAccountPassword,
>   useUser,
> } from '@dynamic-labs-sdk/react-hooks';
>
> function SetPassword({ walletAccount }: { walletAccount: WalletAccount }) {
>   const user = useUser();
>   const { mutateAsync: setPassword, isPending } =
>     useSetWaasWalletAccountPassword();
>
>   // isPasswordRequiredForWaasWallets throws before auth settles, so wait for a
>   // signed-in user before calling it.
>   if (!user) return null;
>   if (!isPasswordRequiredForWaasWallets()) return null;
>
>   return (
>     <form
>       onSubmit={async (event) => {
>         event.preventDefault();
>         const password = new FormData(event.currentTarget).get('password');
>         await setPassword({ password: String(password), walletAccount });
>       }}
>     >
>       <input name="password" type="password" autoComplete="new-password" />
>       <button type="submit" disabled={isPending}>
>         {isPending ? 'Saving…' : 'Set password'}
>       </button>
>     </form>
>   );
> }
> ```

> [!WARNING]
>
> The password can't be recovered if the user forgets it. Make that clear in your UI, and pair it with a backup & recovery option so users aren't locked out.

## Unlocking on a new device

When a returning user signs in on a device that hasn't unlocked the wallet yet, prompt for the password before the wallet can sign. Use `getWalletRecoveryState` to detect this: its `walletReadyState` is `'encrypted'` when this device's key share is still locked (prompt to unlock) and `'ready'` once it's unlocked. Check it after sign-in so you only show the prompt when it's actually needed.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import { getWalletRecoveryState, unlockWallet } from '@dynamic-labs-sdk/client/waas';
> import type { WalletAccount } from '@dynamic-labs-sdk/client';
>
> async function unlockIfNeeded(walletAccount: WalletAccount) {
>   const { walletReadyState } = await getWalletRecoveryState({ walletAccount });
>   // 'encrypted' means this device's key share is still locked; 'ready' means
>   // it's already unlocked and can sign.
>   if (walletReadyState !== 'encrypted') return;
>
>   // promptForPassword is your implementation.
>   const password = await promptForPassword();
>
>   try {
>     await unlockWallet({ password, walletAccount });
>     // showUnlocked is your implementation.
>     showUnlocked();
>   } catch {
>     // showError is your implementation — likely a wrong password.
>     showError("That password didn't work. Try again.");
>   }
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import type { WalletAccount } from '@dynamic-labs-sdk/client';
> import {
>   useGetWalletRecoveryState,
>   useUnlockWallet,
> } from '@dynamic-labs-sdk/react-hooks';
>
> function UnlockWallet({ walletAccount }: { walletAccount: WalletAccount }) {
>   const { data: recoveryState } = useGetWalletRecoveryState({ walletAccount });
>   const { mutateAsync: unlock, isPending, isError } = useUnlockWallet();
>
>   // Only prompt while this device's key share is still encrypted (locked).
>   if (recoveryState?.walletReadyState !== 'encrypted') return null;
>
>   return (
>     <form
>       onSubmit={async (event) => {
>         event.preventDefault();
>         const password = new FormData(event.currentTarget).get('password');
>         await unlock({ password: String(password), walletAccount });
>       }}
>     >
>       <input name="password" type="password" autoComplete="current-password" />
>       {isError ? <p role="alert">That password didn’t work.</p> : null}
>       <button type="submit" disabled={isPending}>
>         {isPending ? 'Unlocking…' : 'Unlock wallet'}
>       </button>
>     </form>
>   );
> }
> ```

## Changing the password

Offer a settings screen where the user provides their current password and a new one.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import { updateWaasPassword } from '@dynamic-labs-sdk/client/waas';
> import type { WalletAccount } from '@dynamic-labs-sdk/client';
>
> async function changePassword(
>   walletAccount: WalletAccount,
>   currentPassword: string,
>   newPassword: string,
> ) {
>   await updateWaasPassword({ currentPassword, newPassword, walletAccount });
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import type { WalletAccount } from '@dynamic-labs-sdk/client';
> import { useUpdateWaasPassword } from '@dynamic-labs-sdk/react-hooks';
>
> function ChangePassword({ walletAccount }: { walletAccount: WalletAccount }) {
>   const { mutateAsync: updatePassword, isPending } = useUpdateWaasPassword();
>
>   return (
>     <form
>       onSubmit={async (event) => {
>         event.preventDefault();
>         const form = new FormData(event.currentTarget);
>         await updatePassword({
>           currentPassword: String(form.get('current')),
>           newPassword: String(form.get('next')),
>           walletAccount,
>         });
>       }}
>     >
>       <input name="current" type="password" autoComplete="current-password" />
>       <input name="next" type="password" autoComplete="new-password" />
>       <button type="submit" disabled={isPending}>
>         {isPending ? 'Updating…' : 'Change password'}
>       </button>
>     </form>
>   );
> }
> ```

## Handling errors

| Situation | What to do |
| - | - |
| `isPasswordRequiredForWaasWallets()` is `false` | Don't render any password UI — the wallet isn't password-protected. |
| `isPasswordRequiredForWaasWallets()` before sign-in | It throws `User is not authenticated` rather than returning `false` — only call it once a user is signed in. |
| Wrong password on unlock | Show a retry; nothing is unlocked. |
| Wrong `currentPassword` on change | Reject the change and ask the user to re-enter their current password. |
| User forgets the password | It can't be recovered — direct them to backup & recovery. |

## See also

* [Post-login user setup](https://docs.dynamic.xyz/docs/javascript/building-ui/post-auth-user-setup.md) — where wallet creation (and the first password prompt) fits in
* Wallet backup & recovery — a safety net for a forgotten password
* [Transaction confirmation & simulation](https://docs.dynamic.xyz/docs/javascript/building-ui/transaction-confirmation.md) — signing with the unlocked wallet


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.