feat: Cloudflare JWT Assertion Authentication Method#178
Open
jeremyhart wants to merge 9 commits into
Open
Conversation
Adds an optional auth mode that trusts cryptographically-verified Cloudflare Access identities instead of the built-in login. - New internal/cfaccess package verifies the Cf-Access-Jwt-Assertion JWT against the team's JWKS (RS256, aud/iss/exp), returning the email claim - middleware.RequireAuth gains a CF branch: verify JWT, match the email to an existing user (no auto-provisioning; 403 on unknown), inject the user per-request (stateless, no session cookie) - Adds User.Email (additive column, app-layer uniqueness) plus GetUserByEmail/UpdateUserEmail helpers - When enabled, built-in login/passkey/setup return 403; new public /api/v1/auth/config tells the SPA to show a Cloudflare notice and use the CF logout URL; admin Users UI gains an Email field/column - CLI --create-admin gains --email to bootstrap the first admin - Config: CLAWORC_CF_ACCESS_ENABLED / _TEAM_DOMAIN / _AUD with fail-fast validation; docs in docs/auth.md, CLAUDE.md, website_docs, helm values Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FPx1Pv2gshfsJ5GyZqtbsY
CI's model-change guard requires a new migration whenever models change, even for additive columns. Adds version 7 to add the users.email column and its index, guarded by HasColumn/HasIndex so it is a no-op on fresh installs where AutoMigrate already created it (mirrors 00005_add_team_ids). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FPx1Pv2gshfsJ5GyZqtbsY
Add Cloudflare Access (Zero Trust) authentication support
Resolves merge conflicts for PR gluk-w#178 (jeremyhart:main -> gluk-w:main): - CLAUDE.md: keep both CF Access env vars and upstream's new vars + Terminology section - config.go: merge strings/time imports and CFAccess* + AllowedHostMounts settings - UserModal.tsx: adopt upstream @common/ alias paths while keeping useAuth/email handling - CloudflareLoginNotice.tsx: relocate under upstream's pages/ -> app/pages/ rename - website_docs/*.mdx: accept upstream removal of website_docs/ - Renumber migration_00007_add_user_email -> 00009 to avoid version collision with upstream's 00007 (backfill_instance_uuid) and 00008 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H1hLK8p2QkG8j5s4CPfHFL
Add a first-class "Cloudflare AI Gateway" option to the Add Provider form so
users can enter their Account ID and Gateway Name directly instead of hand-
crafting a base URL. These are path segments in the Cloudflare endpoint
(.../v1/{account}/{gateway}/compat), not separate credentials, so the UI now
collects them and assembles the universal (/compat) endpoint URL.
Backend:
- New api_type `cloudflare-ai-gateway`. Because the base ends in `/compat`
rather than `/v1`, the standard /v1 dedup does not fire, so its RewritePath
strips the client's leading /v1 to yield `.../compat/chat/completions`. It
speaks OpenAI-completions format and is declared to OpenClaw as
`openai-completions`.
- Support Authenticated gateways: a new encrypted `cf_ai_gateway_token` column
on LLMProvider is forwarded as `cf-aig-authorization: Bearer <token>` in
addition to the upstream provider's Bearer key.
Frontend:
- ProviderModal collects Account ID, Gateway Name, optional Gateway Token, the
upstream provider API key, and models (provider/model IDs). Parses the fields
back out of the base URL when editing.
Docs: document the new api_type in docs/virtual-keys.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KynRXwNky8tzc8yYEomRG7
The Migration Drift Check requires a companion migration whenever models.go changes. Add migration 00010 to add the additive, encrypted cf_ai_gateway_token column on llm_providers, mirroring 00009. Guarded by HasColumn so it is a no-op on fresh installs where AutoMigrate already created the column. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KynRXwNky8tzc8yYEomRG7
…nfig-skqr2h Add Cloudflare AI Gateway provider support
Follow-up to #5, addressing review feedback. The /compat connector now uses Cloudflare's Unified Billing model, which collapses the auth design to the standard single-credential flow. Connector / UI: - Rename the option to "Cloudflare AI Gateway - Unified Billing" and sort it alphabetically in the provider dropdown (was pinned at the bottom). - Replace the confusing "Provider API Key" + separate "Gateway Token" fields with one "Cloudflare API Token" (a token with AI Gateway Run permission). Cloudflare authenticates and bills the upstream provider, so no per-provider keys are needed. - Restore the Test button (it now works via the standard probe + token). Backend: - cloudflare-ai-gateway now embeds openAICompletions: the Cloudflare token is sent as Authorization: Bearer (stored as the provider's api_key), identical to other providers. Only the path differs (strip leading /v1 for /compat). - Drop the cf_ai_gateway_token column and all cf-aig-authorization plumbing. Migrations / CI: - Delete migration 00010. Adding a column is additive and handled by AutoMigrate — docs/migrations.md explicitly says not to write a migration for it. The CI "Guard against model changes without a new migration" step contradicted that policy and forced the noise migration, so remove it; migrationcheck still verifies AutoMigrate + migrations cover every model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KynRXwNky8tzc8yYEomRG7
…xups Simplify Cloudflare AI Gateway to Unified Billing; fix migration guard
gluk-w
reviewed
Jun 28, 2026
| echo " Run 'make migration' from control-plane/ to author one." | ||
| exit 1 | ||
| fi | ||
|
|
Contributor
Author
There was a problem hiding this comment.
Sorry, that wasn't meant to make it through to the PR
Owner
There was a problem hiding this comment.
could you restore the file content?
gluk-w
reviewed
Jun 28, 2026
| export interface User { | ||
| id: number; | ||
| username: string; | ||
| email?: string; |
Owner
There was a problem hiding this comment.
Is it possible to put email in the "username" for cloudflare accounts?
Contributor
Author
There was a problem hiding this comment.
It would be too ambiguous to the user, it will be the email matching from the Cloudflare side so you would have to enfore all usernames in the database are emails.
gluk-w
requested changes
Jun 28, 2026
gluk-w
left a comment
Owner
There was a problem hiding this comment.
Great stuff! I'm eager to merge this to the mainstream. Please address the comments, try to reuse "username" for CF emails if possible
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Enables authentication with Cloudflare JWT Assertion for use when the control plane is behind Cloudflare Zero Trust Application management