Authentication handoff
Reuse application identity without trusting client-supplied user data.
Feedbax supports anonymous reads and authenticated mutations through a framework-neutral identity-provider contract. Product code asks that provider for the current session, requires authentication, projects a public user, or starts login/logout; it never reads tokens or cookies itself.
For a copyable host implementation, see examples/auth-handoff: it includes a
Next.js App Router flow, environment setup, framework-neutral signing contract,
coordinated logout, and security guidance.
Signed handoff
The host application issues an HS256 JWT with a protected kid header and these claims:
- standard
iss,aud,sub,iat, andexpclaims; - required
emailandnameclaims; - optional
picture,company,role,plan, and root-relativereturn_pathclaims.
Use a stable, immutable host user ID for sub. Set aud to the configured Feedbax audience and keep exp no more than five minutes after iat (the example uses two minutes). Redirect the browser to /auth/handoff?token=.... Feedbax verifies the algorithm, key ID, signature, issuer, audience, issue time, and expiration server-side, then responds with a 303 to a clean local URL.
Configuration
Authentication secrets remain in environment variables:
FEEDBAX_AUTH_AUDIENCE=feedbax
FEEDBAX_AUTH_LOGIN_URL=https://app.example.com/login
FEEDBAX_AUTH_ACTIVE_SESSION_KEY_ID=session-2026-07
FEEDBAX_AUTH_ISSUERS=[{"issuer":"https://app.example.com","keys":[{"id":"handoff-2026-07","secret":"BASE64URL_32_RANDOM_BYTES"}]}]
FEEDBAX_AUTH_SESSION_KEYS=[{"id":"session-2026-07","secret":"DIFFERENT_BASE64URL_32_RANDOM_BYTES"}]Generate each secret from at least 32 cryptographically random bytes and base64url-encode it. Handoff and session secrets must be different. Multiple issuers and retained keys can appear in the JSON arrays.
The session is an encrypted A256GCM JWT in the host-only __Host-feedbax_session cookie with Secure, HttpOnly, SameSite=Lax, Path=/, and an explicit lifetime. Private identity fields are encrypted and are never included in the public-user projection.
Rotation and failures
To rotate a handoff key, deploy the new kid and secret to Feedbax first, switch the host issuer to the new key, wait beyond the maximum handoff lifetime, then remove the old key. For a session key, add the new key, change FEEDBAX_AUTH_ACTIVE_SESSION_KEY_ID, retain the previous key through the longest session lifetime, and then remove it.
Invalid, malformed, expired, or unknown-key handoffs return the same sanitized error. Invalid session cookies are treated as anonymous. Tokens, cookies, secrets, private claims, and raw verification errors must never be logged.
Return paths accept only root-relative URLs. Absolute URLs, protocol-relative URLs, backslashes, control characters, and authentication endpoints fall back to /, preventing open redirects and redirect loops.
Protected mutations
Board, feedback detail, roadmap, and changelog reads remain anonymous. Submission, voting, commenting, and subscription endpoints independently require the encrypted session; hiding a control in the browser is never treated as authorization.
When a signed-out visitor chooses a protected action, send the current root-relative path, query, and fragment as X-Feedbax-Return-Path. A 401 AUTHENTICATION_REQUIRED response includes the configured login location. Preserve any draft locally, start the signed handoff, and let the user review and explicitly retry after returning; mutations are never replayed automatically.
All mutation requests use same-origin application/json, strict { input, captchaToken? } envelopes, and bounded bodies. Per-action rate limits are configurable through MutationProtectionConfig. The default memory store is not shared between serverless instances; production Cloudflare and Vercel deployments must inject a durable RateLimitStore. CaptchaProvider is an optional server-verification boundary and no vendor is enabled by default.