From 9b5e1d82cd9b296a3da1950b8dbc319aae5c60d5 Mon Sep 17 00:00:00 2001 From: bsatoriu Date: Wed, 14 Jan 2026 16:56:14 -0800 Subject: [PATCH 1/4] make member_session.session_key unique; optimize member_session logs --- api/auth/cas_auth.py | 11 +++++------ api/models/member_session.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/api/auth/cas_auth.py b/api/auth/cas_auth.py index e70d028e..6746f3b7 100644 --- a/api/auth/cas_auth.py +++ b/api/auth/cas_auth.py @@ -2,6 +2,7 @@ import flask import requests +import sqlalchemy from flask import request, json from flask_api import status from xmltodict import parse @@ -299,18 +300,16 @@ def start_member_session_jwt(decoded_jwt, auto_create_member=False): current_app.logger.error(f"Failed to add new member {usr}: {e}") raise - member_session = MemberSession(member_id=member.id, session_key=decoded_jwt, creation_date=datetime.utcnow()) try: - db.session.add(member_session) - db.session.commit() + query = """INSERT INTO member_session (member_id, session_key, creation_date) + VALUES ({}, '{}', '{}') + ON CONFLICT (session_key) DO NOTHING;""".format(member.id, token_string, datetime.utcnow()) + db.session.execute(sqlalchemy.text(query)) except Exception as e: db.session.rollback() current_app.logger.error(f"Failed to create member session for {usr}: {e}") raise - current_app.logger.error(f"Found member {usr}") - current_app.logger.error(f"Member username {member.username}") - return member diff --git a/api/models/member_session.py b/api/models/member_session.py index 4cfa8ca3..b87a26d3 100644 --- a/api/models/member_session.py +++ b/api/models/member_session.py @@ -7,7 +7,7 @@ class MemberSession(Base): id = db.Column(db.Integer, primary_key=True) member_id = db.Column(db.Integer, db.ForeignKey('member.id'), nullable=False) - session_key = db.Column(db.String()) + session_key = db.Column(db.String(), unique=True) creation_date = db.Column(db.DateTime()) member = db.relationship('Member', backref=db.backref('sessions')) From 8cf36f3c3f5e0262e876dd94b88b23ce3a3badaa Mon Sep 17 00:00:00 2001 From: bsatoriu Date: Wed, 14 Jan 2026 15:34:19 -0800 Subject: [PATCH 2/4] Fix sql INSERT INTO member_session error --- api/auth/cas_auth.py | 7 ++++--- api/auth/security.py | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/auth/cas_auth.py b/api/auth/cas_auth.py index 6746f3b7..764a12a6 100644 --- a/api/auth/cas_auth.py +++ b/api/auth/cas_auth.py @@ -34,6 +34,7 @@ blueprint = flask.Blueprint('cas', __name__) PROXY_TICKET_PREFIX = "PGT-" +JWT_TOKEN_PREFIX = "jwt:" MEMBER_STATUS_ACTIVE = "active" MEMBER_STATUS_SUSPENDED = "suspended" @@ -282,13 +283,13 @@ def start_member_session(cas_response, ticket, auto_create_member=False): return member_session -def start_member_session_jwt(decoded_jwt, auto_create_member=False): +def start_member_session_jwt(decoded_jwt, token_string, auto_create_member=False): usr = decoded_jwt.get("preferred_username") member = db.session.query(Member).filter_by(username=usr).first() - if member is None and (auto_create_member): + if member is None and (auto_create_member): member = Member(first_name=decoded_jwt.get("given_name"), last_name=decoded_jwt.get("family_name"), username=usr, @@ -322,7 +323,7 @@ def get_cas_attribute_value(attributes, attribute_key): def decrypt_proxy_ticket(ticket): - if ticket.startswith(PROXY_TICKET_PREFIX): + if ticket.startswith(PROXY_TICKET_PREFIX) or ticket.startswith(JWT_TOKEN_PREFIX): return ticket else: try: diff --git a/api/auth/security.py b/api/auth/security.py index 9ff4d444..521c3219 100644 --- a/api/auth/security.py +++ b/api/auth/security.py @@ -32,8 +32,8 @@ def get_authorized_user(): decoded = verify_jwt_token(auth_header_value) if not decoded: raise AuthenticationError("Invalid or expired jwt token.") - - _member = start_member_session_jwt(decoded) + + _member = start_member_session_jwt(decoded, auth_header_value) return _member else: @@ -46,8 +46,8 @@ def get_authorized_user(): decoded = verify_jwt_token(token) if not decoded: raise AuthenticationError("Invalid or expired jwt token.") - - _member = start_member_session_jwt(decoded) + + _member = start_member_session_jwt(decoded, token) return _member else: # Malformed Authorization header From 7ca031683f1aa9341206845eb55517a3ffe21809 Mon Sep 17 00:00:00 2001 From: bsatoriu Date: Wed, 14 Jan 2026 15:42:06 -0800 Subject: [PATCH 3/4] jwt_decode retry --- api/auth/security.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/api/auth/security.py b/api/auth/security.py index 521c3219..2128074e 100644 --- a/api/auth/security.py +++ b/api/auth/security.py @@ -197,14 +197,23 @@ def verify_jwt_token(token): signing_key = jwks_client.get_signing_key_from_jwt(token) # Decode and validate the token - decoded_token = jwt.decode( - token, - signing_key.key, - algorithms=["RS256"], - audience=settings.JWT_AUDIENCE, - options={"verify_exp": False} - ) + try: + decoded_token = jwt_decode(token, signing_key, True) + except: + # Retry without expiration validation + decoded_token = jwt_decode(token, signing_key, False) + return decoded_token except Exception as e: print(f"JWT validation error: {e}") return None + +def jwt_decode(token, signing_key, verify_exp): + decoded_token = jwt.decode( + token, + signing_key.key, + algorithms=["RS256"], + audience=settings.JWT_AUDIENCE, + options={"verify_exp": verify_exp} + ) + return decoded_token From 79d2db4958b7d617e4c6fd3f06af6cd2accedb95 Mon Sep 17 00:00:00 2001 From: grallewellyn Date: Tue, 27 Jan 2026 14:50:07 -0800 Subject: [PATCH 4/4] pinned poetry version because still on Python 3.9 --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 4be88db4..c24ca8e3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,7 +2,8 @@ FROM python:3.9 as builder # Install Poetry -RUN curl -sSL https://install.python-poetry.org | python3 - +RUN curl -sSL https://install.python-poetry.org | python3 - --version 2.2.1 && \ + export PATH="/root/.local/bin:$PATH" ENV PATH="${PATH}:/root/.local/bin" \ POETRY_NO_INTERACTION=1 \