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

# Authentication Factors

> Main Factors, Sync Factors, and how each Factor Secret is derived.

export const RootKey = () => {
  return <Tooltip tip="The root key is a 256-bit secret from which other keys are derived for use throughout World App.">Root Key</Tooltip>;
};

export const BackupMetadata = () => {
  return <Tooltip tip="The plain-text metadata that accompanies each backup. It is stored in the Backup Service.">Backup Metadata</Tooltip>;
};

export const BackupAccountId = () => {
  return <Tooltip tip="A deterministic identifier of the form backup_account_0..., derived from the user's Root Key. The corresponding secp256k1 keypair is the disaster-recovery key for /reset.">Backup Account ID</Tooltip>;
};

export const BackupEncryptionKeypair = () => {
  return <Tooltip tip="The single keypair used to encrypt the Unsealed Backup. It is randomly generated by World App and not persisted anywhere.">Backup Encryption Keypair</Tooltip>;
};

export const FactorSecret = () => {
  return <Tooltip tip="A 32-byte secret derived from or held by a Main Factor. Used to encrypt the Backup Encryption Keypair.">Factor Secret</Tooltip>;
};

export const SyncFactor = () => {
  return <Tooltip tip="A Sync Factor (an Elliptic Curve Keypair) is a secondary authentication mechanism for a backup. It grants limited permissions and does not require a user interaction to be used. It is primarily used to sync the backup.">Sync Factor</Tooltip>;
};

export const MainFactor = () => {
  return <Tooltip tip="A Main Factor (e.g. a Passkey, OIDC account or Keypair in iCloud Keychain) is the primary authentication mechanism for a backup. It grants the broadest permissions and requires a user interaction to be used.">Main Factor</Tooltip>;
};

# Main Factors vs Sync Factors

A backup distinguishes two factor categories. They differ in *what they can do*, not just *how strongly they authenticate*.

|                           | <MainFactor />                                                 | <SyncFactor />                                                     |
| ------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------ |
| Purpose                   | Authenticate the user; unlock the backup; manage other factors | Update existing backup contents; delete the backup; delete factors |
| Requires user interaction | Yes (passkey tap, OIDC sign-in, biometric on Keychain)         | No (silent — background sync)                                      |
| Can decrypt the backup?   | Yes (holds a <FactorSecret />)                                 | **No**                                                             |
| Can add new factors?      | Yes                                                            | **No**                                                             |
| Per-device                | No (Passkey/OIDC roam)                                         | Yes — one Sync Factor per device                                   |
| Allowed types             | Passkey, OIDC + Turnkey, iCloud Keychain (iOS)                 | EC keypair only                                                    |

The split exists for two reasons. First, sync needs to happen silently in the background; requiring a passkey tap on every change would be untenable. Second, the Sync Factor is long-lived on the device. If it was granted the power to *enroll* new recovery methods would mean a stolen device could silently add factors and gain persistent access. The Sync Factor can *delete* but not *add*; this is enforced by both Backup Service authorization and Turnkey policy (see [Components & Parties](/world-app/backup/components#turnkey)).

# Factor Secrets

A <FactorSecret /> is a 32-byte secret used to encrypt the <BackupEncryptionKeypair />. Each <MainFactor /> either *derives* its secret deterministically (in a cryptographically-secure form) or *holds* it in a system made for cryptographic storage; the client never persists secrets in plaintext.

## Passkey (PRF)

The factor secret is the output of the [WebAuthn PRF extension](https://www.w3.org/TR/webauthn-3/#prf-extension) evaluated over a fixed salt (`"world-app-backup"`). PRF is deterministic per credential, so the same passkey always produces the same secret.

Platform notes:

* **iOS** — Requires iOS 18+. Implemented with `AuthenticationServices` (`ASAuthorizationPublicKeyCredentialPRFRegistrationInput` / assertion input). The credential may be stored in iCloud Keychain by the system (out of scope of mobile apps).
* **Android** — Implemented with the [Credential Manager API](https://developer.android.com/identity/sign-in/credential-manager) (`androidx.credentials`). PRF support depends on the active credential provider — typically Google Password Manager (requires Google Play Services 23.33+); Samsung Pass and others are not yet supported. Minimum API level 29; device screen lock must be set up or registration fails.

## OIDC + Turnkey

The factor secret is a random 32-byte sequence generated by the client at enrollment, then *imported* into Turnkey's TEE-backed enclave under a sub-organization owned by the user. From that point on, the secret only leaves the enclave when the user authenticates the OIDC factor. Turnkey's enclave performs OIDC token verification itself before releasing the secret — the user proves possession of their Google or Apple identity, not just to the Backup Service but to the enclave too.

Supported providers:

* **Google** (iOS, Android)
* **Apple** (iOS, Android)

The client uses Bedrock's `Turnkey` helper (P-256 ECDSA stamping over Turnkey API requests) to interact with the enclave. See [Components & Parties — Turnkey](/world-app/backup/components#turnkey) for the sub-organization structure (root user, auth user, sync factor user, root quorum) and policy details.

## iCloud Keychain (iOS only)

A P-256 keypair is generated by the iOS [`Security`](https://developer.apple.com/documentation/security) framework via `SecKeyCreateRandomKey` (key type `kSecAttrKeyTypeECSECPrimeRandom`). The private key scalar (32 bytes) is the factor secret.

iCloud Keychain is intentionally **not** synced (`kSecAttrSynchronizable: false`) — it is an iOS-only, device-local factor. Its purpose is to provide an additional recovery path on iOS without requiring the user to set up a passkey or sign in with OIDC. Android has no equivalent.

# Sync Factor

The Sync Factor is a P-256 ECDSA keypair generated client-side at backup creation (and again on every recovery onto a new device). The public key is registered with the Backup Service; the private key is stored locally in platform-secure storage:

* **iOS** — Keychain (non-synced).
* **Android** — Android Keystore (hardware-backed when available); falls back to `EncryptedSharedPreferences` if Keystore is unavailable. The fallback is a lower security tier and accepted as a known limitation today.

When the client signs a Backup Service challenge with the Sync Factor, the server verifies the P-256 ECDSA signature (DER-encoded) against the public key in the <BackupMetadata />. Each Sync Factor authorizes exactly one device; recovery onto a new device produces a new Sync Factor and registers it via `/add-sync-factor`.

For OIDC users, the Sync Factor is *also* registered with Turnkey as a sub-organization user (`sync_factor_user`) with a policy permitting deletion-only actions. This is what makes background factor cleanup (e.g., removing an OIDC provider after the user removes the factor in Settings) possible without an additional passkey tap.

# Backup Account ID

Independent of the factor system, every backup has a deterministic <BackupAccountId /> derived from the user's <RootKey /> via a BLAKE2b-based KDF (subkey ID `0x101`, context `OXIDEKEY`). The keypair is **secp256k1**, distinct from the P-256 keypairs used elsewhere in the system. Format:

```
backup_account_<33-byte_compressed_pubkey_hex>
```

This keypair is **not** a factor — it cannot decrypt the backup. Its sole purpose is the disaster-recovery `/reset` endpoint: when a user has lost every Main and Sync Factor but still controls the original device (and therefore the Root Key), the Backup Account ID lets them prove ownership and wipe the existing backup so they can re-enroll fresh factors. See [Disaster Recovery via /reset](/world-app/backup/advanced#disaster-recovery-via-reset).

# Platform Differences

The clients diverge in places where the underlying platform requires it. The list below is the *intentional* divergence, anything else is a bug.

|                            | iOS                                       | Android                                                                                            |
| -------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Passkey API                | `AuthenticationServices` (PRF on iOS 18+) | Credential Manager (`androidx.credentials`); PRF requires GMS 23.33+ and a PRF-supporting provider |
| iCloud Keychain factor     | Supported (Main Factor)                   | **Not available** — no platform equivalent                                                         |
| OIDC providers             | Google, Apple                             | Google, Apple                                                                                      |
| Sync Factor secure storage | Keychain (non-synced)                     | Android Keystore; falls back to `EncryptedSharedPreferences` on devices without Keystore           |
| Passkey rename             | iOS 26.2+ only                            | Provider-dependent                                                                                 |
| OIDC sign-in               | `GoogleSignInSDK` / `AppleSignInSDK`      | `GetSignInWithGoogleOption` (Credential Manager)                                                   |
