Problem/Motivation
RefreshTokenEntityNormalizer::normalize() returns only value and expire. Access tokens and auth codes go through TokenEntityNormalizer, which also sets client. Refresh tokens don't, so every refresh_token row has an empty client field.
As a result, nothing can find refresh tokens by client:
ExpiredCollector::collectForClient($client, TRUE)never returns refresh tokens, even though$include_refreshexists for exactly that.- Deleting a consumer, or any cleanup keyed on the client, leaves its refresh tokens behind.
Steps to reproduce
- Complete an authorization code flow for a consumer with the
refresh_tokengrant. - Load the new
oauth2_tokenentity with bundlerefresh_token. $token->get('client')->isEmpty()isTRUE.\Drupal::service('simple_oauth.expired_collector')->collectForClient($consumer, TRUE)returns only the access token.
Proposed resolution
Set client in RefreshTokenEntityNormalizer::normalize() from the access token the refresh token belongs to:
$client = $token_entity->getAccessToken()->getClient();
assert($client instanceof ClientEntityInterface);
return [
'client' => ['target_id' => $client->getDrupalEntity()->id()],
'value' => $token_entity->getIdentifier(),
'expire' => $token_entity->getExpiryDateTime()->format('U'),
];
Existing refresh tokens can't be backfilled, because the row has no link to its access token. They expire on their own (the default is 14 days).
Remaining tasks
- MR with a kernel test asserting
collectForClient($client, TRUE)returns the refresh token.
Data model changes
None. The client field already exists on every bundle.
AI disclosure
AI-Generated: Yes. I drafted this issue summary with Claude Code and reviewed it before filing.
Issue fork simple_oauth-3630881
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 #3
mglamanComment #4
attilatilman commentedComment #5
vidit.anjaria commentedComment #8
bojan_dev commentedLooks good, rebased and merged!