Web security of hosted pages and the front API
Hosted login, the account portal, the email pages (verification, password reset, invitation) and the browser API they call follow a fixed web security baseline on every plan. These protections are not settings you can switch off. The only setting you control is which web origins may call the OAuth endpoints from the browser.
Cross-site request forgery (CSRF)
Requests that change state are checked before anything is read or changed:
- Origin check. A state-changing request to the hosted sign-in API or to a verification or reset page must carry an
Originheader that is exactly the environment's login host. Any other origin is refused withorigin_not_allowed. - Consent screen. A form post on the consent screen is refused when its
Originis not the page's own host, or whenSec-Fetch-Siteis present and is notsame-origin. - Form tokens. Each sign-in transaction and each email form has its own CSRF token, derived with HMAC-SHA256 from a per-environment key. The sign-in API expects it in the
Idp-Csrf-Tokenheader; the token of one form does not work for another. Tokens are compared in constant time. - Cookies. The cookies that carry sign-in state use
SameSite=Lax(see below), so a request started from another site does not send them.
Cross-origin requests (CORS)
CORS is answered only on the OAuth endpoints a browser application calls: the token, revocation, pushed authorization request and userinfo endpoints. It applies to public single-page applications (spa type) only.
- Set the allowed origins per application with
allowed_web_origins(https://host[:port], at most 20, no wildcards). See Create an application. - A browser's preflight request does not say which application is calling, so it is answered from the union of the allowed origins of the environment's active public single-page applications. The actual request is then checked against the origin list of the application that is calling.
- An origin outside that application's list is refused with
403 origin_not_allowedand receives noAccess-Control-Allow-Originheader. - An allowed origin is echoed back exactly.
*andnullare never sent,Access-Control-Allow-Credentialsis never sent, and every cross-origin answer carriesVary: Origin. - Requests from the login host itself are not cross-origin and are not affected.
Redirects after sign-in
If you use the Next.js SDK, returnTo must be a path of your own app. A full URL or a value such as //evil.example is ignored and the user lands on /. See Next.js SDK.
Security headers
Hosted pages set these headers on every response:
| Header | Value |
|---|---|
Content-Security-Policy |
default-src 'none'; scripts only with a fresh per-response nonce; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests; Trusted Types required for scripts |
Reporting-Endpoints and report-to |
Policy violations are reported to /u/csp-report on the same host |
X-Frame-Options |
DENY: hosted pages cannot be embedded in a frame |
X-Content-Type-Options |
nosniff |
Referrer-Policy |
no-referrer |
Cross-Origin-Opener-Policy |
same-origin |
Permissions-Policy |
Camera, microphone, geolocation, payment, USB, serial, HID and Bluetooth are off; passkey requests are allowed for the page itself |
Cache-Control |
no-store |
The policy allows no inline scripts and no unsafe- keywords. Form submissions are limited to the page's own host; on the consent screen the policy also allows the redirect address of the application that is asking, so the decision can return to it.
The report endpoint accepts only browser violation reports of at most 8 KiB, and is rate limited per IP address. Reports are counted by directive. They carry no query string, fragment or user information when logged.
Strict-Transport-Security
- Environment hosts on
keenauth.appsendmax-age=63072000; includeSubDomains. - A custom domain sends
max-age=63072000only, withoutincludeSubDomains. KeenAuth cannot decide for the other subdomains of your domain, so that choice stays with you.
Cookies
Sign-in state cookies are named with the __Host- prefix, so a browser accepts them only over HTTPS, with Path=/ and without a Domain attribute. They cannot be set from a sibling subdomain.
| Cookie | Flags | Purpose |
|---|---|---|
__Host-idp_session |
Secure, HttpOnly, SameSite=Lax |
The user's session. Its lifetime ends at the session's absolute expiry |
__Host-idp_device |
Secure, HttpOnly, SameSite=Lax |
Device recognition |
__Host-idp_ltx and __Host-idp_azr_* |
Secure, HttpOnly, SameSite=Lax |
Binds a sign-in or authorization attempt to the browser that started it |
__Host-idp_fed_bind |
Secure, HttpOnly, SameSite=None |
Short-lived binding for enterprise SAML sign-in. The identity provider posts back from another site, so Lax would drop it |
Cookie values are not readable by page scripts (HttpOnly).