fix: pass all OAuth params in consent redirect to prevent session race#630
Open
cbcoutinho wants to merge 2 commits intoH2CK:masterfrom
Open
fix: pass all OAuth params in consent redirect to prevent session race#630cbcoutinho wants to merge 2 commits intoH2CK:masterfrom
cbcoutinho wants to merge 2 commits intoH2CK:masterfrom
Conversation
…sent The consent flow redirects back to /apps/oidc/authorize with only client_id and scope in the URL. Version 1.16.2 changed the session fallback to only trigger when client_id is empty, which means state, response_type, and redirect_uri are lost after consent, causing a 500. Restore the 1.16.1 behavior of falling back to session when ANY critical parameter is missing, but with per-parameter guards so URL-provided values are preserved. This fixes both: - Consent redirect (has client_id but missing state/response_type/redirect_uri) - Implicit flow (missing client_id, as in Guacamole case from H2CK#626) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The consent grant redirect only passed client_id and scope, relying on session fallback in the authorize endpoint. This caused intermittent 500 errors on NC 32 (PHP 8.4) when session values were lost between session->close() and the subsequent GET request — a race condition. Now ConsentController::grant() includes all OAuth params (state, response_type, redirect_uri, nonce, resource, code_challenge, code_challenge_method) in the redirect URL, eliminating the session dependency entirely. Also adds a guard in LoginRedirectorController::authorize() that returns a 400 error if critical params are still missing after session fallback, preventing unhandled 500s from trim(null) or matchRedirectUri(null). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
client_idandscope, eliminating the session dependency that causes intermittent 500 errorsLoginRedirectorController::authorize()to return a 400 error when critical OAuth params are missing after session fallbackProblem
After a user grants consent,
ConsentController::grant()redirects back to the authorize endpoint with onlyclient_idandscopein the URL. The authorize endpoint then relies on PHP session fallback to recoverstate,response_type,redirect_uri, and other critical OAuth parameters.This works most of the time, but fails intermittently because:
$this->session->close()is called before the redirect (line 212)/authorizeopens the session againmatchRedirectUri(null, ...)→ TypeErrortrim(null)→ E_DEPRECATED in PHP 8.4 (potentially promoted to error)This is especially prevalent on Nextcloud 32 with PHP 8.4, where
trim(null)deprecation handling is stricter.History
The consent feature was introduced in v1.11.0 (1262cfe, PR #586). The bug has been present since then —
ConsentController::grant()has always only passedclient_idandscopein the redirect URL.Two previous fixes attempted to address symptoms of this bug:
authorize()fromif (empty($client_id))toif (empty($client_id) || empty($state) || empty($response_type) || empty($redirect_uri)), so the fallback would trigger when consent only passed 2 params. However, this broke the non-consent flow wherestate/response_type/redirect_uriare legitimately present as URL params.if (empty($client_id)), which restored the non-consent flow but left the consent race condition unfixed.Neither fix addressed the root cause:
ConsentController::grant()not passing the full set of OAuth parameters. This PR fixes that directly.Affected versions
v1.11.0 through v1.16.2 (current).
Solution
ConsentController::grant() — pass all params in URL
array_filter()omits null values so optional params (resource, code_challenge) don't appear as empty strings in the URL.Since all critical params are now in the URL, the session fallback in
authorize()(guarded byif (empty($client_id))) is no longer needed for the consent flow — but remains functional for the initial login redirect flow where it's still required.LoginRedirectorController::authorize() — null safety guard (defense in depth)
After the session fallback block, a guard checks if critical params are still missing and returns a meaningful 400 error instead of letting downstream code crash with a 500:
Test plan
This PR was generated with the help of AI, and reviewed by a Human