fix: treat the OAuth2 user id as opaque - #389
Conversation
The token endpoint and the bearer auth module split the user id stored on an authorization code, access token or refresh token at the first colon and then resolved whatever user the remainder named. That is a leftover of the `login name:user id` format which only v0.5.0 - v0.5.2 ever wrote: e5b508c restored raw user id storage in 2022, so the parsers see a plain user id today, and a legacy pair value cannot reach them either - authorization codes expire after 10 minutes, access tokens after 1 hour, and refresh tokens were always stored with the already-split value. The parsing is therefore dead code, and a user id which legitimately contains a colon must not be reinterpreted. Treat the stored value as an opaque user id in all three places. The AuthModuleTest case which asserted the splitting behaviour is replaced by one asserting that the stored user id is used verbatim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>
phil-davis
left a comment
There was a problem hiding this comment.
This looks like a good idea (removing "dead/useless" code).
In practice, there won't be (and never were) usernames containing a colon such as "phil:davis". So the ability to opaquely process such usernames is nice-to-have, but usernames in ownCloud Classic already cannot contain a colon.
(usernames containing a colon would create hassles for Basic Auth API requests that tried to send a "username:password"!)
kw-fscheuer
left a comment
There was a problem hiding this comment.
Code review
No issues found. I verified the dead-code argument against the repo history rather than the description alone.
All three parsing sites are removed, and nothing else in lib/ splits a user id on a colon — a repo-wide search for explode/strstr leaves only the hostname split in lib/Utilities.php#L105-L108:
- lib/Controller/OAuthApiController.php#L193-L197 —
authorization_codegrant - lib/Controller/OAuthApiController.php#L231-L233 —
refresh_tokengrant - lib/AuthModule.php#L114-L117 —
authToken()
The compatibility argument holds:
- 4685ce0c wrote the pair format onto the authorization code and the implicit-grant access token only. Both records minted in
generateToken()already used the post-split value, so no refresh token ever stored a pair. - e5b508c3 restored raw
getUID()storage and left only the parsers behind, which is what this PR removes. git compareplaces 4685ce0 insidev0.5.0, and e5b508c outsidev0.5.2but insidev0.5.3— so thev0.5.0-v0.5.2range in the commit message is exact. Combined with the 10 minute / 1 hour expiries, no stored pair value can still reach the removed code.
One non-blocking test note: testAuthTokenKeepsUserIdWithColon asserts only that authToken() returns null, which would also pass if the token were rejected for an unrelated reason. Asserting that a colon-bearing user id resolves to that user would pin the behaviour down more tightly.
Generated with Claude Code
Description
OAuthApiController::generateToken()(both theauthorization_codeand therefresh_tokengrant) and
AuthModule::authToken()split the user id stored on an authorization code, accesstoken or refresh token at the first colon, and then resolved whatever user the remainder named:
This is a leftover of the
login name:user idformat that only v0.5.0 - v0.5.2 ever wrote.A user id is opaque, so one that legitimately contains a colon must not be reinterpreted as a
pair - the token would then be resolved to, and issued for, a different account than the one the
authorization code belongs to.
Why removing the parsing is safe
PageControllerstores the rawIUser::getUID()since e5b508c ("use userid on api calls,show username in login form", 2022-03-10), so nothing writes the pair format any more. The
login name survives only as the OIDC
login_hint.(
AuthorizationCode::EXPIRATION_TIME), access tokens after 1 hour(
AccessToken::EXPIRATION_TIME), and refresh tokens - which never expire - were alwayspersisted with the already-split value.
So this is a plain removal of dead code, with no migration or compatibility shim needed.
Changes
lib/Controller/OAuthApiController.php: drop the colon parsing in both grant paths.lib/AuthModule.php: drop it inauthToken(), where it ran on every bearer request.tests/unit/Controller/OAuthApiControllerTest.php: two regression tests asserting that acolon-bearing user id is passed to the user manager verbatim and ends up unchanged on the
issued access token, refresh token and in the
user_idresponse field.tests/unit/AuthModuleTest.php: theConcatUserIDcase asserted the splitting behaviour andis replaced by one asserting that the stored user id is used verbatim.
Testing
--testsuite unitrun inside a core checkout:OK (163 tests, 525 assertions)v0.6.1):OK (163 tests, 525 assertions)lib/change, exactly the three new tests failmake test-php-styleclean,make test-php-phpstanreports[OK] No errors.Note for the 10.x line
The same patch applies cleanly to
v0.6.1, but there is no maintenance branch for the 0.6 /ownCloud 10 line to target - one has to be cut from the tag first. Happy to open that PR once
the branch exists.