Bug description
Authentication tokens with an explicit expiration timestamp remain in oc_authtoken and continue to be shown in Settings > Security > Devices & sessions and by occ user:auth-tokens:list after expires is in the past.
This is easy to reproduce with the Passwords app. It creates short-lived Web UI tokens as IToken::PERMANENT_TOKEN and then assigns an explicit expires value. Regular use therefore accumulates many unusable "Passwords Session ..." entries.
On one instance, read-only diagnostics found:
passwords_tokens=151 active=1 expired=150
oldest_activity=2026-05-13T09:51:43
newest_activity=2026-07-05T20:10:55
The active token had a future expiration timestamp. Every other token had expires <= UNIX_TIMESTAMP(). Cron is configured and runs every five minutes.
This is distinct from #11655: bulk deletion would make manual cleanup easier, but explicitly expired tokens should not remain listed or be retained as if they were usable.
Steps to reproduce
- Install and enable the Passwords app.
- Open Passwords in the Web UI and authorize a session.
- Let the session token expire, then open/authorize Passwords again.
- Repeat a few times.
- Inspect Settings > Security > Devices & sessions, run
occ user:auth-tokens:list <uid>, or inspect expiration state without reading token material:
SELECT
COUNT(*) AS total,
SUM(expires IS NULL OR expires = 0 OR expires > UNIX_TIMESTAMP()) AS active,
SUM(expires > 0 AND expires <= UNIX_TIMESTAMP()) AS expired
FROM oc_authtoken
WHERE uid = '<uid>'
AND name LIKE 'Passwords Session %';
Expected behavior
Tokens whose explicit expires timestamp is in the past should be removed by token cleanup, or at minimum excluded from the Devices & sessions UI and user:auth-tokens:list.
Actual behavior
Expired tokens remain stored and listed.
In the current server implementation:
TokenCleanupJob runs IProvider::invalidateOldTokens().
PublicKeyTokenProvider::invalidateOldTokens() removes PERMANENT_TOKEN rows according to last_activity and token_auth_token_retention (default: 365 days).
- The explicit
expires value is not considered by that cleanup path.
PublicKeyTokenMapper::getTokenByUser() returns the rows without filtering expired tokens.
As a result, a short-lived token represented as PERMANENT_TOKEN + expires can clutter the user's security page for up to a year after it has become unusable.
Environment
- Nextcloud Server: 34.0.1.2
- Installation: official Docker FPM image with additional packages
- PHP: 8.5.7
- Database: MariaDB 11
- Background jobs: cron, every 5 minutes
- Passwords app: 2026.6.20
- Passwords session lifetime: 3600 seconds
- Passwords client-side encryption: enabled
Additional context
The Passwords documentation describes these "Passwords Session ..." tokens as automatically generated and valid only for a short period, and says they can be deleted safely after use:
https://git.mdns.eu/nextcloud/passwords/-/wikis/Users/F.A.Q
No token values, hashes, passwords, hostnames, public addresses, or user-identifying data are included in this report.
Bug description
Authentication tokens with an explicit expiration timestamp remain in
oc_authtokenand continue to be shown in Settings > Security > Devices & sessions and byocc user:auth-tokens:listafterexpiresis in the past.This is easy to reproduce with the Passwords app. It creates short-lived Web UI tokens as
IToken::PERMANENT_TOKENand then assigns an explicitexpiresvalue. Regular use therefore accumulates many unusable "Passwords Session ..." entries.On one instance, read-only diagnostics found:
The active token had a future expiration timestamp. Every other token had
expires <= UNIX_TIMESTAMP(). Cron is configured and runs every five minutes.This is distinct from #11655: bulk deletion would make manual cleanup easier, but explicitly expired tokens should not remain listed or be retained as if they were usable.
Steps to reproduce
occ user:auth-tokens:list <uid>, or inspect expiration state without reading token material:Expected behavior
Tokens whose explicit
expirestimestamp is in the past should be removed by token cleanup, or at minimum excluded from the Devices & sessions UI anduser:auth-tokens:list.Actual behavior
Expired tokens remain stored and listed.
In the current server implementation:
TokenCleanupJobrunsIProvider::invalidateOldTokens().PublicKeyTokenProvider::invalidateOldTokens()removesPERMANENT_TOKENrows according tolast_activityandtoken_auth_token_retention(default: 365 days).expiresvalue is not considered by that cleanup path.PublicKeyTokenMapper::getTokenByUser()returns the rows without filtering expired tokens.As a result, a short-lived token represented as
PERMANENT_TOKEN + expirescan clutter the user's security page for up to a year after it has become unusable.Environment
Additional context
The Passwords documentation describes these "Passwords Session ..." tokens as automatically generated and valid only for a short period, and says they can be deleted safely after use:
https://git.mdns.eu/nextcloud/passwords/-/wikis/Users/F.A.Q
No token values, hashes, passwords, hostnames, public addresses, or user-identifying data are included in this report.