v2.4.7
v2.4.7 — Bug fix: OIDC redirect_uri http:// behind proxies that only send X-Forwarded-Proto
Fixes #28 reported by @hbjydev (Kanidm + cloudflared).
What broke
OIDC sign-in failed at the IdP with a redirect_uri mismatch error for users behind reverse proxies that send X-Forwarded-Proto: https but do not send X-Forwarded-Host (cloudflared, many nginx configs, some Caddy setups). The plugin built redirect_uri=http://... even though the browser-facing origin was HTTPS, and the IdP correctly rejected per the OAuth 2.0 spec's exact-match requirement.
Why
BuildRedirectUri required both X-Forwarded-Proto AND X-Forwarded-Host to be present before honouring either. When only Proto came through, the combined check failed and scheme fell back to Request.Scheme — which is "http" because TLS terminates upstream in containerised deployments.
Fix
Each forwarded header is now evaluated independently:
X-Forwarded-Protois honoured for scheme regardless ofX-Forwarded-Host's presenceX-Forwarded-Hostis honoured for host regardless ofX-Forwarded-Proto's presence- Either header's absence falls through to the direct request value
The proxyTrusted gate (peer must be in TrustedProxyCidrs) is unchanged for both header checks — attackers connecting directly cannot inject either. The SEC-H1 boundary is intact.
Implementation notes
- URL-construction logic extracted to a pure static helper
Services/OidcRedirectUriBuilder.csso it's testable withoutHttpContextmocking. - 5 new regression tests in
OidcRedirectUriBuilderTests.cs:- No proxy headers → direct scheme/host (baseline)
- Proto from trusted proxy → https propagates (the #28 fix)
- Proto from untrusted peer → ignored (security — Proto gate)
- Host from untrusted peer → ignored (security — redirect_uri-poisoning regression test)
- Both headers from trusted proxy → both honoured (Traefik-style positive)
- Tests 3 and 4 are the load-bearing security ones — if a future refactor weakens the
proxyTrustedgate, they fire. - Verified Google OIDC sign-in still works after the refactor.
Test suite
155/155 pass (was 150, +5 new in OidcRedirectUriBuilderTests). Clean build with TreatWarningsAsErrors=true.
Verify the release
cosign verify-blob \
--certificate Jellyfin.Plugin.TwoFactorAuthv2.4.7.0.zip.pem \
--signature Jellyfin.Plugin.TwoFactorAuthv2.4.7.0.zip.sig \
--certificate-identity-regexp 'https://github.com/ZL154/JellyfinSecurity/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
Jellyfin.Plugin.TwoFactorAuthv2.4.7.0.zip
SLSA build provenance: Jellyfin.Plugin.TwoFactorAuthv2.4.7.0.zip.intoto.jsonl — verify with gh attestation verify --owner ZL154 Jellyfin.Plugin.TwoFactorAuthv2.4.7.0.zip.