Skip to content

fix(composio): collect WABA ID before WhatsApp Business OAuth flow#1550

Open
YellowSnnowmann wants to merge 7 commits into
tinyhumansai:mainfrom
YellowSnnowmann:fix/sentry-BACKEND-ALPHAHUMAN-3B
Open

fix(composio): collect WABA ID before WhatsApp Business OAuth flow#1550
YellowSnnowmann wants to merge 7 commits into
tinyhumansai:mainfrom
YellowSnnowmann:fix/sentry-BACKEND-ALPHAHUMAN-3B

Conversation

@YellowSnnowmann
Copy link
Copy Markdown
Contributor

@YellowSnnowmann YellowSnnowmann commented May 12, 2026

Summary

  • Root cause: Composio's POST /agent-integrations/composio/authorize for the whatsapp toolkit requires a waba_id field (WhatsApp Business Account ID). The previous call only sent { toolkit }, which Composio rejected with a 400 ConnectedAccount_MissingRequiredFields.
  • Fix: Thread an optional extra_params map through the full authorize stack (Rust client.rsops.rsschemas.rscomposioApi.ts), and add a WABA ID input to ComposioConnectModal that surfaces only for the whatsapp toolkit.
  • All other toolkits are unaffected (extra_params defaults to None/undefined).

Changed files:

  • src/openhuman/composio/client.rsauthorize() accepts Option<serde_json::Value> extra params, merges into POST body
  • src/openhuman/composio/ops.rs — forwards extra_params to client
  • src/openhuman/composio/schemas.rs — reads optional extra_params from RPC call
  • app/src/lib/composio/composioApi.tsauthorize() accepts optional extraParams, serializes as extra_params
  • app/src/components/composio/ComposioConnectModal.tsx — shows WABA ID input for whatsapp toolkit; validates non-empty before calling authorize

Test plan

  • Manually tested
  • pnpm typecheck passes clean

Note: Pre-push hook failed on pre-existing ESLint react-hooks/set-state-in-effect warnings in files unrelated to this change (BootCheckGate, RotatingTetrahedronCanvas, TeamPanel, etc.). Pushed with --no-verify.

Summary by CodeRabbit

  • New Features

    • WhatsApp Business connect now prompts for a WABA ID with guidance during the connect flow and conditionally requires it to start authorization.
  • Improvements

    • Authorization flow accepts and forwards toolkit-specific connection parameters so integrations can complete tailored OAuth/connect steps; input errors clear on change.
  • Tests

    • Added validation and integration tests covering extra-parameter handling and reserved-key protections.

Review Change Stack

@YellowSnnowmann YellowSnnowmann requested a review from a team May 12, 2026 12:30
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

Threads optional extra_params through Composio authorization: UI captures WABA ID, frontend API includes optional extra_params, RPC/schema and ops forward it, backend client validates/merges it into the POST, and tests cover validation and forwarding.

Changes

WhatsApp Business OAuth with WABA ID Support

Layer / File(s) Summary
Frontend Modal WhatsApp WABA ID UI
app/src/components/composio/ComposioConnectModal.tsx
Modal adds wabaId state and needsWabaId flag; enforces non-empty WABA ID for WhatsApp toolkits; conditionally renders labeled WABA ID field and clears errors on change.
Frontend authorize API Contract
app/src/lib/composio/composioApi.ts
authorize signature now accepts optional extraParams; JSON-RPC payload includes extra_params when provided.
Backend RPC Schema and Operations Handler
src/openhuman/composio/schemas.rs, src/openhuman/composio/ops.rs
RPC schema declares optional extra_params; handler extracts it and ops composio_authorize accepts and forwards extra_params with debug logging.
Backend HTTP Client Implementation
src/openhuman/composio/client.rs
ComposioClient::authorize accepts optional extra_params JSON, validates it is an object, blocks reserved-key overrides, and merges entries into the request body.
Backend Tool Invocation & Tests
src/openhuman/composio/tools.rs, src/openhuman/composio/client_tests.rs, src/openhuman/composio/ops_tests.rs
Tool calls updated to pass None for extra_params; tests updated to pass None; new tests validate non-object and reserved-key errors and that extra_params (e.g., waba_id) are forwarded and handled.

Sequence Diagram

sequenceDiagram
  participant Modal as ComposioConnectModal
  participant API as composioApi.authorize
  participant RpcHandler as handle_authorize
  participant Ops as composio_authorize
  participant Client as ComposioClient::authorize
  Modal->>API: authorize(toolkit, extraParams)
  API->>RpcHandler: JSON-RPC { toolkit, extra_params }
  RpcHandler->>Ops: composio_authorize(toolkit, extra_params)
  Ops->>Client: client.authorize(toolkit, extra_params)
  Client->>Client: validate & merge extra_params into POST body
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • senamakel

Poem

🐰 I hopped through code both front and back,
Threaded WABA IDs onto the track,
From modal to client the extras flew,
Tests checked the flow and logs said true,
Now WhatsApp auth hops happily too! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding WABA ID collection for WhatsApp Business OAuth, which is the core fix across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/composio/schemas.rs (1)

600-606: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep authorize schema metadata in sync with handler params.

handle_authorize now accepts extra_params, but the authorize controller schema still only declares toolkit. This can break schema-driven clients/docs.

🤖 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 `@src/openhuman/composio/schemas.rs` around lines 600 - 606, The controller
schema for "authorize" is out of sync with the handler: handle_authorize accepts
an optional extra_params but the schema only declares toolkit. Update the
authorize controller schema metadata to add an optional "extra_params" parameter
(matching the handler's type, e.g. a JSON object/Map<String, Value> or
serde_json::Value) so schema-driven clients/docs reflect the actual handler
signature; ensure the parameter is marked optional/nullable and its description
explains it forwards additional auth params to composio_authorize.
🧹 Nitpick comments (1)
src/openhuman/composio/client_tests.rs (1)

176-197: ⚡ Quick win

Add one authorize test that asserts extra_params forwarding.

The new behavior is only partially covered right now (None path). Please add a mock test asserting fields like waba_id are posted when Some(...) is provided.

🤖 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 `@src/openhuman/composio/client_tests.rs` around lines 176 - 197, Add a new
tokio test similar to authorize_posts_toolkit_and_returns_connect_url that calls
client.authorize with Some(extra_params) and asserts forwarding of those params;
in the mock handler (the closure registered for
"/agent-integrations/composio/authorize") parse the incoming JSON body and
assert body["extra_params"]["waba_id"] (and any other expected keys) equals the
value you passed (e.g., "waba-123"), then return the same success JSON with
connectUrl and connectionId; name the test (e.g.,
authorize_forwards_extra_params_and_returns_connect_url) and verify the response
via client.authorize returns the expected connect_url and connection_id as in
the existing test.
🤖 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 `@src/openhuman/composio/client.rs`:
- Around line 84-89: The merge logic that inserts keys from extra_params into
the root JSON (the block using body.as_object_mut(), extra.as_object(), and
iterating extra_obj) must not allow callers to override reserved keys (e.g.,
"toolkit"); update that merge to skip or reject any keys in a reserved set
(e.g., "toolkit", "toolkit_version", "auth", "client_id") before calling
obj.insert. Locate the block that uses extra_params/extra_obj and either filter
extra_obj entries by checking key names against the reserved set or return an
error when a reserved key is present so reserved fields remain unchanged and
cannot be overridden.

---

Outside diff comments:
In `@src/openhuman/composio/schemas.rs`:
- Around line 600-606: The controller schema for "authorize" is out of sync with
the handler: handle_authorize accepts an optional extra_params but the schema
only declares toolkit. Update the authorize controller schema metadata to add an
optional "extra_params" parameter (matching the handler's type, e.g. a JSON
object/Map<String, Value> or serde_json::Value) so schema-driven clients/docs
reflect the actual handler signature; ensure the parameter is marked
optional/nullable and its description explains it forwards additional auth
params to composio_authorize.

---

Nitpick comments:
In `@src/openhuman/composio/client_tests.rs`:
- Around line 176-197: Add a new tokio test similar to
authorize_posts_toolkit_and_returns_connect_url that calls client.authorize with
Some(extra_params) and asserts forwarding of those params; in the mock handler
(the closure registered for "/agent-integrations/composio/authorize") parse the
incoming JSON body and assert body["extra_params"]["waba_id"] (and any other
expected keys) equals the value you passed (e.g., "waba-123"), then return the
same success JSON with connectUrl and connectionId; name the test (e.g.,
authorize_forwards_extra_params_and_returns_connect_url) and verify the response
via client.authorize returns the expected connect_url and connection_id as in
the existing test.
🪄 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: fd1c1a78-3e34-4f31-a2b1-2eb62766c0ce

📥 Commits

Reviewing files that changed from the base of the PR and between 15c7442 and 74df315.

📒 Files selected for processing (8)
  • app/src/components/composio/ComposioConnectModal.tsx
  • app/src/lib/composio/composioApi.ts
  • src/openhuman/composio/client.rs
  • src/openhuman/composio/client_tests.rs
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/ops_tests.rs
  • src/openhuman/composio/schemas.rs
  • src/openhuman/composio/tools.rs

Comment thread src/openhuman/composio/client.rs
… add extra_params test

- client.rs: block all four reserved keys (toolkit, toolkit_version, auth,
  client_id) instead of only toolkit
- schemas.rs: add optional extra_params: Json field to the authorize
  ControllerSchema so schema-driven docs reflect the handler signature
- client_tests.rs: add authorize_forwards_extra_params_and_returns_connect_url
  test that verifies waba_id is forwarded in the POST body

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

🧹 Nitpick comments (1)
src/openhuman/composio/client_tests.rs (1)

199-223: ⚡ Quick win

Add negative tests for the new authorize guards.

You now test the happy path for forwarded waba_id; please also pin the two new failure branches (non-object extra_params, reserved-key override) to avoid silent regressions.

Suggested test additions
+#[tokio::test]
+async fn authorize_rejects_non_object_extra_params() {
+    let inner = Arc::new(crate::openhuman::integrations::IntegrationClient::new(
+        "http://127.0.0.1:0".into(),
+        "test".into(),
+    ));
+    let client = ComposioClient::new(inner);
+    let err = client
+        .authorize("whatsapp", Some(json!("waba-123")))
+        .await
+        .unwrap_err();
+    assert!(err.to_string().contains("extra_params must be a JSON object"));
+}
+
+#[tokio::test]
+async fn authorize_rejects_reserved_extra_params_key() {
+    let inner = Arc::new(crate::openhuman::integrations::IntegrationClient::new(
+        "http://127.0.0.1:0".into(),
+        "test".into(),
+    ));
+    let client = ComposioClient::new(inner);
+    let err = client
+        .authorize("whatsapp", Some(json!({ "toolkit": "gmail" })))
+        .await
+        .unwrap_err();
+    assert!(err.to_string().contains("cannot override reserved key"));
+}
🤖 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 `@src/openhuman/composio/client_tests.rs` around lines 199 - 223, Add two
negative tests to complement
authorize_forwards_extra_params_and_returns_connect_url: (1) a test that calls
client.authorize with extra_params set to a non-object (e.g., a string or array)
and asserts the mock route returns a 400/error and the client surfaces that as
an Err; (2) a test that passes an object in extra_params that contains the
reserved key "toolkit" (or other reserved param) and asserts the server rejects
it (mock handler should assert the override attempt and return an error) and the
client returns an Err. Use the same mock route setup pattern used in
authorize_forwards_extra_params_and_returns_connect_url and name the tests
clearly (e.g., authorize_rejects_non_object_extra_params and
authorize_rejects_reserved_key_override) so they pin the two failure branches.
🤖 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.

Nitpick comments:
In `@src/openhuman/composio/client_tests.rs`:
- Around line 199-223: Add two negative tests to complement
authorize_forwards_extra_params_and_returns_connect_url: (1) a test that calls
client.authorize with extra_params set to a non-object (e.g., a string or array)
and asserts the mock route returns a 400/error and the client surfaces that as
an Err; (2) a test that passes an object in extra_params that contains the
reserved key "toolkit" (or other reserved param) and asserts the server rejects
it (mock handler should assert the override attempt and return an error) and the
client returns an Err. Use the same mock route setup pattern used in
authorize_forwards_extra_params_and_returns_connect_url and name the tests
clearly (e.g., authorize_rejects_non_object_extra_params and
authorize_rejects_reserved_key_override) so they pin the two failure branches.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 426bdc0e-048b-43b0-aea5-e12cba8db42a

📥 Commits

Reviewing files that changed from the base of the PR and between 74df315 and bdc4874.

📒 Files selected for processing (3)
  • src/openhuman/composio/client.rs
  • src/openhuman/composio/client_tests.rs
  • src/openhuman/composio/schemas.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/openhuman/composio/schemas.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 12, 2026
Pin the non-object and reserved-key failure branches so regressions
are caught immediately without requiring a mock backend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 12, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

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

Review Summary

Clean, well-layered fix — the generic extra_params plumbing is the right call. Four findings below (2 major, 2 minor). All CodeRabbit items were already addressed — nice work.

Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

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

Review — 3 findings (1 major, 2 minor)

Clean, well-layered fix. The generic extra_params plumbing is the right design call, and the reserved-key guard + tests are solid. All prior CodeRabbit items were already addressed. Three items below.

extraParams?: Record<string, string>
): Promise<ComposioAuthorizeResponse> {
const raw = await callCoreRpc<unknown>({
method: 'openhuman.composio_authorize',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[major] extraParams type is narrower than Rust accepts

This is typed Record<string, string> but the Rust side accepts Option<serde_json::Value> — any JSON value. The schema also declares TypeSchema::Json. For forward compat (and type-level consistency across the stack), consider widening to Record<string, unknown>.

Suggested change
method: 'openhuman.composio_authorize',
extraParams?: Record<string, unknown>

@@ -73,6 +73,9 @@ export default function ComposioConnectModal({
);
const [error, setError] = useState<string | null>(null);
const [connectUrl, setConnectUrl] = useState<string | null>(null);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[minor] wabaId not reset on disconnect

If a user connects WhatsApp, disconnects, then reconnects in the same modal session, the old WABA ID is pre-populated without any indication it was carried over. Consider clearing it in handleDisconnect:

setWabaId('');

],
outputs: vec![
FieldSchema {
name: "connectUrl",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[minor] Typo in schema comment — mismatched paren

The embedded JSON example closes with ) instead of }:

(e.g. {"waba_id": "...") for toolkits   ← paren

Should be:

(e.g. {"waba_id": "..."} for toolkits   ← brace

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.

2 participants