Skip to content
Authreads Docs

Protect a login page you host

If your domain serves the page where a person types a password, your response headers and every script on that page are part of the authentication boundary. Authreads cannot add headers to a response your server owns.

Choose the smaller security boundary

Prefer authreads-hosted OpenID Connect unless your product genuinely needs to own the credential UI. On the hosted page, Authreads owns the HTML, cookies, no-store policy, CSP, and script inventory. Your application receives the authorization result and never has to secure a password form.

If you self-host, your page, reverse proxy, framework bootstrap, browser session, and CSP reporting are yours to operate. Keep app login credentials and returned Authreads session bearers on your server; never ship them in browser JavaScript. The generated API reference remains the source of truth for request and response shapes.

Do not load analytics, tag managers, A/B testing, session replay, chat widgets, CDN scripts, or any other third-party JavaScript on a page where a password or one-time code is entered. Any script allowed on that origin can read the credential.

The response-header baseline

ControlRequired outcome
Content-Security-PolicyAllow only the code, connections, forms, and assets the credential page needs.
frame-ancestors 'none'Prevent another site framing the login page. Keep X-Frame-Options: DENY for older clients.
Cache-Control: no-store, max-age=0Keep credentials, one-time links, and user-specific HTML out of browser and intermediary caches.
Referrer-Policy: no-referrerDo not send credential-page URLs to another origin in the Referer header.
X-Content-Type-Options: nosniffStop browsers guessing an executable type for a mislabeled response.

The starter below mirrors Authreads' credential-page posture. Replace the collector URL; narrow or extend sources only for assets your page actually owns. If browser calls go through your same-origin backend-for-frontend, keep connect-src 'self'. Do not add a broad wildcard to make a violation disappear.

Credential-page response headers · report-only stage
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'nonce-<fresh-random-value>';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  font-src 'self' data:;
  connect-src 'self';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';
  object-src 'none';
  report-to credential-csp;
  report-uri https://<your-report-collector>/csp
Reporting-Endpoints: credential-csp="https://<your-report-collector>/csp"
Cache-Control: no-store, max-age=0
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Generate at least 128 bits of cryptographic randomness for every document response and put the same nonce on every application/framework-owned inline script. Never accept a nonce from the request, reuse it, or fall back to 'unsafe-inline' when generation fails. If your page has no inline scripts, omit the nonce and use script-src 'self'.

Report first, enforce after evidence

  1. Start with one report-only policy
    Keep the current enforcing policy unchanged. Send the stricter candidate once as Content-Security-Policy-Report-Only; do not emit two fields.
  2. Exercise every credential route
    Test login, password reset, invitation, one-time-code, validation, 404, and error pages. Confirm the page remains interactive and reports contain no first-party violation.
  3. Observe real traffic
    Confirm reports actually reach your collector. Separate your own resources from browser extension injection; zero reports is not proof if delivery is broken.
  4. Make enforcement a reviewed release
    After an agreed clean window, promote the measured policy to Content-Security-Policy. Keep a rollback and alert on first-party violations.

Do not put upgrade-insecure-requests in the report-only rehearsal: browsers do not upgrade there and warn about the ineffective directive. Configure HSTS at your HTTPS edge for the whole hostname only after confirming every subdomain it covers is HTTPS-ready.

Your boundary and ours

SurfaceWho owns the page security
Authreads-hosted sign-inAuthreads owns the HTML, security headers, no-store behavior, scripts, and CSP reporting.
Your self-hosted login/reset pageYou own the response, headers, dependencies, browser session, monitoring, and enforcement decision.
Authreads APIAuthreads owns API authentication, validation, rate limits, token issuance, and the published API/OIDC contracts.

Resolve issuer, endpoints, and jwks_uri from /.well-known/openid-configuration, and use the generated API reference for exact fields and errors. Those machine-readable contracts outrank copied prose when the API evolves.

Pre-release check

  • No third-party script executes on the credential origin.
  • Secrets and Authreads session bearers never enter browser storage or client bundles.
  • Every credential document is non-cacheable, including error and not-found responses.
  • Each inline script has the current response nonce; two responses never share one.
  • Framing is denied, referrers are suppressed, and content sniffing is disabled.
  • Report delivery is proven before the measured policy becomes enforcing.
  • Login, reset, invitation, and failure routes work with the enforcing candidate.