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

# Passkeys alongside Veriqa

> A passkey added after users sign in through a messaging app combines the convenience of Veriqa and the phishing resistance of a passkey in one scenario.

Signing in through a messaging app is convenient, but the confirmation in the chat is not tied to the
browser where the session will open: a person talked into confirming someone else's QR code hands the
sign-in to the attacker. A passkey is bound to your application's origin and works only near the
browser, so a mandatory key protects the users who have one. Users still sign in with Veriqa, which
also delivers the button for creating the key, while your application registers and verifies the key.

> [!NOTE]
>
> Veriqa already provides the mechanisms this scenario needs; you implement the logic on top of them
> on your side, in whatever way suits you — see [What you build yourself](#what-you-build-yourself).

## What Veriqa adds to passkeys

On its own, a passkey needs an account to attach to and a way back in when the device that holds the key
is lost. When people sign in through Veriqa, both come with the first sign-in:

* **An account without a sign-up form.** The first sign-in through the messaging app gives your application the user's
  identity, and you create the account from it — no form, no password, no email verification, as in
  [Passwordless sign-in and registration](https://veriqa.app/docs/scenarios/passwordless-login.md). The key is added to an
  account that already exists.
* **A backup channel from the first sign-in.** The account is already linked to the person's messaging app. If
  the device that holds the key is lost, you still have a channel to reach the person; run recovery through
  it as a separate, stricter procedure (see [What to keep in mind](#what-to-keep-in-mind)).
* **Fewer steps than with a sign-up form.** To sign in, the person scans a QR code and taps a button in the chat,
  and they create the key with one more button in the same chat.

## What you build yourself

Veriqa neither creates nor verifies passkeys: a WebAuthn server library for your stack registers and
verifies the key. Veriqa provides the sign-in and the button in the messaging app after it.

1. **A button in the messaging app after the sign-in.** Set the `Redirect` post-completion action (or a
   link item of `Menu`) to the URL of your key-creation page with `{handle}` in it —
   [Menu and Redirect](https://veriqa.app/docs/reference/configuration.md#menu-and-redirect-a-message-with-buttons):

   ```json theme={null}
   {
     "Veriqa": {
       "OpenIddict": {
         "Clients": [
           {
             "ClientId": "app",
             "CompletionBehavior": {
               "ActionByChannel": { "telegram": "Redirect" },
               "RedirectUrl": "https://app.example.com/passkey/add?h={handle}",
               "RedirectButtonText": "Add a passkey"
             }
           }
         ]
       }
     }
   }
   ```

   *Synthetic example: the client ID, the URL and the button label are made up.* The settings sit in
   the client entry, so the button arrives after every sign-in to this application through Telegram,
   and only in the chat of the person who confirmed the sign-in. Add the other channels that show
   buttons to the same map.
2. **The key-creation page.** The link in the button opens where the person has the messaging app —
   usually on the phone. Your server exchanges the handle and learns who signed in
   (`POST /api/link-handle/redeem` with an application token — see
   [Two bots](https://veriqa.app/docs/scenarios/channel-linking.md#two-bots-handing-the-user-over-to-your-bot)), gets `sub` and
   starts a WebAuthn registration for that `sub`. The key is created on this device and bound to your
   application's origin. The handle is single-use and valid for
   `Veriqa:TransactionEngine:CompletedRetentionSeconds` — 600 seconds by default.
3. **Keys only through the button.** Do not let a session opened by a Veriqa sign-in create a key: if
   the sign-in was forwarded, that session belongs to the attacker, who would add their own key to
   someone else's account. If the `sub` already has a key, the key-creation page registers a new one
   only after a signature with an existing key. Otherwise, an attacker who got the person to confirm
   a forwarded sign-in could ask them to forward the button's link too and add their own key.
4. **The key before the session.** When Veriqa returns the `id_token`, check whether that `sub` has a
   key. If it does, ask for a signature with the key in the same browser and open the session only
   after you have verified it. If it does not, follow your own policy: open an ordinary session or
   point them to the button in the messaging app.
5. **Mandatory mode.** For a user who has created a key, turn off sign-in without the key: as long as
   it remains available, the attacker will use it.

## Why this works

The attacker starts a sign-in to your application and forwards the QR code to the victim. The victim
confirms in their messaging app, and Veriqa hands the authorization code to the attacker's browser.
Your application then asks that browser for the victim's key. A fake page on another origin gets no
signature for your origin, and signing in to someone else's computer with a key on the phone requires
Bluetooth between them: under [RFC 10027](https://www.rfc-editor.org/rfc/rfc10027) (BCP 247,
§6.2.3.3) both devices need BLE. With a mandatory key, and new keys added only with an existing
one, the attacker gets no session.

In BCP 247 terms, for users with a mandatory key this is proximity over radio (§6.1.1). It is also
the phishing-resistant sign-in before a confirmation your server creates (§6.1.15). How this fits the
other mitigations is shown on [Veriqa, BCP 247 and the threat model](https://veriqa.app/docs/concepts/cross-device-security.md).

## What to keep in mind

* **Only the mandatory mode protects.** A user who has no key, or who can still sign in without one,
  is no better protected than with an ordinary sign-in through a messaging app.
* **Creating the first key is the weak spot.** The link in the button is a bearer link: whoever opens
  it first creates a key for an account that has none. An attacker who talked the person into
  confirming someone else's sign-in can ask them to forward this link too. Once the account has a
  key, the page adds a new one only after a signature with an existing key (step 3). Tell the user
  about every new key through a channel you have, and let them remove a key that is not theirs.
* **Bluetooth, or a key on the computer itself.** Signing in to a computer with a key on the phone
  requires Bluetooth on both devices. Where radio is forbidden, a key created on the computer itself
  works: create the first key there by opening the button, for example, in a desktop messaging app.
  If the first key is already on the phone, no key can be added on such a computer, because adding
  one takes a signature with the existing key, and that signature needs Bluetooth too. In that case
  the [place factor](https://veriqa.app/docs/scenarios/place-and-identity.md) fits such a perimeter.
* **Not every channel shows the button.** Messaging apps and the sign-in with a provider account show
  a button after the sign-in. Email shows no buttons, and WhatsApp through Twilio does not send them:
  these users need another way to create a key, but not from a session opened through Veriqa.
* **A lost device.** Account recovery without the key is another way around the key. Run it as a
  separate procedure, stricter than an ordinary sign-in — see
  [Account recovery](https://veriqa.app/docs/scenarios/account-recovery.md).


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