Skip to content

How EntryIdP works

EntryIdP is a biometric-first OpenID Connect (OIDC) identity provider. Users authenticate with a face liveness check — there is nothing to type and nothing to remember: no OTPs, magic links, email codes, or social logins. Face liveness is the only authentication method.

Your application integrates using any standard OIDC library with the Authorization Code + PKCE flow. From the protocol's point of view EntryIdP behaves like a normal OIDC provider — but with one difference you must understand before writing any code.

Two entry points: login and enrolment

This is what makes EntryIdP different from a drop-in OIDC swap.

Entry pointRequestWhat it does
LoginGET /authorizeThe user does a liveness check; EntryIdP matches their live face against an existing enrolment. Works only for users who have enrolled before.
EnrolmentGET /authorize?prompt=createA first-time user registers their face (and self-declared profile + ID document) before being signed in.

A standard /authorize request always tries to identify an existing user by their face. If no enrolled face matches and the request did not include prompt=create, EntryIdP returns the user to your app with error=access_denied&error_description=user_not_registered. With prompt=create, an unmatched face is routed into the enrolment UI instead of being rejected.

Your app decides which entry point to use. The Integrate your app guide shows both patterns (separate "Sign in" / "Create account" buttons, or login-first-then-catch-the-error) for every platform.

The authentication flow

Example 1 — returning user (login)

  1. Your app redirects the user to /authorize?client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256&scope=openid profile&response_type=code.
  2. EntryIdP presents the liveness UI. The user looks at the camera; a live face is detected and matched against their enrolled biometric.
  3. EntryIdP redirects back to your redirect_uri with ?code=…&state=….
  4. Your server exchanges the code for tokens at POST /token (sending code_verifier). You receive an ID token, an access token, and a refresh token.
  5. Call GET /userinfo with the access token to retrieve profile claims.

Example 2 — first-time user (enrolment)

  1. Your app redirects to /authorize with the same parameters as above, plus prompt=create.
  2. EntryIdP presents the enrolment UI: liveness check → profile capture → ID document scan.
  3. On completion EntryIdP issues an authorization code and redirects back, exactly as in the login flow.
  4. Your server exchanges the code for tokens at POST /token. The returned ID token carries face_liveness_verified: true; the user is now enrolled and future logins use the login path above.

Example 3 — SPA or mobile app (public client, tokens in the client)

  1. Your client generates a random code_verifier and derives code_challenge = BASE64URL(SHA-256(code_verifier)).
  2. Redirect the user to /authorize with both values. Store code_verifier in memory (SPA) or the platform secure keychain (iOS/Android) — never in localStorage or a cookie.
  3. EntryIdP redirects back to your redirect_uri with ?code=….
  4. Your client exchanges the code directly at POST /token, sending code_verifier. Tokens are returned to the client.
  5. Keep the access token in memory only. Use the refresh token to rotate; store it in the secure keychain on mobile or, for SPAs, accept that a page reload requires the user to re-authenticate.

Example 4 — server-rendered app or BFF (tokens never leave the server)

  1. Your backend generates the code_verifier/code_challenge pair and stores the verifier in the user's server-side session.
  2. Your backend builds the /authorize URL and either redirects the browser directly or passes the URL to a thin frontend to redirect.
  3. EntryIdP redirects back to a backend redirect_uri (e.g. https://yourapp.com/auth/callback).
  4. Your backend exchanges the code for tokens at POST /token — the tokens are stored server-side and never sent to the browser.
  5. Your backend sets an HttpOnly; Secure; SameSite=Strict session cookie. The browser presents this cookie on every request; your backend validates the session and calls APIs on the user's behalf.

Which pattern should you use?

Public client (Example 3)BFF / server-side (Example 4)
Best forNative mobile apps, Electron, SPAs where you genuinely have no backendWeb apps with a server, server-rendered pages, anything that handles sensitive data
Tokens storedIn client memory / secure keychainOn the server; browser holds only a session cookie
XSS exposureAccess token in JS memory is reachable if your page is compromisedTokens are never in the browser — XSS cannot steal them
Does the choice affect EntryIdP?No — the OIDC flow and all EntryIdP endpoints are identical either way.The only difference is who calls POST /token and where the tokens land.

The short answer: if your app has a backend, use the BFF pattern — it is strictly safer. For native mobile the public-client pattern is idiomatic and fine, provided you use the platform keychain (not plain storage) for the refresh token. For pure SPAs with no backend the public-client pattern is your only option; mitigate the risk by keeping tokens in memory and using short access-token lifetimes (EntryIdP issues 15-minute access tokens by default).

See it run: the developer sample

Try the interactive sample app — a working relying-party that walks you through this exact flow step by step. It starts a real Authorization Code + PKCE login (and enrolment), takes you through the liveness check and consent screen, and shows the authorization code, tokens, and /userinfo claims you get back at each stage. Use it to watch the flow end to end before wiring up your own app. (Seeded in all non-production environments.)

Key concepts

ConceptWhat it means for your app
AuthenticationThe user passed a liveness check and your app holds a valid, unexpired access token for them.
LivenessA live human face was detected and matched a stored enrolment — not a photo, video, or replay.
EnrolmentA first-time user registered their face via the /authorize?prompt=create path.
Verified identityAn identity-proofing bureau confirmed the user's ID document, name, and biometric match. Surfaced as the identity_verified claim — separate from liveness.

Liveness is not identity verification

These are two different assurances, carried by two different claims:

  • face_liveness_verified (in the ID token) — confirms a live person was present and matched their enrolled face. It does not confirm legal name, date of birth, or ID document.
  • identity_verified (from /userinfo, only when the identity:verified scope is granted) — confirms a bureau checked the user's identity document.

Use face_liveness_verified for "is this the returning account holder?" Use identity_verified only for decisions that legally require proof of identity.

Environments

EnvironmentIssuer URL
Localhttp://localhost:5000
Testhttps://idp-test.entryidp.com
Demohttps://idp-demo.entryidp.com
Productionhttps://idp.entryidp.com

Every environment publishes its endpoints at {issuer}/.well-known/openid-configuration. Fetch them from there rather than hardcoding URLs.

Next steps

  1. Get a client — what you need before you can integrate, and how to request a registration.
  2. Integrate your app — step-by-step guides and copyable AI prompts for web and mobile.
  3. Reference — endpoints, scopes, claims, token lifetimes, and error codes.

EntryIdP — Synapser