> ## Documentation Index
> Fetch the complete documentation index at: https://www.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](/docs/javascript/building-ui/post-auth-user-setup), 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.

<Tabs>
  <Tab title="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>

  <Tab title="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>
      );
    }
    ```
  </Tab>
</Tabs>

<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.
</Warning>

## 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 `WalletReadyState.ENCRYPTED` when this device's key share is still locked (prompt to unlock) and `WalletReadyState.READY` once it's unlocked. Check it after sign-in so you only show the prompt when it's actually needed.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import {
      getWalletRecoveryState,
      unlockWallet,
      WalletReadyState,
    } 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 !== 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>

  <Tab title="React">
    ```tsx theme={"system"}
    import type { WalletAccount } from '@dynamic-labs-sdk/client';
    import { WalletReadyState } from '@dynamic-labs-sdk/client/waas';
    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 !== 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>
      );
    }
    ```
  </Tab>
</Tabs>

## Changing the password

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

<Tabs>
  <Tab title="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>

  <Tab title="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>
      );
    }
    ```
  </Tab>
</Tabs>

## 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](/docs/javascript/building-ui/post-auth-user-setup) — where wallet creation (and the first password prompt) fits in
* Wallet backup & recovery — a safety net for a forgotten password
* [Transaction confirmation & simulation](/docs/javascript/building-ui/transaction-confirmation) — signing with the unlocked wallet
