Voidauth upstream issue #390
Labels
No labels
bug
chore
documentation
enhancement
help
in-progress
question
ready-for-release
released
translation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
adam/gate#390
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
proxyAuthPathargument shift causes 500 on session expiry; unsafenew URL()inredirectUriAllowedvalidator amplifies the failureVersion:
1.12.0Git revision:2099ecb70f86e82f2507444055c40238a9b745b0Image:voidauth/voidauth:latest(digestsha256:e8c8cf1d…) Deployment: voidauth + caddy forward-auth, postgres backend, multipleproxy_authrows with non-nullmaxSessionLengthSummary
A real (not scanner) authenticated user hitting any forward-auth-protected service after their session age exceeds the row's
maxSessionLengthgets "Internal Server Error" instead of being re-prompted to log in. Two compounding bugs in the bundleddist/index.mjs:proxyAuthPathwith positional arguments shifted by one, producing a malformedredirect_uriredirectUriAllowedoverride forproxyauth_internal_clientuses an unsafenew URL()on the innerproxyauth_urlquery parameter, which throws on the malformed input from Bug AEither fix alone resolves the user-visible 500. I think both warrant fixing.
Bug A — argument shift in
proxyAuthPathcallThe function signature (
dist/index.mjs, function definition near byte 13261896 in this build):Three positional parameters:
baseUrl,redirectUrl,prompt.The buggy call site is in
proxyAuth()'s session-expiry branch (call site near byte 13263067):The call passes only 2 positional arguments where the function expects 3:
The intent appears to be
prompt=loginfor forced re-authentication. The arguments got shifted by one —"login"ends up encoded as theproxyauth_urlquery parameter, and the protected service URL ends up as the OIDC base URL.Concrete output
With
url2.href = "http://protected.example.com/":Plug the buggy values into
proxyAuthPath:This becomes the
redirect_urivalue sent to/oidc/auth. Two structural problems:baseUrlalready ends in/, and the function unconditionally adds another)proxyauth_url=login— a bare token, not a URLSuggested fix
Pass the correct arguments:
Or, more idiomatically, since the prefix
${config_default.APP_URL}is now redundant with the firstproxyAuthPatharg, the whole construction could be simplified.Other call sites worth auditing
proxyAuthPathis called from 6 sites in this build (function defined at byte 13261896; call sites at bytes 13263067, 13263545, 13264196, 13264651, 13264962, 13265287). Only the 13263067 site was audited end-to-end against a live reproduction. The other 5 may or may not have similar issues. Worth reviewing all 6 against the signature.Bug B — unsafe
new URL()inredirectUriAllowedvalidatorIn the
redirectUriAllowedoverride (single occurrence at byte 4251559, in theproxyauth_internal_clientbranch):The outer URI uses safe
URL.parse()(returnsnullon failure). The innerproxyauth_urlparameter uses throwingnew URL(). Whenproxyauth_urlcontains anything that fails URL parsing — e.g., a bare token like"login"per Bug A —new URL()throws, the throw propagates intoauthorizationErrorHandler, and the user gets 500.Stack trace (from production logs)
Suggested fix
Use
URL.parse()for consistency with the outer URI handling:Then the truthiness check
!!proxyAuthURLalready handles the parse-failure case correctly.Reproduction
proxy_authrow with non-nullmaxSessionLength(e.g.,10minutes)maxSessionLengthExpected: redirected to
/oidc/authfor re-authentication, then back to the service.Actual: 500 "Internal Server Error" at
/oidc/auth. Logs show the TypeError above.Workaround for affected operators
Setting
maxSessionLength = NULLon allproxy_authrows short-circuits the buggy code path in Bug A:Bug B remains latent but is no longer reachable via Bug A. Per-domain forced re-authentication is disabled; sessions expire only at the global TTL. Reversible via the admin UI.
Severity assessment (operator view)
The condition is silently pervasive: any deployment with non-null
maxSessionLengthonproxy_authrows hits this on every session expiry, across every protected subdomain. The error logs don't include request context, which makes diagnosis from logs alone non-trivial — the bug surfaces as aTypeErrorwith no indication of which request triggered it.I have replicated the issue locally, and will release a patch shortly
Should be fixed now in v1.12.1