# Software authenticator

> Configure software WebAuthn credentials, handle registration save callbacks, and manage local or external signature counters.

Source: https://console.getchrovia.com/en/docs/sdk-software-authenticator

**Entitlement:** `softwareAuthenticator`. **EP:** `internal.software_authenticator`. Websites use standard `navigator.credentials.create()` and `get()`, not a `chrovia.softwareAuthenticator` JS domain. Registration save replies additionally require `messaging` and a responsible Provision plugin.

## Configure the authenticator

```json
{
  "internal": {
    "software_authenticator": {
      "config": {
        "disguise_as": "windows_hello",
        "display_name": "SDK Passkey Vault",
        "counter_source": "local"
      }
    }
  }
}
```

| Field | Values/default |
| --- | --- |
| `config.disguise_as` | `windows_hello`, `icloud_keychain`, `gpm_phone`; default `gpm_phone` |
| `config.display_name` | Non-empty UI label; default `Passkey Vault` |
| `config.counter_source` | `external` requests counters from your plugin for incrementing presets; otherwise local |
| `credentials` | Array of serialized credential records, if importing previously saved credentials |

Choose how newly registered passkeys are presented to websites. These presets do not save passkeys to actual Windows Hello, iCloud, or a phone, and do not provide hardware-backed credentials. Changing the preset affects new registrations, not existing passkeys.

To test, fully restart with your configuration and save handler installed, open a site's account security settings, and choose to add a passkey. Select your configured vault name when offered and finish the site's confirmation. Sign out, choose passkey sign-in, and select the saved account if prompted. Keep an alternative sign-in method while testing.

## Registration needs a save reply

Register this handler at every Provision Service Worker start. This minimal example approves **local test registration**; the kernel saves the record only after approval:

```js
const messaging = globalThis.chrovia?.messaging;
if (messaging) {
  messaging.on('chrovia.software_authenticator.credential.save', data => {
    const channel = data?.callback_channel;
    if (data?.action !== 'save_credential'
        || typeof channel !== 'string'
        || !channel.startsWith('chrovia.software_authenticator.credential.save.reply.')) {
      return;
    }
    messaging.emit(channel, { success: true });
  });
}
```

The request carries a complete credential record, `action: "save_credential"`, and `callback_channel`. Reply `{success: true}` to accept, or `{success: false}` to reject. Failure or no reply within 30 seconds rejects registration. Returning a value from the listener is not a reply.

For a managed vault, validate and durably save the record in your controlled storage before replying success. Do not log the payload: it contains sensitive sign-in data. Do not install two plugins that answer the same registration channel.

Use an HTTPS/localhost relying-party test site or Chrovia Studio's software-authenticator demo to register and sign in. Normal WebAuthn origin, RP, challenge, and server verification requirements still apply. A browser-side credential alone is not a server-registered account, and this capability does not promise unattended success for every site or headless flow.

## Back up, restore, and remove passkeys

Treat each credential as an opaque record: preserve all its fields and values without decoding or reconstructing them. Use `rp_id` to identify the website, `user_id` to identify the account, `credential_id` to identify the passkey, and optional `user_name` / `user_display_name` for display. Keep identifiers exactly as received.

The save request contains these record fields at the top level, alongside `action` and `callback_channel`. Exclude those two message-routing fields when saving the record in your vault. Registering a new passkey for the same `rp_id` and `user_id` replaces the previous local passkey, so replace that account's vault entry rather than blindly appending another one. Wait for the site to confirm registration before relying on the new passkey.

To restore or remove local passkeys:

1. Fully quit the instance and back up its current Extended Preferences file. Preserve a recent complete credential list from `internal.software_authenticator.credentials`; it can contain updates made after registration. Reading through `chrovia.prefs` additionally requires `extendedPrefs`.
2. Prepare the complete desired `credentials` array from saved records. To remove a passkey, omit its record; to remove all, use an empty array. Preserve the rest of the document and authenticator configuration.
3. For unsigned preferences, replace `internal.software_authenticator.credentials` in the instance file while closed. For signed preferences, obtain a replacement signed file containing the desired list.
4. Restart the instance and test sign-in on the original website. Editing the list does not refresh an already-running instance.

Malformed or incomplete records can be skipped. Do not invent placeholders. Restoring a browser record does not register an account on the website, and removing it locally does not revoke the site's registration or delete copies in other vaults. Manage those separately in the site's account security settings. Keep backups and save payloads private; do not expose them to website scripts.

## External counters

With `counter_source: "external"`, the kernel can emit `chrovia.software_authenticator.counter.claim` with `action: "claim_counter"`, `rp_id`, base64 `credential_id`, and `callback_channel`. Reply on that channel with `{success: true, counter: "42"}`, using a non-negative uint32 decimal string from an atomic allocation in your vault.

The allocation must be coordinated for the same credential across instances; do not reuse the constant example value. Failed, invalid, or missing replies fall back to local counter behavior after at most 30 seconds; they do not automatically reject sign-in.

External-counter requests apply to passkeys registered with `windows_hello` or `icloud_keychain`. The default `gpm_phone` preset reports zero and does not request external counters. Select an incrementing preset before registration when your integration needs counter allocation. Changing `disguise_as` later does not change existing passkeys.

## Using signed preferences

With `signedExtendedPrefs`, passkey registration or sign-in can make the current EP file unusable on the next launch. Arrange to obtain an updated [signed EP](https://console.getchrovia.com/en/docs/sdk-signed-extended-prefs) containing the latest credential list before restarting. A successful save reply does not update the signed file.

For unsigned local testing, use an issued License without `signedExtendedPrefs`.

TOTP secrets and [saved passwords](https://console.getchrovia.com/en/docs/sdk-passwords) are different data and protocols. Do not store them as software WebAuthn credentials.
