# Trust & Privacy

:::note[In one sentence]

The phone proves which region it is in without revealing where in that region it
is, and LFA stores the minimum needed to keep the factor bound to the right
device and user.

:::

## What never leaves the phone

The Octet Location app runs the location check entirely on-device: sensor reads,
motion analysis, and the anti-spoof pipeline all happen locally. What leaves the
phone is a signed proof whose payload is a **region verdict** ("inside GB") plus
the cryptographic material to verify it. Raw coordinates, paths, speeds, and
sensor detail never leave the device, so LFA cannot leak, sell, or disclose what
it never receives.

## Why a verdict can be trusted

The verdict carries no coordinates, so it is only as trustworthy as the chain
that produces it. LFA verifies that chain fail-closed on every login:

1. **The proof is signed on-device**, produced by the Octet SDK's location and
   anti-spoof pipeline (the same proof-of-location machinery as the
   [Octet Mobile SDK](/docs/concepts/proof-of-location/)).
2. **The device is hardware-attested.** The signing key lives in the phone's
   secure hardware. Apple App Attest certifies, once, at
   [enrollment](/docs/concepts/device-lifecycle/), that a genuine, unmodified app on a
   genuine device created it. LFA verifies the evidence against the platform's
   root certificates directly (Apple's for App Attest, Google's for Android key
   attestation), with no runtime calls to either vendor.
3. **Every login is freshly signed, and the freshness is committed inside the
   proof.** The phone commits this session's one-time challenge inside the signed
   proof, identically on both platforms. Freshness therefore travels with the
   proof rather than alongside it: a captured proof cannot be replayed into
   another login even if it is stored, forwarded, or verified later and out of
   band.
4. **Any failed check is a denial.** Weak attestation, a stale proof, a spoof
   verdict below the [policy floor](/docs/concepts/policies/#spoof-verdict-floor), an
   unknown or revoked device: LFA denies rather than scoring the result.

## What LFA stores

| Stored | What it is | Why |
|---|---|---|
| Device key + attestation record | The device's public key and its App Attest certification. The device ID is a hash of the key | To verify each login's hardware signature |
| Device-to-user binding | The per-client user identifier you supply (`octet_user_id` for a **direct** client, or the `login_hint` from the signed request object for a **broker** client) | To pin the factor to the user's own phone |
| Revocation flag | Whether a device has been revoked | To deny lost or replaced phones |

Notes on the binding: the identifier is **yours**. LFA never sees a password,
email, or profile unless you choose to send one as the ID (send an opaque
internal ID instead, see [Prerequisites](/docs/getting-started/prerequisites/)).
Bindings are scoped per client, so identifiers are never visible across clients
and LFA builds no cross-application identity graph.

The binding identifier reaches LFA one of two ways:

- **Direct clients** send `octet_user_id` on the RP-authenticated `/token` call.
  This is the standard path and is unchanged.
- **Broker clients** (Okta, Microsoft Entra ID, Auth0) run a fixed token
  exchange and cannot add `octet_user_id`, so they instead authenticate the
  front channel. Their `/authorize` request carries a signed request object, and
  the `login_hint` **inside that verified object** becomes the binding
  identifier. LFA never binds to the raw query-string `login_hint`. See
  [Okta integration](/docs/integration/okta/) and the
  [OIDC reference](/docs/reference/oidc/).

:::note

Either way, what LFA stores is an opaque user identifier that the customer chose,
not coordinates, credentials, or a profile.

:::

## What LFA does not store

- **Coordinates or location history.** Never received in the first place.
- **User profiles or credentials.** Identity stays with your IdP.
- **`login_hint` (direct clients).** Passed through into the id_token as
  `octet_login_hint`, not persisted. For **broker** clients this is different:
  the `login_hint` from the signed request object is the binding identifier and
  is stored, exactly as `octet_user_id` is for a direct client (see
  [What LFA stores](/docs/concepts/trust-and-privacy/)). It is still an identifier you
  chose, with no coordinates, credentials, or profile.
- **Long-lived tokens.** The login session lives 90 seconds, the authorization
  code 60 seconds, the location assertion 120 seconds. There are no refresh
  tokens.

## Where to go next

- [Device lifecycle](/docs/concepts/device-lifecycle/). Enrollment, replacement, and
  revocation of the bound device.
- [How a login works](/docs/concepts/how-a-login-works/). The full flow the chain
  above protects.
