feat(platform): implement trusted headers authentication middleware#1252
Conversation
Wire up the existing trusted headers business logic to an HTTP endpoint so reverse proxies (Authelia, Authentik, oauth2-proxy) can authenticate users via identity headers. - Add Convex HTTP action at /api/trusted-headers/authenticate that reads proxy headers, finds/creates users, and sets session cookies - Header names default to Remote-Email/Name/Role/Teams and are configurable via TRUSTED_*_HEADER env vars for proxy compatibility - Login page auto-redirects to the auth endpoint when TRUSTED_HEADERS_ENABLED=true (user never sees the login form) - Expose TRUSTED_HEADERS_ENABLED to frontend via server.ts env config - Add TRUSTED_TEAMS_HEADER to docker-entrypoint env passthrough - Rewrite authentication docs to accurately describe offline-first auth model, configurable header names, and proxy compatibility table - Add clarifying comments to auth-related UI components
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
📝 WalkthroughWalkthroughThis PR implements trusted headers authentication for Tale. It updates authentication documentation to describe an offline-first model where the first user creates an owner account and admins provision subsequent users. The implementation introduces a new HTTP endpoint ( Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes The primary complexity stems from the new Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
services/platform/docker-entrypoint.sh (1)
624-655:⚠️ Potential issue | 🟠 MajorSync
TRUSTED_HEADERS_INTERNAL_SECRETto Convex env as well.The trusted-headers flow validates this secret in Convex, but it is not included in the env sync list. That can silently disable the defense-in-depth check even when operators set it in container env.
🔧 Proposed fix
ENV_VARS_TO_SYNC=( "SITE_URL" "BASE_PATH" "ENCRYPTION_SECRET_HEX" "BETTER_AUTH_SECRET" @@ "RAG_DATABASE_URL" "TRUSTED_HEADERS_ENABLED" + "TRUSTED_HEADERS_INTERNAL_SECRET" "TRUSTED_EMAIL_HEADER" "TRUSTED_NAME_HEADER" "TRUSTED_ROLE_HEADER" "TRUSTED_TEAMS_HEADER"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@services/platform/docker-entrypoint.sh` around lines 624 - 655, ENV_VARS_TO_SYNC array is missing TRUSTED_HEADERS_INTERNAL_SECRET so Convex won't receive the runtime secret; add "TRUSTED_HEADERS_INTERNAL_SECRET" to the ENV_VARS_TO_SYNC list in the docker-entrypoint.sh snippet (the ENV_VARS_TO_SYNC variable) so the entrypoint will export/sync that environment variable into the Convex environment alongside TRUSTED_HEADERS_ENABLED and related TRUSTED_* entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/authentication.md`:
- Around line 80-87: Update the wording in the "How it works" section so Step 2
clearly states that the login page JavaScript performs a client-side navigation
using window.location.href to go to /api/trusted-headers/authenticate (instead
of implying an HTTP redirect); keep the rest of the flow unchanged and ensure
the phrase references the exact endpoint "/api/trusted-headers/authenticate" so
readers understand it's client-side navigation.
In `@services/platform/app/routes/_auth/log-in.tsx`:
- Around line 65-72: The current useEffect (checking trustedHeadersEnabled,
redirectTo, getEnv/siteUrl/basePath) unconditionally redirects to the
trusted-headers authenticate endpoint and can cause an infinite loop if that
endpoint redirects back to /log-in with an error; modify the effect to first
parse window.location.search (URLSearchParams) and skip the redirect when a
trusted_headers_error param is present (e.g., trusted_headers_error=1), and
ensure the authenticate endpoint includes that query param on error so the login
page can detect it and not re-trigger useEffect redirection.
In `@services/platform/convex/trusted_headers_auth/authenticate_handler.ts`:
- Around line 239-241: The escapeJs function currently only escapes backslashes
and single quotes; update escapeJs to also escape control and line-separator
characters that can break out of a JS string (at minimum \n, \r, \u2028, \u2029,
and optionally \t, \b, \f, \v) by replacing them with their JavaScript escape
sequences so the resulting single-quoted literal is safe; locate the escapeJs
function in authenticate_handler.ts and add replacements for these characters
(in addition to the existing backslash and single-quote handling) so any
redirectTo value cannot inject or break the generated JS string.
---
Outside diff comments:
In `@services/platform/docker-entrypoint.sh`:
- Around line 624-655: ENV_VARS_TO_SYNC array is missing
TRUSTED_HEADERS_INTERNAL_SECRET so Convex won't receive the runtime secret; add
"TRUSTED_HEADERS_INTERNAL_SECRET" to the ENV_VARS_TO_SYNC list in the
docker-entrypoint.sh snippet (the ENV_VARS_TO_SYNC variable) so the entrypoint
will export/sync that environment variable into the Convex environment alongside
TRUSTED_HEADERS_ENABLED and related TRUSTED_* entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: fccc150b-6b52-4deb-abcd-ef4c5563f7ca
📒 Files selected for processing (14)
README.mddocs/authentication.mddocs/environment-reference.mdservices/platform/app/features/auth/hooks/queries.tsservices/platform/app/features/settings/organization/components/member-add-dialog.tsxservices/platform/app/routes/_auth/log-in.tsxservices/platform/app/routes/_auth/sign-up.tsxservices/platform/convex/http.tsservices/platform/convex/trusted_headers_auth/authenticate_handler.tsservices/platform/convex/trusted_headers_auth/http_handlers.tsservices/platform/convex/trusted_headers_auth/internal_mutations.tsservices/platform/docker-entrypoint.shservices/platform/lib/env.tsservices/platform/server.ts
- Break infinite redirect loop: login page checks for ?trusted_headers_error=1 param before redirecting to the auth endpoint; auth endpoint error responses now redirect back to /log-in?trusted_headers_error=1 - Harden escapeJs to escape \n, \r, \t, \f, \v, U+2028, U+2029 so redirectTo values cannot break the generated JS string literal - Add TRUSTED_HEADERS_INTERNAL_SECRET to ENV_VARS_TO_SYNC so Convex receives the runtime secret - Clarify docs: step 2 describes client-side navigation via window.location.href, not an HTTP redirect
Summary
/api/trusted-headers/authenticate)TRUSTED_*_HEADERenv vars (defaults:Remote-Email,Remote-Name,Remote-Role,Remote-Teams) for compatibility with Authelia, Authentik, oauth2-proxy, etc.TRUSTED_HEADERS_ENABLED=true— users behind a proxy never see the login formDetails
New files:
convex/trusted_headers_auth/internal_mutations.ts— Convex internalMutation wrapping the existingtrustedHeadersAuthenticatebusiness logicconvex/trusted_headers_auth/authenticate_handler.ts— HTTP handler that reads proxy headers, calls the mutation, signs session cookies (mirrors the SSO set-session pattern), and redirects to the appconvex/trusted_headers_auth/http_handlers.ts— httpAction wrapper for route registrationAuth flow:
Remote-Emailetc.) → Caddy →/api/trusted-headers/authenticateSet-Cookie+ redirect to dashboardTest plan
npm run typecheck --workspace=@tale/platform,npm run lint --workspace=@tale/platform)TRUSTED_HEADERS_ENABLED=truebehind an authenticating proxy (e.g. Authelia) and verify auto-loginTRUSTED_EMAIL_HEADERto proxy-specific header)Summary by CodeRabbit
New Features
Documentation