From 8e9afbcf9288f46d61bfb0c665d7fb9c238bc6b9 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Thu, 11 Jun 2026 22:32:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Validate=20JWT=20token=20exp=20claim=20?= =?UTF-8?q?=E2=80=94=20was=20silently=20ignored=20after=20migration=20to?= =?UTF-8?q?=20joserfc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit joserfc.jwt.decode() does not validate the exp claim by default, so expired tokens were accepted indefinitely. Added explicit check after decoding. See issue #2781. --- gns3server/services/authentication.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gns3server/services/authentication.py b/gns3server/services/authentication.py index 9b9c6ffa7..42c980415 100644 --- a/gns3server/services/authentication.py +++ b/gns3server/services/authentication.py @@ -17,6 +17,7 @@ from joserfc import jwt from joserfc.jwk import OctKey from joserfc.errors import JoseError +import time from datetime import datetime, timedelta, timezone import bcrypt @@ -80,6 +81,10 @@ class AuthService: username: str = payload.claims.get("sub") if username is None: raise credentials_exception + # Validate the exp claim — joserfc does not validate time-based claims by default + token_exp: int = payload.claims.get("exp", 0) + if token_exp and time.time() > token_exp: + raise credentials_exception token_version: int = payload.claims.get("ver", 0) token_data = TokenData(username=username, token_version=token_version) except (JoseError, ValidationError, ValueError):