Problem/Motivation
When creating a new consumer with the client credentials grant, the form requires you to also input a User reference with description:
When no specific user is authenticated Drupal will use this user as the author of all the actions made.
This wording made me think the user account is only relevant when the user creates new content and that's the author that would be assigned. But the user is also used in more ways than this. The TokenAuthUser user object essentially wraps the specified user. When ->getRoles() is invoked, it returns the intersection of the user's roles and the scope's roles. This makes sense now that I think about it, because the purpose of the scope is essentially to define what the application/consumer has access to as a subset of what the actual user account has access to.*
*Note that this scoping system doesn't work that way for permissions, confusingly. E.g., the Permission scope seems additive to the permissions of the referenced User account. This is also confusing. Why does the permission scope do this but the Role scope doesn't?
I think this relationship should be made clearer on the configuration form. I initially assumed that I could create a client credentials consumer that acted as its own distinct user and the role and permission scopes would assign that user access to things on the site, but that's not how this works.
Steps to reproduce
Proposed resolution
Update the form description for the User field on client credentials to something like this:
When no specific user is authenticated Drupal will masquerade as this user.
Also figure out a way to clarify the additive nature of the Permission scope vs the intersection nature of the Role scope.
Remaining tasks
User interface changes
API changes
Data model changes
Issue fork simple_oauth-3590930
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
bkosborneComment #3
vidit.anjaria commentedComment #4
vidit.anjaria commentedComment #5
vidit.anjaria commentedComment #8
alex ua commentedComment #9
vidit.anjaria commentedComment #10
cobadger commentedComment #11
nikro commentedPicking this up 🫡
Comment #12
vidit.anjaria commentedComment #13
nikro commentedThanks @cobadger for the detailed review, and @alex ua for the original patch. I have pushed changes on top of the existing branch addressing all four threads.
AuthorizedRequestBaserather than only inClientCredentialsTest, as suggested. The first account created in an empty user table becomes uid 1, andSuperUserAccessPolicythen short-circuits every access check. Worth noting thatRefreshTokenTestandOpenIdConnectTestpass unchanged once they get an ordinary account, so they were not relying on the bypass either. The base fix therefore hardens all three with no behaviour change, and makes the per-subclass override unnecessary.SuperUserTest. It now loads uid 1 explicitly instead of depending on creation order, which also removes the ambiguity around the kernel reboot intestSuperUser(). The existingassertEquals('1', ...)stays as an intentional guard.testSaveConsumerWithNewSecret()into its owntestClientCredentialsFieldDescriptions(), with a fixture holding only what the descriptions need.scopesfield rather than growing theuser_iddescription further, as suggested.On that last point, I confirmed the asymmetry locally before wording it. With a Role granularity scope pointing at a role the selected user does not hold, the same token gives:
So permission checks pass while role membership checks quietly fail, with nothing to explain why.
Role::getPermissions()contributes the role's permissions unconditionally, whileTokenAuthUser::getRoles()intersects with the user's real roles.To be explicit about scope: this MR now resolves both halves of the report, the User field wording and the permission-versus-role asymmetry. Full test suite passes locally (90/90) and PHPCS is clean; the PHPStan finding in
Oauth2ClientCredentialsTokenCreateForm.phpis pre-existing on 6.1.x and untouched here.Cross-referencing #3589432: Access denied in JSONAPI for OAuth users, which documents the same asymmetry on the scope form. Different audience, same facts, so the wording in both should stay consistent.
Comment #14
nikro commentedPicking this up - will actually check mid-week.
Comment #15
vidit.anjaria commentedComment #16
pfrillingWorking on this today.
Comment #17
pfrillingThe changes all look good to me.
I made a quick wording change to the Scopes field description to address Alex's point 4 — the previous text conflated the unconditional permission grant with the separate role-membership check. The pipeline is green after a rebase.
Cross linking the related #3589432: Access denied in JSONAPI for OAuth users, which adds scope-form descriptions, a README section, and additional tests for the behavior documented here.
Drafted with help of an LLM
Comment #19
bojan_dev commentedNice work all!