> For the complete documentation index, see [llms.txt](https://docs.baas.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.baas.sh/baas-sdk/authentication.md).

# Authentication

Sign users in with a passkey or an external wallet, and pick the right entry point for your UI.

The SDK signs your users in two ways: with a **passkey** or with an **external wallet**. Both produce the same session.

{% hint style="info" %}
**Users own their account.** With passkeys, the account stays on their device and syncs through their password manager (iCloud Keychain, Google Password Manager, 1Password…). With external wallets, the wallet they already use is the account. BaaS never holds the user's key — that is what self-custodial means here.
{% endhint %}

## Which sign-in method?

{% columns %}
{% column %}

### Passkey <sub>recommended</sub>

A passkey lets users sign in with their device's built-in authentication (Face ID, Touch ID, or Windows Hello). No wallet or seed phrase for the user to manage.
{% endcolumn %}

{% column %}

### External wallet

Users sign in with a wallet they already have, such as MetaMask, Rainbow, or Coinbase Wallet. Best when they're already in the wallet ecosystem and want to use their existing address.
{% endcolumn %}
{% endcolumns %}

## Sign up vs sign in

The two methods follow different paths:

* **Passkey**: first-time users **sign up**, then **sign in**. Sign up creates the account and returns its address without opening a session. A common pattern: a **"Create account"** button that calls sign up and then shows your sign-in step, and a **"Sign in"** button for everyone else.
* **External wallet**: there is no sign-up. `signInWithWallet()` creates the account on the first sign-in and reuses it afterwards.

## Avoid double prompts

Disable the sign-in button while a sign-in is in flight: a second click opens a second wallet dialog, or aborts a passkey prompt with `OPERATION_ABORTED`. The [complete React example](/baas-sdk/integrations/react-app-example.md) shows it with a `pending` state.

## Next

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Passkeys</strong></td><td>Sign up and sign in with a device passkey.</td><td><a href="/baas-sdk/authentication/passkeys.md">Passkeys</a></td></tr><tr><td><strong>External wallet</strong></td><td>Sign in with MetaMask, Rainbow, or any EIP-1193 wallet.</td><td><a href="/baas-sdk/authentication/external-wallet.md">External wallet</a></td></tr><tr><td><strong>Sessions</strong></td><td>Read the session, listen for changes, refresh, and sign out.</td><td><a href="/baas-sdk/authentication/sessions.md">Sessions</a></td></tr></tbody></table>
