- Calls your backend to start verify
- Opens the hosted verify UI by POSTing the
handofftoken toverifyUrl(a hidden auto-submitting form)
returnUrl afterwards, and your server works out what happened by calling GET /session/{sessionId}.
Backend setup first: Get your credentials · Backend overview. End-to-end: Quickstart.
Building a native mobile app? See Mobile apps — the same backend calls, with the return delivered to a deep link your OS routes into your app.
The browser half
The whole browser half is only a few lines: an auto-submitting form on the way out, and a plain landing page on the way back. No dependency, no build step — shown below and on each framework page. Either way your backend still makes the two authenticated calls.Handoff — auto-submitting form
lang, to force the UI language.
The hosted page is always a full top-level page. It sends frame-ancestors 'none', so it cannot be embedded in an iframe, and there is no inline or embedded mode and no postMessage API — the form submit above is the only way in.
The landing page — a plain GET, nothing sensitive
After verification, Authentica redirects the browser to your registeredreturnUrl with an ordinary top-level GET:
- your normal
SameSite=Laxcookies arrive as usual — no cookie tricks - no CSRF exemption is needed, and there is no POST route to build
- it can be served by any stack, like any other page of your app
GET /session/{session_id} with your client credentials, checks the echoed state and userRef, and performs the action. The browser’s only job is to display whatever your backend concludes. Never render “verified” just because the redirect arrived.
There is no callback route in your SPA, and none on your server either — nothing is ever pushed to you. There is nothing to listen for.
One landing page for every action
Whatever you are verifying — a login, a payment, a phone change — the browser side is identical: start, POST into verify, land back. You register onereturnUrl for the app, and your backend uses state to recognise which action the user was in the middle of and what to show next. See Routing many actions through one return URL.
That means the browser needs no per-action logic. Do not branch on query params or store the action in localStorage; your backend owns that decision, keyed by state.
Rules
- Do not put
client_idorsecretin the browser - Do not call Authentica
/sessionor/session/{id}from frontend JS - Do not navigate to
verifyUrl— POST to it - Do not append
handoff,lang, or anything else toverifyUrl - Do not try to embed the hosted page — it refuses to be framed
- Do not treat
session_idorstateon the landing URL as proof of anything — ask your backend - Register
returnUrlexactly in the portal
Pick your framework
JavaScript
Plain fetch + auto-submitting form
React
Form POST from a React handler
Vite
Vite + React form-POST pattern
Angular
Component start + form POST

