You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Web server: optional authentication via an identity-aware proxy (e.g. Cloudflare Access) instead of the access token #981
I run codeg-server bound to 127.0.0.1 and reach it from my phone through a Cloudflare Tunnel, with Cloudflare Access (SSO via Entra ID) in front of it. Access already authenticates every request, and the tunnel connector validates the Access JWT before forwarding. Codeg's own access token is therefore a second login on top of the SSO one, and it has to be kept and restored by hand whenever the machine is rebuilt.
Two problems
1. Two locks where one is enough. There is currently no way to tell Codeg "trust the proxy's signed identity". In web/auth.rs, require_token accepts a bearer token or the codeg-token. WebSocket sub-protocol and nothing else.
2. An expired proxy session leaves the UI in "reconnecting" forever. Once the Access session expires (24 h in my setup), Access answers the frontend's fetch('/api/...') with a 302 to the login page on another origin. The browser blocks that cross-origin redirect, WebTransport.call sees a network error rather than a 401, and the reconnect dialog never resolves. The only way out is a manual reload. Measured against my deployment: without a session, POST /api/<command> returns 302 → https://<team>.cloudflareaccess.com/cdn-cgi/access/login/.... The same request with X-Requested-With: XMLHttpRequest returns 401 instead.
Proposal
An opt-in auth mode. The default stays exactly as it is today.
Config (env/CLI, for example CODEG_AUTH=proxy-jwt): the header name (Cf-Access-Jwt-Assertion for Cloudflare), the JWKS URL, the expected issuer and the expected audience.
When the mode is on, the middleware accepts a request only if that header holds a JWT with a valid signature against the cached JWKS, the expected iss and aud, and an unexpired exp. It fails closed: missing config, an unreachable JWKS with no cached key, or any validation failure gives a 401. The token is not required in this mode.
Because the JWT is signature-checked, this stays safe even though any local process can reach the loopback port: a process that can reach the port still cannot forge a JWT.
Frontend: send X-Requested-With: XMLHttpRequest on API calls so that a proxy which supports it answers 401 instead of redirecting. In proxy mode, a 401 triggers a full page reload, which lets the proxy run its login flow, instead of showing the token prompt.
(The header alone would already turn problem 2 into a visible "unauthorized" state for anyone behind Access, even in token mode.)
The configuration is generic (header + JWKS + iss + aud), so it is not tied to Cloudflare. I have only tested it against Cloudflare Access.
Questions
Is this something you would accept? If so, do you have a preference for how it is configured (env vars, a settings entry, or both) and for the name?
Should the desktop app's web-service settings expose it, or should it be standalone codeg-server only?
I'm happy to send a PR with tests if it fits the project's direction.
Thanks for taking the time to investigate this and write up such a clear, detailed report, @MagTer!
I've checked the current implementation, and it matches your diagnosis: authentication is token-only, API calls and health probes don't send X-Requested-With, and network failures during health checks keep the UI in the reconnect loop.
We'd be very happy to accept a PR along these lines, including better handling of expired proxy sessions and an opt-in, JWT-validated proxy authentication mode while keeping the existing token mode as the default. The fail-closed approach and tests you propose make sense. We can work out the configuration naming and desktop settings scope in the PR.
Thanks again for the thoughtful proposal and for offering to contribute!
Opened both parts: #984 (expired proxy session → Sign-in expired + Reload) and #985 (opt-in CODEG_AUTH=proxy-jwt, builds on #984, so please review #984 first). Both were run behind a live Cloudflare Access application before posting; the results and the one gap (Reload after a natural expiry) are in the PR texts. Configuration naming and desktop scope are open in #985, as you suggested.
A clarification on the scope of #985, since it affects how it fits multi-user setups.
The proxy-jwt mode answers one question: is this a valid assertion from this proxy for this application? It verifies the signature against the proxy's published keys, iss, aud and exp/nbf. It does not read email, sub or groups, so it does not choose users itself. Who may get in is decided by the proxy's own policy; with Cloudflare Access, that is the application's Access policy (in our setup, one Entra group). The aud check is what binds the assertion to the Codeg application, so a valid assertion that the same user got for another application behind the same team is refused.
If you would like Codeg to choose users as well, we can add that on top, depending on the direction you prefer. For example:
an allowlist of identities, such as CODEG_PROXY_AUTH_ALLOWED_EMAILS, matched against the verified assertion's email, or
passing the verified identity through to the app (for example to show who is signed in), as a base for per-user behaviour later.
We kept it out of #985 on purpose. For a single-user server behind a proxy, the proxy's policy is already the user list, and a second list in Codeg has to be kept in step with it. Happy to do either as a follow-up PR, or to fold it into #985 if you would rather review it in one go.
@MagTer thank you for splitting this into two clean PRs, and for the clarification above. Here are our answers to the open questions, so nothing waits on us:
Naming.CODEG_AUTH=proxy-jwt with CODEG_PROXY_AUTH_HEADER, CODEG_PROXY_AUTH_JWKS_URL, CODEG_PROXY_AUTH_ISSUER and CODEG_PROXY_AUTH_AUDIENCE is fine as it is. It follows the existing CODEG_* style, and none of the names is taken.
Allowlist or identity pass-through. Neither for now, and please don't fold them into feat(server): optional sign-in through an identity-aware proxy (CODEG_AUTH=proxy-jwt) #985. Codeg has one access token and one data directory, with no user accounts yet, so there is nothing to attach an identity to. The proxy's policy already decides who gets a valid assertion for this application, and keeping the list there avoids maintaining it in two places. What we do want is that boundary written down for operators, so I added a short "Who gets in" section to docs/proxy-auth.md in feat(server): optional sign-in through an identity-aware proxy (CODEG_AUTH=proxy-jwt) #985. If per-user features come later, an allowlist can be a small follow-up.
ring vs jsonwebtoken. Keep ring. It is already compiled into every build through rustls and sqlx, while jsonwebtoken 11 only offers the aws_lc_rs and rust_crypto backends, so switching would bring in a second crypto stack.
Docs site. The source of docs.codeg.app is public at https://github.com/xintaofei/codeg-docs. The environment variables are listed in getting-started/configuration.md and zh/getting-started/configuration.md. A PR there is welcome.
I've also reviewed both PRs for production readiness; the details are in the PR threads. #984 is ready as it is. On #985 I pushed a few commits on top of yours. The most important one closes a cross-site hole that cookie-based sign-in opens; see the comment there.
Activity
xintaofei commentedon Oct 18, 2026
Thanks for taking the time to investigate this and write up such a clear, detailed report, @MagTer!
I've checked the current implementation, and it matches your diagnosis: authentication is token-only, API calls and health probes don't send
X-Requested-With, and network failures during health checks keep the UI in the reconnect loop.We'd be very happy to accept a PR along these lines, including better handling of expired proxy sessions and an opt-in, JWT-validated proxy authentication mode while keeping the existing token mode as the default. The fail-closed approach and tests you propose make sense. We can work out the configuration naming and desktop settings scope in the PR.
Thanks again for the thoughtful proposal and for offering to contribute!
MagTer commentedon Oct 19, 2026
Opened both parts: #984 (expired proxy session → Sign-in expired + Reload) and #985 (opt-in
CODEG_AUTH=proxy-jwt, builds on #984, so please review #984 first). Both were run behind a live Cloudflare Access application before posting; the results and the one gap (Reload after a natural expiry) are in the PR texts. Configuration naming and desktop scope are open in #985, as you suggested.MagTer commentedon Oct 19, 2026
A clarification on the scope of #985, since it affects how it fits multi-user setups.
The proxy-jwt mode answers one question: is this a valid assertion from this proxy for this application? It verifies the signature against the proxy's published keys,
iss,audandexp/nbf. It does not reademail,subor groups, so it does not choose users itself. Who may get in is decided by the proxy's own policy; with Cloudflare Access, that is the application's Access policy (in our setup, one Entra group). Theaudcheck is what binds the assertion to the Codeg application, so a valid assertion that the same user got for another application behind the same team is refused.If you would like Codeg to choose users as well, we can add that on top, depending on the direction you prefer. For example:
CODEG_PROXY_AUTH_ALLOWED_EMAILS, matched against the verified assertion'semail, orWe kept it out of #985 on purpose. For a single-user server behind a proxy, the proxy's policy is already the user list, and a second list in Codeg has to be kept in step with it. Happy to do either as a follow-up PR, or to fold it into #985 if you would rather review it in one go.
xintaofei commentedon Oct 19, 2026
@MagTer thank you for splitting this into two clean PRs, and for the clarification above. Here are our answers to the open questions, so nothing waits on us:
CODEG_AUTH=proxy-jwtwithCODEG_PROXY_AUTH_HEADER,CODEG_PROXY_AUTH_JWKS_URL,CODEG_PROXY_AUTH_ISSUERandCODEG_PROXY_AUTH_AUDIENCEis fine as it is. It follows the existingCODEG_*style, and none of the names is taken.codeg-serveronly, configured through the environment, as feat(server): optional sign-in through an identity-aware proxy (CODEG_AUTH=proxy-jwt) #985 does it. No settings entry and nothing in the desktop app for now; we can add that later if people ask for it.docs/proxy-auth.mdin feat(server): optional sign-in through an identity-aware proxy (CODEG_AUTH=proxy-jwt) #985. If per-user features come later, an allowlist can be a small follow-up.ringvsjsonwebtoken. Keepring. It is already compiled into every build through rustls and sqlx, whilejsonwebtoken11 only offers theaws_lc_rsandrust_cryptobackends, so switching would bring in a second crypto stack.getting-started/configuration.mdandzh/getting-started/configuration.md. A PR there is welcome.I've also reviewed both PRs for production readiness; the details are in the PR threads. #984 is ready as it is. On #985 I pushed a few commits on top of yours. The most important one closes a cross-site hole that cookie-based sign-in opens; see the comment there.
fix(web): an expired proxy session asks for a reload instead of recon…
feat(server): optional sign-in through an identity-aware proxy (CODEG…