Add Cloudflare Access authentication settings#348
Conversation
Merge pull request remnawave#212 from remnawave/dev
Merge pull request remnawave#214 from remnawave/dev
Merge pull request remnawave#248 from remnawave/dev
Merge pull request remnawave#267 from remnawave/dev
Merge pull request remnawave#272 from remnawave/dev
Merge pull request remnawave#276 from remnawave/dev
Merge pull request remnawave#278 from remnawave/dev
Merge pull request remnawave#335 from remnawave/dev
Greptile SummaryWires Cloudflare Access as a new auth method across the frontend: new settings controls (team domain, audience, email allowlist, allowed emails/domains) are added to the authentication settings card, and the login page automatically attempts a Cloudflare Access token exchange on load when the method is enabled. New Zod schemas locally extend the published backend contract to carry the extra fields until the upstream package ships.
Confidence Score: 4/5The change is safe to merge. The new auth flow is additive and degrades cleanly on backends that do not yet expose Cloudflare Access fields. The Cloudflare Access auto-login on the login page permanently gates retries with a ref, so a transient failure leaves users with only a red badge and no actionable path forward (apart from refreshing). The schema extension approach and dual-callback src/pages/auth/login/login.page.tsx — the one-shot login attempt and missing retry path deserve a second look before shipping to users who may be behind Cloudflare Access exclusively. Important Files Changed
Sequence DiagramsequenceDiagram
participant Browser
participant LoginPage
participant useGetAuthStatus
participant useCloudflareAccessLogin
participant Backend
Browser->>LoginPage: mount
LoginPage->>useGetAuthStatus: GET /api/status
Backend-->>useGetAuthStatus: "{ authentication: { cloudflareAccess: { enabled: true } } }"
useGetAuthStatus-->>LoginPage: authStatus (with cloudflareAccess)
Note over LoginPage: useEffect fires<br/>isCloudflareAccessEnabled=true<br/>isAttempted=false → attempt once
LoginPage->>useCloudflareAccessLogin: "POST /api/auth/cloudflare-access {}"
Backend-->>useCloudflareAccessLogin: "{ response: { accessToken: ... } }"
alt Success
useCloudflareAccessLogin-->>LoginPage: onSuccess → setToken + setIsAuthenticated(true)
LoginPage->>Browser: AuthGuard redirects to dashboard
else Failure
useCloudflareAccessLogin-->>LoginPage: onError → setIsCloudflareAccessFailed(true)
Note over LoginPage: Red badge shown, ref=true<br/>no retry possible, must refresh
end
|
| useEffect(() => { | ||
| if ( | ||
| !authStatus?.isLoginAllowed || | ||
| !authMethods.isCloudflareAccessEnabled || | ||
| isCloudflareAccessAttemptedRef.current | ||
| ) { | ||
| return | ||
| } | ||
|
|
||
| isCloudflareAccessAttemptedRef.current = true | ||
| setIsCloudflareAccessFailed(false) | ||
| cloudflareAccessLogin({ variables: {} }) | ||
| }, [authStatus?.isLoginAllowed, authMethods.isCloudflareAccessEnabled, cloudflareAccessLogin]) |
There was a problem hiding this comment.
No retry path after Cloudflare Access failure
Once isCloudflareAccessAttemptedRef.current is set to true, the effect is permanently gated and will never fire again in the same component instance. If the initial automatic login request fails (e.g., network hiccup, CF misconfiguration), the red badge appears but there is no way to reattempt login short of a full page refresh. When Cloudflare Access is the only configured auth method, the user is left with no actionable path forward — no retry button, no other form visible.
| /> | ||
| </Group> | ||
| </Center> | ||
|
|
||
| <Accordion.Panel> | ||
| <Stack gap="md"> | ||
| <TextInput | ||
| description="Cloudflare Access team domain, for example team.cloudflareaccess.com" | ||
| key={form.key('cloudflareAccessSettings.teamDomain')} | ||
| label="Team domain" | ||
| placeholder="team.cloudflareaccess.com" | ||
| {...form.getInputProps( | ||
| 'cloudflareAccessSettings.teamDomain' | ||
| )} | ||
| /> | ||
|
|
||
| <TextInput | ||
| description="Cloudflare Access application AUD tag" | ||
| key={form.key('cloudflareAccessSettings.audience')} | ||
| label="Audience" | ||
| placeholder="0000000000000000000000000000000000000000000000000000000000000000" | ||
| {...form.getInputProps( | ||
| 'cloudflareAccessSettings.audience' | ||
| )} |
There was a problem hiding this comment.
No format validation on
teamDomain and audience inputs
teamDomain and audience are free-text fields with no client-side format constraints. A user can save an arbitrary string as the team domain (should look like <team>.cloudflareaccess.com or a custom domain) or as the audience (should be a 64-character hex string). While the backend should ultimately validate these, adding pattern/regex validation in CloudflareAccessSettingsSchema would surface misconfiguration earlier and keep the UX consistent with how other structured fields are handled in the form.
Summary
Adds UI support for Cloudflare Access authentication.
This wires the frontend to the new backend Cloudflare Access auth method, adds settings controls, and automatically attempts Cloudflare Access login when the method is enabled.
Changes
Notes
The frontend currently extends the published backend contract schemas locally so the new fields are preserved before the backend contract package is released with these additions.
Validation
git diff --check