louislam/dockge · 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

During JWT-based auto-login (the 'loginByToken' socket event), the decoded token carries an 'h' field: a shake256 hash of the user's password hash at token-issue time. If the stored password hash no longer matches (the password was changed or the token is stale from a previous incarnation), the handler throws this Error so the client is forced to re-authenticate with credentials.

Source

Thrown at backend/socket-handlers/main-socket-handler.ts:83

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

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

            try {
                const decoded = jwt.verify(token, server.jwtSecret) as JWTDecoded;

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

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

                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 server.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",

View on GitHub (pinned to f809ae192b)

Solutions

  1. Discard the stored token and log in again with username/password to obtain a fresh JWT.
  2. If the password was intentionally changed, no repair is needed — the old token is invalid by design.
  3. Clear client-side token storage (localStorage/cookie) when handling this error.

Example fix

// before
socket.emit('loginByToken', savedToken, cb); // may throw
// after
socket.emit('loginByToken', savedToken, (res) => {
    if (!res.ok) localStorage.removeItem('token');
});
Defensive patterns

Strategy: fallback

Validate before calling

// Detect stale token by decoding expiry locally before use
function tokenLooksValid(token) {
    try {
        const payload = JSON.parse(atob(token.split('.')[1]));
        return payload.exp * 1000 > Date.now();
    } catch { return false; }
}

Try / catch

socket.emit('loginByToken', token, (res) => {
    if (!res.ok && /invalid/.test(res.msg || '')) {
        localStorage.removeItem('token');
        showLoginForm();
    }
});

Prevention

When it happens

Trigger: Emitting 'loginByToken' with a JWT minted before a password change, or a token persisted from an earlier Dockge install/database where the user's password hash differed.

Common situations: Browser keeps a saved token while the admin rotates the password; restoring an old database while clients retain old tokens; token copied from another environment with different password hashes.

Understand the failure class

Related errors


AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31). Data as JSON: /api/errors/411991175265039e. Report an issue: GitHub.