#6006·traccar

Re-sharing a device/group reuses the temporary user but never updates its expirationTime, causing all subsequent share tokens to fail with "User has expired"

Author: xnetinhoCreated Aug 30, 2026Updated Aug 30, 2026

In ShareResource.share(), the temporary share user is looked up by a deterministic email (userEmail:uniqueId for devices, userEmail:group:<id> for groups). When that user already exists (a re-share of the same object), the code skips the if (share == null) block, which means share.setExpirationTime(expiration) is never called for existing temporary users.

While the token itself is generated with the correct new expiration (tokenManager.generateToken(share.getId(), expiration)), the authentication flow in LoginService.login(token) fetches the user from the database and calls checkUserEnabled(user) -> checkDisabled(). This method validates the user's expirationTime directly from the database record (which still holds the old, expired date), completely ignoring the token's validity.

As a result, once the first share expires, every subsequent share token generated for the same object fails with SecurityException("User has expired") (HTTP 401).

Steps to reproduce:

  1. Send a POST /api/share/device with a short expiration (e.g., 10 minutes).
  2. Authenticate with the returned token — it works successfully.
  3. Wait for the expiration time to pass.
  4. Send POST /api/share/device again for the exact same device, setting a new expiration.
  5. Authenticate with the newly returned token — fails with HTTP 401 "User has expired".

Expected behavior: A re-share should update the temporary user's expirationTime in the database so that the database record is in sync with the new token expiration, allowing the new token to authenticate successfully.

Suggested fix:

⚠️ Note: The following fix was formulated with the assistance of AI. It has not been locally compiled or validated and is provided purely as a conceptual guide to help locate and fix the issue, not as a ready-to-merge Pull Request.

In ShareResource.java, specifically in the share(...) method, add an else branch to update the expiration time in the storage:

java
if (share == null) {
    // ... existing user creation logic ...
    share.setExpirationTime(expiration);
    // ...
    share.setId(storage.addObject(share, new Request(new Columns.All())));
    // ...
} else {
    // Update the expiration time in the database for the existing temporary user
    share.setExpirationTime(expiration);
    storage.updateObject(share, new Request(
            new Columns.Include("expirationTime"),
            new Condition.Equals("id", share.getId())
    ));
}