{"record":{"id":"d824c3ba09b59894","repo":"louislam/uptime-kuma","slug":"the-token-is-invalid-due-to-password-change-or-old","errorCode":null,"errorMessage":"The token is invalid due to password change or old token","messagePattern":"The token is invalid due to password change or old token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"server/server.js","lineNumber":416,"sourceCode":"        // Public Socket API\n        // ***************************\n\n        socket.on(\"loginByToken\", async (token, callback) => {\n            const clientIP = await server.getClientIP(socket);\n\n            log.info(\"auth\", `Login by token. IP=${clientIP}`);\n\n            try {\n                let decoded = jwt.verify(token, server.jwtSecret);\n\n                log.info(\"auth\", \"Username from JWT: \" + decoded.username);\n\n                let user = await R.findOne(\"user\", \" username = ? AND active = 1 \", [decoded.username]);\n\n                if (user) {\n                    // Check if the password changed\n                    if (decoded.h !== shake256(user.password, SHAKE256_LENGTH)) {\n                        throw new Error(\"The token is invalid due to password change or old token\");\n                    }\n\n                    log.debug(\"auth\", \"afterLogin\");\n                    await afterLogin(socket, user);\n                    log.debug(\"auth\", \"afterLogin ok\");\n\n                    log.info(\"auth\", `Successfully logged in user ${decoded.username}. IP=${clientIP}`);\n\n                    callback({\n                        ok: true,\n                    });\n                } else {\n                    log.info(\"auth\", `Inactive or deleted user ${decoded.username}. IP=${clientIP}`);\n\n                    callback({\n                        ok: false,\n                        msg: \"authUserInactiveOrDeleted\",\n                        msgi18n: true,","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/server.js#L398-L434","documentation":"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}.","triggerScenarios":"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'.","commonSituations":"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).","solutions":["On receiving authInvalidToken / ok:false, clear the stored token and redirect the user to the login screen.","After a password change, have all clients discard their cached JWT (the server already disconnects other sockets).","Do not reuse tokens across instances with different jwtSecret or restored DBs.","Ensure the client does not pin a token forever; re-authenticate on this error."],"exampleFix":"// before\nsocket.emit('loginByToken', token, (res) => { if (!res.ok) console.warn(res.msg); });\n\n// after\nsocket.emit('loginByToken', token, (res) => {\n  if (!res.ok) { localStorage.removeItem('token'); router.push('/login'); }\n});","handlingStrategy":"fallback","validationCode":"// Before relying on a stored token, be ready to re-authenticate on failure\nfunction isTokenLikelyStale(token, knownHashFingerprint) {\n  try { const d = jwt.decode(token); return !d || d.h !== knownHashFingerprint; }\n  catch { return true; }\n}","typeGuard":"function isStaleTokenResponse(res) {\n  return res && res.ok === false && res.msg === 'authInvalidToken';\n}","tryCatchPattern":"socket.emit('loginByToken', token, (res) => {\n  if (!res.ok) {\n    // token invalid: password changed, old token, or user inactive\n    localStorage.removeItem('token');\n    return router.push('/login');\n  }\n});","preventionTips":["On authInvalidToken, always clear the stored token and re-login.","After a password change, force all clients to drop cached tokens.","Do not persist tokens across DB restores or jwtSecret rotations.","Treat this as an expected auth flow, not a bug."],"tags":["auth","jwt","password-change","socket","session"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}