louislam/uptime-kuma · warning · Error

The token is invalid due to password change or old token

Error message

The token is invalid due to password change or old token

What it means

Thrown by the 'loginByToken' socket handler after the JWT is successfully decoded and the user is found and active. The token carries an 'h' claim (shake256 of the user's password hash); if it no longer matches the current stored password hash, the token is considered stale. The handler's catch block converts ANY thrown error into the generic i18n callback {ok:false, msg:'authInvalidToken', msgi18n:true}.

Source

Thrown at server/server.js:416

        // Public Socket API
        // ***************************

        socket.on("loginByToken", async (token, callback) => {
            const clientIP = await server.getClientIP(socket);

            log.info("auth", `Login by token. IP=${clientIP}`);

            try {
                let decoded = jwt.verify(token, server.jwtSecret);

                log.info("auth", "Username from JWT: " + decoded.username);

                let user = await R.findOne("user", " username = ? AND active = 1 ", [decoded.username]);

                if (user) {
                    // Check if the password changed
                    if (decoded.h !== shake256(user.password, SHAKE256_LENGTH)) {
                        throw new Error("The token is invalid due to password change or old token");
                    }

                    log.debug("auth", "afterLogin");
                    await afterLogin(socket, user);
                    log.debug("auth", "afterLogin ok");

                    log.info("auth", `Successfully logged in user ${decoded.username}. IP=${clientIP}`);

                    callback({
                        ok: true,
                    });
                } else {
                    log.info("auth", `Inactive or deleted user ${decoded.username}. IP=${clientIP}`);

                    callback({
                        ok: false,
                        msg: "authUserInactiveOrDeleted",
                        msgi18n: true,

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. On receiving authInvalidToken / ok:false, clear the stored token and redirect the user to the login screen.
  2. After a password change, have all clients discard their cached JWT (the server already disconnects other sockets).
  3. Do not reuse tokens across instances with different jwtSecret or restored DBs.
  4. Ensure the client does not pin a token forever; re-authenticate on this error.

Example fix

// before
socket.emit('loginByToken', token, (res) => { if (!res.ok) console.warn(res.msg); });

// after
socket.emit('loginByToken', token, (res) => {
  if (!res.ok) { localStorage.removeItem('token'); router.push('/login'); }
});
Defensive patterns

Strategy: fallback

Validate before calling

// Before relying on a stored token, be ready to re-authenticate on failure
function isTokenLikelyStale(token, knownHashFingerprint) {
  try { const d = jwt.decode(token); return !d || d.h !== knownHashFingerprint; }
  catch { return true; }
}

Type guard

function isStaleTokenResponse(res) {
  return res && res.ok === false && res.msg === 'authInvalidToken';
}

Try / catch

socket.emit('loginByToken', token, (res) => {
  if (!res.ok) {
    // token invalid: password changed, old token, or user inactive
    localStorage.removeItem('token');
    return router.push('/login');
  }
});

Prevention

When it happens

Trigger: The client presents a JWT saved before the user's password was changed/reset; the password hash in the DB changed; or an old token from a previous secret rotation is replayed. The client ultimately receives 'authInvalidToken'.

Common situations: User changed their password on another device; admin reset the password; the DB was restored from a backup with different hashes; token persisted in localStorage across a password change; jwtSecret changed (though that usually fails jwt.verify first).

Understand the failure class

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/d824c3ba09b59894. Report an issue: GitHub.