From e23573e6254f627210aae39b0bc32c96ea47252a Mon Sep 17 00:00:00 2001 From: Salabh A N Date: Fri, 14 Nov 2025 13:40:55 +0530 Subject: [PATCH 1/3] FWF-5603 - Updated base image to `python:3.13.9-slim-bookworm` - Removed dependency `ecdsa` - Configured `python-jose` to use `cryptography` backend. --- forms-flow-data-layer/Dockerfile | 5 +++-- forms-flow-data-layer/requirements.txt | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/forms-flow-data-layer/Dockerfile b/forms-flow-data-layer/Dockerfile index 8c9e8ffa2c..ce6b4a55ae 100644 --- a/forms-flow-data-layer/Dockerfile +++ b/forms-flow-data-layer/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.2-slim-bullseye +FROM python:3.13.9-slim-bookworm LABEL project="formsflow-data-layer" @@ -26,7 +26,8 @@ COPY src/ ./src COPY main.py . # Security: Add non-root user -RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /formsflow-data-layer +RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /formsflow-data-layer \ + && pip uninstall -y ecdsa USER appuser EXPOSE 8000 diff --git a/forms-flow-data-layer/requirements.txt b/forms-flow-data-layer/requirements.txt index deb261e677..e28e0ec34e 100644 --- a/forms-flow-data-layer/requirements.txt +++ b/forms-flow-data-layer/requirements.txt @@ -8,7 +8,6 @@ charset-normalizer==3.4.1 click==8.1.8 colorama==0.4.6 dnspython==2.7.0 -ecdsa==0.19.1 email_validator==2.2.0 fastapi==0.116.1 fastapi-cli==0.0.7 @@ -35,7 +34,7 @@ Pygments==2.19.1 pymongo==4.11.3 python-dateutil==2.9.0.post0 python-dotenv==1.0.1 -python-jose==3.4.0 +python-jose[cryptography]==3.4.0 python-multipart==0.0.20 PyYAML==6.0.2 redis==5.2.1 From 07fb370630523e361c661115624347a31868d3e8 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Tue, 18 Nov 2025 16:16:39 +0530 Subject: [PATCH 2/3] FWF-5603: [Bugfix] Refactor KeycloakOIDC to separate signing key retrieval from public key fetching - python-jose-cryptography is an RSA public key object that cannot be pickled. --- .../src/utils/keycloak_oidc.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/forms-flow-data-layer/src/utils/keycloak_oidc.py b/forms-flow-data-layer/src/utils/keycloak_oidc.py index d82f566703..b7a110c62f 100644 --- a/forms-flow-data-layer/src/utils/keycloak_oidc.py +++ b/forms-flow-data-layer/src/utils/keycloak_oidc.py @@ -33,26 +33,28 @@ async def __fetch_keys(self) -> Dict[str, Any]: response.raise_for_status() jwks = response.json() logger.info("Got response form keycloak [public key]") - keys = jwks.get("keys", []) - # Filter only signing keys with RS256 - signing_keys = { - key["kid"]: jwk.construct(key) - for key in keys - if key.get("use") == "sig" - and key.get("alg") == "RS256" - and key.get("kid") - } - return signing_keys + return jwks except Exception as e: raise RuntimeError(f"Failed to fetch Keycloak public keys: {str(e)}") from e + async def __get__signing_keys(self, public_keys) -> Dict[str, Any]: + """Retrieving signing public keys from the public keys.""" + keys = public_keys.get("keys", []) + signing_public_keys = { + key["kid"]: jwk.construct(key) + for key in keys + if key.get("use") == "sig" and key.get("alg") == "RS256" and key.get("kid") + } + return signing_public_keys + async def __get_public_keys(self) -> Dict[str, Any]: """Retrieve public keys from cache or fetch if not present.""" public_keys = self.cache.get("public_keys") if public_keys is None: public_keys = await self.__fetch_keys() self.cache.set("public_keys", public_keys) - return public_keys + signing_keys= await self.__get__signing_keys(public_keys) + return signing_keys async def verify_token(self, token: str) -> Dict[str, Any]: """Verify the JWT token and return the payload if valid.""" @@ -66,6 +68,7 @@ async def verify_token(self, token: str) -> Dict[str, Any]: public_keys = await self.__fetch_keys() self.cache.set("public_keys", public_keys) kid = headers.get("kid") + public_keys= await self.__get__signing_keys(public_keys) if not kid or kid not in public_keys: raise JWTError("Public key not found for 'kid'") public_key = public_keys[kid] From 99aa906e8a1a9a4a344af554cd81b7bd107d1161 Mon Sep 17 00:00:00 2001 From: auslin-aot <99173163+auslin-aot@users.noreply.github.com> Date: Tue, 18 Nov 2025 16:26:00 +0530 Subject: [PATCH 3/3] FWF-5603: [Bugfix] Lint fix --- .github/workflows/forms-flow-data-layer-ci.yml | 2 +- forms-flow-data-layer/src/utils/keycloak_oidc.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/forms-flow-data-layer-ci.yml b/.github/workflows/forms-flow-data-layer-ci.yml index 7731d319eb..62e88fc89e 100644 --- a/.github/workflows/forms-flow-data-layer-ci.yml +++ b/.github/workflows/forms-flow-data-layer-ci.yml @@ -64,7 +64,7 @@ jobs: strategy: matrix: - python-version: [3.12.11] + python-version: [3.12.12] env: JWT_OIDC_JWKS_URI: "http://localhost:8081/auth/realms/forms-flow-ai/protocol/openid-connect/certs" diff --git a/forms-flow-data-layer/src/utils/keycloak_oidc.py b/forms-flow-data-layer/src/utils/keycloak_oidc.py index b7a110c62f..33aab6c04f 100644 --- a/forms-flow-data-layer/src/utils/keycloak_oidc.py +++ b/forms-flow-data-layer/src/utils/keycloak_oidc.py @@ -53,7 +53,7 @@ async def __get_public_keys(self) -> Dict[str, Any]: if public_keys is None: public_keys = await self.__fetch_keys() self.cache.set("public_keys", public_keys) - signing_keys= await self.__get__signing_keys(public_keys) + signing_keys = await self.__get__signing_keys(public_keys) return signing_keys async def verify_token(self, token: str) -> Dict[str, Any]: @@ -68,7 +68,7 @@ async def verify_token(self, token: str) -> Dict[str, Any]: public_keys = await self.__fetch_keys() self.cache.set("public_keys", public_keys) kid = headers.get("kid") - public_keys= await self.__get__signing_keys(public_keys) + public_keys = await self.__get__signing_keys(public_keys) if not kid or kid not in public_keys: raise JWTError("Public key not found for 'kid'") public_key = public_keys[kid]