Passkey login (Face ID, Touch ID, Android biometrics) plus WhatsApp one-time codes, for any website. No passwords, no email flows, no auth code to maintain.
What your site gets
| You get | How |
|---|---|
| Biometric sign-in | WebAuthn passkeys — the same standard behind Face ID, Touch ID, and Android biometrics |
| Guaranteed fallback | 6-digit codes delivered over WhatsApp, for first-time enrollment and device loss |
| Zero password storage | Okia hosts the auth pages; your site only exchanges an OAuth code for tokens |
| Privacy by construction | Each site receives a pairwise subject ID (pw_…). Two sites can never correlate the same user |
| Standard protocol | OAuth2 authorization code + PKCE (S256 only); OIDC-shaped ES256 JWTs verified against a public JWKS |
| Step-up signal | amr claim tells you whether login was passkey or OTP (["otp"]), so you can require step-up |
The five-minute pitch
- Enroll — a user hits your login button, enters their phone on the hosted Okia page, gets a WhatsApp code, verifies, and is offered a passkey. Under 60 seconds, once ever.
- Return — every later login on any connected site is one Face ID tap. Under 5 seconds, zero typing.
- Fallback — WhatsApp code is always one tap away; nobody gets locked out by a lost phone upgrade.
- Your side — you exchange a short-lived code at
POST /oauth/token, verify the ID token, and mint your own session. One endpoint, any language.
Users have no email in Okia. You identify them by the pairwise sub; the phone number is available only via the consented phone scope (double opt-in).
Protocol facts
All endpoints live on the auth domain.
| Endpoint | Method |
|---|---|
/authorize |
GET |
/oauth/token |
POST |
/oauth/revoke |
POST |
/userinfo |
GET |
/logout |
GET |
/.well-known/jwks.json |
GET |
/.well-known/openid-configuration |
GET |
| Artifact | Format | Lifetime | aud |
Notes |
|---|---|---|---|---|
| Auth code | opaque | 60 s | — | single-use |
| ID token | ES256 JWT | 15 min | your client_id |
token_use: "id", nonce, amr, auth_time |
| Access token | ES256 JWT | 15 min | https://auth.okia.io |
token_use: "access" |
| Refresh token | opaque | 30 d rolling idle, 90 d absolute | — | rotates on every use |
Always check token_use when verifying: an access token is never an ID token.
Pick your connector
| Connector | Fits | Quickstart | Target |
|---|---|---|---|
| Universal JS snippet | Any page that can add a <script> tag — static HTML, any CMS, any backend |
quickstart-js.md | ~5 min |
| WordPress plugin | WordPress and WooCommerce; pure redirect, zero JS, caching-plugin safe | quickstart-wordpress.md | ~6 min |
@okia/react |
React SPAs and Next.js App Router | quickstart-react.md | ~8 min |
Anything else (Laravel, Rails, Webflow, Framer, headless Shopify): it is a standard OAuth2 code + PKCE flow against the endpoints above — implement the redirect and the token exchange directly.