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 an exchange without it
is rejected (invalid_request).
If you federate through an identity broker such as Okta, the broker runs a fixed token exchange and cannot add octet_user_id.
That path is 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 requirement determines how you use your OIDC library. 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 already has the identifier of the user who is mid-login, because you initiated the redirect from their session or your primary IdP's.
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.
Protocol constraints
A misconfigured request fails immediately rather than falling back to weaker behaviour:
- 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. - There are no refresh tokens. Run a new login when you need fresh proof of 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
There are two ways to use the result:
- 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.