# Policies

:::note[In one sentence]

A policy is the named rule a login must satisfy: which region the user must be
in, how strong the device attestation must be, how fresh the proof may be, and
how strict the anti-spoof verdict is.

:::

Policies live on the LFA side, registered for your tenant by the Octet team
alongside your OIDC clients. Your application never evaluates location data. It
names a policy, and LFA answers pass or fail.

---

## What a policy contains

| Field | Meaning | Today |
|---|---|---|
| `geofence` | The region the user must prove presence in | `kind = "country"` with an ISO 3166-1 alpha-2 code |
| `attestation_strength` | The minimum device-attestation floor: `basic` or `strong` | Production policies run `strong` |
| `max_proof_age_s` | How old the phone's location proof may be, in seconds | 300 is the standard country-level window |
| `allow_plausible_spoofing_verdict` | Whether a `PLAUSIBLE` anti-spoof verdict passes, or only `VERIFIED` | Default `false` (`VERIFIED` only) |

A registered policy looks like this. You do not write it. Octet registers it for
you, and the shape is shown here to make it concrete:

```toml
[[tenant.policies]]
id = "country_uk"
version = 1
geofence = { kind = "country", iso_code = "GB" }
attestation_strength = "strong"
max_proof_age_s = 300
```

## How a policy is selected

- **Direct OIDC integrations** pass `octet_policy_id` on the `/authorize`
  request to pick a policy per login: one client, many policies. Omitting the
  parameter uses the client's pinned default. An unknown id is rejected.
- **Brokers** (Okta and similar) pin the policy per registered client: one
  client per policy. A broker runs a fixed token exchange and does not set
  `octet_policy_id`, so each policy gets its own client. Enforcing UK presence
  for one app and US presence for another means two clients.

## Geofences

Country-level geofences are what ships today. The user proves presence in a
country, identified by its ISO code, and the tokens assert exactly that. Finer
regions (state or province, city, custom areas) are on the roadmap, and the
policy schema (`kind` plus parameters) grows into them without changing your
integration.

See [regions](/docs/concepts/regions/) for how a region is expressed and matched
on the device.

## Attestation strength

This sets the minimum hardware backing LFA accepts.

- **`strong`**. The proof must be rooted in the phone's secure hardware and
  carry a per-login signature from the enrolled device key. iOS and Android both
  reach it (see [device attestation](/docs/concepts/device-attestation/)). This
  is the production floor, and a passing login carries `acr: urn:octet:loc:strong`.
- **`basic`**. Accepts proofs below that floor. For test and development setups.
  Do not use it to gate anything that matters.

A proof that misses the floor is denied outright, not downgraded.

## Proof freshness

`max_proof_age_s` bounds how stale the phone's proof may be when LFA checks it.
300 seconds matches the SDK's country-level proof window (see
[time semantics](/docs/concepts/time-semantics/)). The location assertion LFA
then issues is itself valid for **120 seconds**. Presence is asserted about now,
and nothing in the flow doubles as a long-lived credential.

## Spoof-verdict floor

Every proof carries an anti-spoof verdict. The labels and the evidence behind
them are covered in [verdicts](/docs/concepts/verdicts/). The policy sets where
the cutoff sits. LFA always rejects anything below `PLAUSIBLE`. Whether
`PLAUSIBLE` itself passes is yours to choose:

- **Default (`allow_plausible_spoofing_verdict = false`)**. Only `VERIFIED`
  proofs pass. This is fail-closed, and recommended for anything
  security-relevant.
- **Opt-in (`true`)**. `PLAUSIBLE` also passes. Lower friction, lower assurance.
  For flows where a false rejection costs more than a marginal proof.

## Where to go next

- [Direct OIDC integration](/docs/integration/direct-oidc/). The `octet_policy_id`
  parameter in practice.
- [How a login works](/docs/concepts/how-a-login-works/). Where the policy check sits
  in the flow.
