Skip to content

Fix validation logic for gchat trigger#26

Merged
MaryamZi merged 4 commits into
wso2:add-chat-triggersfrom
RadCod3:add-chat-triggers
May 21, 2026
Merged

Fix validation logic for gchat trigger#26
MaryamZi merged 4 commits into
wso2:add-chat-triggersfrom
RadCod3:add-chat-triggers

Conversation

@RadCod3
Copy link
Copy Markdown
Contributor

@RadCod3 RadCod3 commented May 19, 2026

Purpose

Replaces the broken payload-token check in the Google Chat provider with proper JWT verification of the auth header against Google's JWKS, mirroring the Ballerina implementation.

What's new

  • Two verification modes (selectable via subscription.provider_config):
    • endpoint_url → OIDC ID token verified via Google's OIDC JWKS; checks aud == endpoint_url, email == chat@system.gserviceaccount.com, email_verified == true.
    • project_number → self-signed JWT from the Chat service account, verified via the service-account JWKS; checks aud == project_number, iss == chat@system.gserviceaccount.com.
  • Verification now runs before JSON parsing and reads the Authorization header.
  • Old provider_config.verification_token field removed.

@RadCod3 RadCod3 requested a review from MaryamZi as a code owner May 19, 2026 20:15
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc84b322-640b-4a13-b6a5-70cf3d52c147

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR replaces the Google Chat integration's legacy HMAC-based verification token flow with bearer token authentication using Google-signed JWTs. The implementation adds PyJWT dependency, redesigns GChatConfig to accept either a project number or HTTP endpoint URL audience, extracts bearer tokens from request headers, and verifies them via JWKS-backed JWT validation. Session ID generation now derives from space and user information instead of thread names. Request verification enforces bearer token presence and returns HTTP 401 on validation failure. Test fixtures and assertions migrate to the new configuration schema, and test coverage expands to validate bearer token parsing, audience configuration coercion, and both OIDC and service-account JWT verification paths.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides Purpose, approach details, and implementation information, but omits several required template sections including Goals, User Stories, Release Notes, Documentation, Training, Certification, Marketing, Test coverage details, Security checks, Samples, Related PRs, and Test environment. Complete the pull request description by adding the missing template sections, particularly Goals, Release Notes, comprehensive test coverage details (Unit/Integration tests with code coverage metrics), and Security checks confirmation.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix validation logic for gchat trigger' is directly related to the main change: replacing broken payload-token verification with proper JWT verification in the Google Chat provider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@RadCod3 RadCod3 force-pushed the add-chat-triggers branch from e9059e5 to 4b78af2 Compare May 20, 2026 09:08
@RadCod3 RadCod3 force-pushed the add-chat-triggers branch 2 times, most recently from 7ff2121 to ead9595 Compare May 20, 2026 18:40
RadCod3 added 2 commits May 21, 2026 00:17
Previously we used thread_name to generate the session_id. However, each
gchat message has its own thread thereby giving all messages a new
session with the agent. We can consider supporting replying within
thread later. Until then lets use space_name:user_name as the default
session_id

temp

Fix tests after prev fix
@MaryamZi
Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Google Chat platform-chat integration to replace the previous payload token comparison with JWT-based verification using the Authorization: Bearer ... header and Google JWKS, aligning behavior with the described Ballerina approach.

Changes:

  • Replace platform_config.verification_token with platform_config.project_number or platform_config.endpoint_url and validate mutual exclusivity.
  • Verify Google Chat requests via JWT + JWKS before JSON parsing (raw-request verification using the Authorization header).
  • Update tests and fixture AFM examples to reflect the new configuration and verification behavior.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
python-interpreter/packages/afm-core/src/afm/interfaces/platform_chat/gchat.py Implements bearer-token extraction and JWT verification via Google JWKS; updates GChat config fields and runtime verification flow.
python-interpreter/packages/afm-core/src/afm/cli.py Updates inline type-suppression comments near router creation calls.
python-interpreter/packages/afm-core/pyproject.toml Adds pyjwt[crypto] dependency required for JWT/JWKS verification.
python-interpreter/packages/afm-core/tests/test_platform_chat.py Updates tests for new GChat config fields and Authorization-header verification; adjusts session id expectations.
python-interpreter/packages/afm-core/tests/test_parser.py Updates parser tests to expect new GChat platform_config fields.
python-interpreter/packages/afm-core/tests/fixtures/sample_gchat_platform_chat_agent.afm.md Updates sample fixture to use project_number.
python-interpreter/packages/afm-core/tests/fixtures/sample_gchat_platform_chat_sync_agent.afm.md Updates sample fixture to use endpoint_url.
Comments suppressed due to low confidence (1)

python-interpreter/packages/afm-core/src/afm/interfaces/platform_chat/gchat.py:214

  • get_gchat_session_id no longer considers message.thread.name when building the session id (it now only uses space + user). This is a behavior change unrelated to the PR’s stated purpose (token validation) and may break session continuity for threaded conversations. If intentional, it should be called out in the PR description; otherwise consider restoring the thread-based session id logic.
def get_gchat_session_id(payload: object) -> str:
    if not isinstance(payload, dict):
        return "default"

    space = payload.get("space")
    space_name = (
        _non_empty_string(space.get("name")) if isinstance(space, dict) else None
    )
    if space_name is None:
        return "gchat:unknown-space:default"

    user = payload.get("user")
    if isinstance(user, dict):
        user_name = _non_empty_string(user.get("name"))
        if user_name:
            return f"gchat:{space_name}:{user_name}"

    return f"gchat:{space_name}:default"

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 190 to 195
if webchat_interface is not None:
webchat_router = create_webchat_router(
agent,
webchat_interface.signature,
webchat_path, # type: ignore[arg-type]
webchat_path, # ty:ignore[invalid-argument-type]
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2ccb273

Comment on lines +114 to +116
if not isinstance(auth_header, str) or not auth_header.startswith("Bearer "):
return None
token = auth_header[len("Bearer ") :].strip()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2ccb273

Comment on lines +131 to +137
try:
if isinstance(config, HttpEndpointUrlConfig):
return _verify_id_token(token, config.endpoint_url)
return _verify_project_number_jwt(token, config.project_number)
except Exception:
logger.exception("GChat bearer token verification raised an unexpected error")
return False
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2ccb273

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
python-interpreter/packages/afm-core/tests/test_platform_chat.py (1)

775-845: ⚡ Quick win

Add endpoint-url verification-path endpoint tests for parity.

Current endpoint auth tests exercise the project_number branch only. Add at least one accept/reject test for the endpoint_url branch so both verification modes are covered at request-handler level.

Proposed test addition
+    `@pytest.mark.asyncio`
+    async def test_gchat_verification_accepts_valid_bearer_with_endpoint_url_config(
+        self,
+        monkeypatch: pytest.MonkeyPatch,
+    ) -> None:
+        monkeypatch.setattr(
+            "afm.interfaces.platform_chat.gchat._verify_oidc_id_token",
+            lambda *_args, **_kwargs: True,
+        )
+        agent = MagicMock(spec=AgentRunner)
+        agent.afm = MagicMock()
+        agent.afm.metadata = MagicMock()
+        agent.afm.metadata.version = "1.0.0"
+        agent.afm.metadata.interfaces = [
+            PlatformChatInterface(
+                type="platformchat",
+                platform="gchat",
+                mode=PlatformChatMode.NOTIFICATION,
+                prompt="GChat event: ${http:payload.message.text}",
+                signature=Signature(input=JSONSchema(type="object")),
+                platform_config={"endpoint_url": "https://example.com/gchat"},
+                exposure=Exposure(http=HTTPExposure(path="/gchat")),
+            )
+        ]
+        agent.arun = MagicMock()
+        app = create_platform_chat_app(agent, verify_signatures=True)
+
+        transport = ASGITransport(app=app)
+        async with AsyncClient(transport=transport, base_url="http://test") as client:
+            response = await client.post(
+                "/gchat",
+                headers={"Authorization": "Bearer pretend-jwt"},
+                json={"type": "MESSAGE", "message": {"text": "verified", "sender": {"type": "HUMAN"}}},
+            )
+
+        assert response.status_code == 202
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python-interpreter/packages/afm-core/tests/test_platform_chat.py` around
lines 775 - 845, Add tests that cover the endpoint_url verification branch by
duplicating the existing bearer tests but patching the endpoint_url verifier;
specifically, add a test like test_gchat_endpoint_url_rejects_bad_bearer that
monkeypatches "afm.interfaces.platform_chat.gchat._verify_endpoint_url_jwt" to
return False, creates the app via create_platform_chat_app(...,
verify_signatures=True), posts to "/gchat" with Authorization: "Bearer bad-jwt"
and asserts 401 + "Invalid GChat bearer token", and another test like
test_gchat_endpoint_url_accepts_valid_bearer that monkeypatches the same
function to return True, posts a valid MESSAGE payload and asserts 202 and that
ran.wait() completes; use the same mock_gchat_notification_agent fixture and
AsyncClient/ASGITransport patterns as the existing tests to ensure parity.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python-interpreter/packages/afm-core/pyproject.toml`:
- Line 25: The pyproject specifies "pyjwt[crypto]>=2.8" but gchat.py
(python-interpreter/packages/afm-core/src/afm/interfaces/platform_chat/gchat.py)
calls jwt.decode(..., issuer=list(_GOOGLE_OIDC_ISSUERS), ...), which requires
PyJWT >= 2.10.1; update the dependency in pyproject.toml to
"pyjwt[crypto]>=2.10.1" so list-based issuer validation is supported.

In `@python-interpreter/packages/afm-core/src/afm/cli.py`:
- Line 194: Some inline type-check suppressions use the wrong syntax ("#
ty:ignore[...]") so the type-checker doesn't recognize them; update each
occurrence in afm.cli where you see "# ty:ignore[invalid-argument-type]" (for
example next to the webchat_path argument and the other two occurrences in the
same file) to "# ty: ignore[invalid-argument-type]" so the suppression is valid;
search for the exact string "ty:ignore[invalid-argument-type]" and replace with
"ty: ignore[invalid-argument-type]" inside the relevant function
signatures/argument lists (e.g., the webchat_path parameter) to fix the inline
suppression.

---

Nitpick comments:
In `@python-interpreter/packages/afm-core/tests/test_platform_chat.py`:
- Around line 775-845: Add tests that cover the endpoint_url verification branch
by duplicating the existing bearer tests but patching the endpoint_url verifier;
specifically, add a test like test_gchat_endpoint_url_rejects_bad_bearer that
monkeypatches "afm.interfaces.platform_chat.gchat._verify_endpoint_url_jwt" to
return False, creates the app via create_platform_chat_app(...,
verify_signatures=True), posts to "/gchat" with Authorization: "Bearer bad-jwt"
and asserts 401 + "Invalid GChat bearer token", and another test like
test_gchat_endpoint_url_accepts_valid_bearer that monkeypatches the same
function to return True, posts a valid MESSAGE payload and asserts 202 and that
ran.wait() completes; use the same mock_gchat_notification_agent fixture and
AsyncClient/ASGITransport patterns as the existing tests to ensure parity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 00fb296f-085b-4edf-825b-c38bc3bee602

📥 Commits

Reviewing files that changed from the base of the PR and between dcb7af5 and 9da422e.

⛔ Files ignored due to path filters (1)
  • python-interpreter/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • python-interpreter/packages/afm-core/pyproject.toml
  • python-interpreter/packages/afm-core/src/afm/cli.py
  • python-interpreter/packages/afm-core/src/afm/interfaces/platform_chat/gchat.py
  • python-interpreter/packages/afm-core/tests/fixtures/sample_gchat_platform_chat_agent.afm.md
  • python-interpreter/packages/afm-core/tests/fixtures/sample_gchat_platform_chat_sync_agent.afm.md
  • python-interpreter/packages/afm-core/tests/test_parser.py
  • python-interpreter/packages/afm-core/tests/test_platform_chat.py

Comment thread python-interpreter/packages/afm-core/pyproject.toml Outdated
Comment thread python-interpreter/packages/afm-core/src/afm/cli.py Outdated
@MaryamZi MaryamZi requested a review from Copilot May 21, 2026 06:41
@MaryamZi MaryamZi merged commit e73aefd into wso2:add-chat-triggers May 21, 2026
2 checks passed
@RadCod3 RadCod3 review requested due to automatic review settings May 21, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants