Direct OIDC Integration
LFA is a standard OIDC identity provider. Any stack that can federate to an external OIDC IdP, or perform an authorization-code flow, can consume LFA. You redirect to LFA, the user proves presence on their phone, and you get back an id_token whose claims carry the location verdict.
The Quick Start walks the wire protocol end to end. This page covers the integration decisions around it.
The one non-standard requirement
This is the direct path, where your app drives the OIDC flow itself. On this
path your token exchange must include octet_user_id, your stable, opaque
identifier for the user signing in. It drives
device-to-user binding, and a production exchange
without it is rejected (invalid_request).
If you federate through an identity broker (Okta, Microsoft Entra ID,
Auth0), the broker runs a fixed token exchange and cannot add octet_user_id.
That path is still supported: the broker authenticates the front channel with a
signed /authorize request instead, and the device binds to the verified
login_hint. See Okta. The rest of this page assumes the
direct path.
This shapes the library question. Any OIDC library can handle the /authorize
redirect. For the token exchange, either:
- your library lets you add a custom form field to the token request, so use it, or
- perform the exchange yourself. It is one POST (see the Quick Start), and you can hand the resulting tokens back to your session layer.
Your callback handler knows which of your users is mid-login, because you initiated the redirect from their session or your primary IdP's. The identifier is already in hand when you exchange.
Selecting a policy per login
Pass octet_policy_id on /authorize to choose which registered
policy this login must satisfy. One client can use many
policies:
&octet_policy_id=country_uk
Omit it to use your client's pinned default. An unknown id is rejected rather than falling back, so a typo cannot silently weaken the check.
login_hint
If you pass login_hint on /authorize, LFA echoes it back in the id_token as
the namespaced, unverified octet_login_hint claim. It is useful for
correlating the response with the login you started. On the direct path it is
pure passthrough: LFA does not verify or persist it, does not bind to it
(octet_user_id on /token is the binding identifier), and never reflects it
into identity claims such as email.
Strictness worth knowing up front
LFA is deliberately narrow. Misconfigurations fail immediately rather than degrading:
- PKCE S256 is required.
stateandnonceare both mandatory. - Scopes are
openid octet.location. Extra scopes a library adds (profile email) are ignored, and no profile claims come back. - No refresh tokens. A location proof is a statement about now. Re-prompt when you need fresh presence.
- Authorization codes are single-use with a 60-second redemption window.
The full tables (endpoints, parameters, claims, errors) live in the OIDC Reference.
Gating on the result
Two levels. Use what fits:
- Simple gate. Require
acr == "urn:octet:loc:strong"in the id_token. That single check means hardware-attested presence with the policy passed. - Audit or downstream verification. Keep the
octet_location_assertionclaim, also mirrored as top-levellocation_assertionin the token response. It is a signed, 120-second statement of the region verdict, verifiable against the same JWKS.
Client registration
Clients are registered by the Octet team. Email hello@octetproof.com with your redirect URIs and the policies you need. See Prerequisites.
Where to go next
- Device lifecycle. Binding, new phones, and
revocation: the operational side of
octet_user_id. - OIDC Reference. Every endpoint, parameter, claim, and error.