# Passkeys, MFA and SAML

Each environment's auth service can sign your app's users in with passkeys,
ask for a second step, and let a company's identity provider sign its people
in. Turn them on in the console under **Authentication > Sign-in methods**, or
with `PATCH .../auth/config`. Your app keeps using `@supabase/supabase-js`
unchanged.

An environment that turns none of this on runs exactly as before.

## Passkey sign-in

Turn on **Passkey sign-in** (`passkey_enabled: true`). In the app:

```js
// Once, while signed in:
await supabase.auth.registerPasskey()

// Later, from the sign-in screen:
const { data, error } = await supabase.auth.signInWithPasskey()
```

The passkey domain (the WebAuthn relying party) is derived for you:

- the domain is your site URL's host, else your first verified custom domain,
  else the project host;
- the accepted origins are your site URL, the exact `https://` entries of your
  redirect allow list and your verified custom domains, when they are on that
  domain or below it.

The console shows the derived domain under the switches. To set it yourself
(a parent domain, or a native Android app's `android:apk-key-hash:` origin),
use **Advanced**: `webauthn_rp_id`, `webauthn_rp_display_name` and
`webauthn_rp_origins` (at most 20).

## Two-step sign-in (MFA)

- **Authenticator app (TOTP)** is on by default (`mfa_totp_enabled`). Use
  `supabase.auth.mfa.enroll({ factorType: 'totp' })`, then `challenge` and
  `verify`. The session reaches `aal2`.
- **Security key as a second step** (`mfa_webauthn_enabled`): use
  `supabase.auth.mfa.webauthn.register(...)` and
  `supabase.auth.mfa.webauthn.authenticate(...)`.

## SAML single sign-on

On the Pro and Studio plans. Open **Authentication > SSO**:

1. **Turn on SAML.** The signing key is generated for you and never shown.
2. Give your identity provider the two URLs the page shows: the metadata URL
   (also the entity ID) and the reply URL (ACS).
3. **Connect** it with its metadata URL or metadata file, and the email
   domains it signs in.

In the app:

```js
const { data, error } = await supabase.auth.signInWithSSO({ domain: 'example.com' })
// data.url is the identity provider's sign-in page
```

One connection per environment to start. The routes are
`GET .../auth/sso`, `POST .../auth/sso/providers` and
`DELETE .../auth/sso/providers/{id}`.

## Access token hook

Adds claims (a role, a plan) to every session token. Under **Advanced**,
choose a SQL function or an edge function:

```sql
create or replace function public.custom_access_token_hook(event jsonb)
returns jsonb language plpgsql stable as $$
begin
  return jsonb_set(event, '{claims,plan}', to_jsonb('pro'::text));
end $$;
grant execute on function public.custom_access_token_hook(jsonb) to supabase_auth_admin;
revoke execute on function public.custom_access_token_hook(jsonb) from authenticated, anon, public;
```

An edge function hook is called over HTTPS and signed. Its secret is created
for you as the environment secret `AUTH_HOOK_CUSTOM_ACCESS_TOKEN_SECRET`, so
the function reads it from its environment. Redeploy the function after you
turn the hook on.

## Off unless you turn them on

Under **Advanced**: Sign in with Solana (`web3_solana_enabled`), Sign in with
Ethereum (`web3_ethereum_enabled`) and the OAuth 2.1 server
(`oauth_server_enabled`, with `oauth_server_authorization_path`, the consent
page on your site URL).
