diff --git a/qcarchivetesting/conda-envs/fulltest_qcportal.yaml b/qcarchivetesting/conda-envs/fulltest_qcportal.yaml index 0ab7545a0..ec271e322 100644 --- a/qcarchivetesting/conda-envs/fulltest_qcportal.yaml +++ b/qcarchivetesting/conda-envs/fulltest_qcportal.yaml @@ -16,7 +16,7 @@ dependencies: - tabulate - tqdm - pandas - - pyjwt + - pyjwt>=2.10.0 - packaging - typing_extensions - python-dateutil diff --git a/qcarchivetesting/conda-envs/fulltest_server.yaml b/qcarchivetesting/conda-envs/fulltest_server.yaml index 83eda7fbb..fd45bc62d 100644 --- a/qcarchivetesting/conda-envs/fulltest_server.yaml +++ b/qcarchivetesting/conda-envs/fulltest_server.yaml @@ -29,6 +29,7 @@ dependencies: # QCFractal dependencies - flask - flask-jwt-extended + - pyjwt>=2.10.0 - waitress - bcrypt - sqlalchemy>=2.0 diff --git a/qcarchivetesting/conda-envs/fulltest_snowflake.yaml b/qcarchivetesting/conda-envs/fulltest_snowflake.yaml index 7763d0f26..a101d78a8 100644 --- a/qcarchivetesting/conda-envs/fulltest_snowflake.yaml +++ b/qcarchivetesting/conda-envs/fulltest_snowflake.yaml @@ -32,6 +32,7 @@ dependencies: # QCFractal dependencies - flask - flask-jwt-extended + - pyjwt>=2.10.0 - waitress - bcrypt - sqlalchemy>=2.0 diff --git a/qcarchivetesting/conda-envs/fulltest_worker.yaml b/qcarchivetesting/conda-envs/fulltest_worker.yaml index 1551ed8da..05b29ac49 100644 --- a/qcarchivetesting/conda-envs/fulltest_worker.yaml +++ b/qcarchivetesting/conda-envs/fulltest_worker.yaml @@ -19,7 +19,7 @@ dependencies: - tabulate - tqdm - pandas - - pyjwt + - pyjwt>=2.10.0 - packaging - typing_extensions - python-dateutil diff --git a/qcfractal/pyproject.toml b/qcfractal/pyproject.toml index 89fc563bc..f032af9a4 100644 --- a/qcfractal/pyproject.toml +++ b/qcfractal/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ dependencies = [ "flask", "flask-jwt-extended", + "pyjwt >=2.10", "waitress", "bcrypt", "sqlalchemy >=2.0", diff --git a/qcfractal/qcfractal/flask_app/handlers.py b/qcfractal/qcfractal/flask_app/handlers.py index 01404cec8..75b3c948f 100644 --- a/qcfractal/qcfractal/flask_app/handlers.py +++ b/qcfractal/qcfractal/flask_app/handlers.py @@ -10,6 +10,7 @@ set_access_cookies, get_jwt_request_location, ) +from jwt.exceptions import InvalidSubjectError from werkzeug.exceptions import InternalServerError, HTTPException from qcfractal.flask_app import storage_socket @@ -172,3 +173,10 @@ def handle_auth_error(error): def handle_compute_manager_error(error: ComputeManagerError): # Handle compute manager errors return jsonify(msg=str(error)), 400 + + +@home_v1.app_errorhandler(InvalidSubjectError) +def handle_old_tokens(error): + # Handle old tokens that have integers as the subject + # Just say they have been expired, and you need to login again + return jsonify(msg="Token has expired"), 401 diff --git a/qcfractal/qcfractal/flask_app/helpers.py b/qcfractal/qcfractal/flask_app/helpers.py index 5063dd678..5c5bec71e 100644 --- a/qcfractal/qcfractal/flask_app/helpers.py +++ b/qcfractal/qcfractal/flask_app/helpers.py @@ -11,6 +11,7 @@ create_access_token, create_refresh_token, ) +from jwt.exceptions import InvalidSubjectError from werkzeug.exceptions import BadRequest, Forbidden from qcfractal.flask_app import storage_socket @@ -78,6 +79,10 @@ def assert_role_permissions(requested_action: str): role = claims.get("role", None) groups = claims.get("groups", None) + # user_id is stored in the JWT as a string + if user_id is not None: + user_id = int(user_id) + subject = {"user_id": user_id, "username": username} # Pull the first part of the URL (ie, /api/v1/molecule/a/b/c -> /api/v1/molecule) @@ -105,7 +110,7 @@ def access_token_from_user(user_info: UserInfo, role_info: RoleInfo): Creates a JWT access token from user/role information """ return create_access_token( - identity=user_info.id, + identity=str(user_info.id), additional_claims={ "username": user_info.username, "role": user_info.role, @@ -161,7 +166,7 @@ def login_and_get_jwt(get_refresh_token: bool) -> Tuple[str, Optional[str]]: access_token = access_token_from_user(user_info, role_info) if get_refresh_token: - refresh_token = create_refresh_token(identity=user_info.id) + refresh_token = create_refresh_token(identity=str(user_info.id)) else: refresh_token = None