Fix OAuth signature generation in validateRestApiAccess - #2
Conversation
The validateRestApiAccess function was calling getAuthHeaders() without the required URL parameter for OAuth 1.0a signature generation. This caused authentication failures when using HTTP connections (which fall back to OAuth instead of Basic Auth). Changes: - Get baseURL from httpClient.defaults.baseURL - Construct full URL for each endpoint - Pass proper parameters (method, url, params) to getAuthHeaders() This fixes the "REST API validation failed: undefined" error when connecting via HTTP/OAuth.
Walkthrough
Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/config/auth.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/config/auth.js (2)
scripts/check-env.js (1)
httpClient(109-112)src/tests/authentication.test.js (2)
headers(46-46)headers(113-113)
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/config/auth.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/config/auth.js (2)
scripts/check-env.js (2)
httpClient(109-112)authManager(106-106)src/tests/authentication.test.js (2)
headers(46-46)headers(113-113)
🔇 Additional comments (1)
src/config/auth.js (1)
330-332: LGTM! OAuth signature generation fix is correct.The fix properly constructs the full URL and passes it to
getAuthHeadersalong with the HTTP method and query parameters. This ensures OAuth1Handler can generate valid signatures for API requests.The implementation is consistent with
OAuth1Handler.testConnection(lines 159-160) and correctly addresses the authentication failures described in the PR objectives.
Prevents TypeError when httpClient.defaults is undefined by using optional chaining (httpClient?.defaults?.baseURL).
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/config/auth.js (1)
328-336: Core fix is correct — OAuth signatures now use the full URL.Constructing
fullUrlfrombaseURL + endpoint.pathand passingmethod,fullUrl, andparamstogetAuthHeadersproperly aligns the OAuth 1.0a signature base string with the actual request, fixing the authentication failure.One minor robustness note: if
baseURLever ends with a trailing/, the concatenation would produce a double-slash (e.g.,.../gf/v2//forms), causing an OAuth signature mismatch. Currently this is safe because callers strip trailing slashes, but a one-line normalization would make this resilient to future callers:🛡️ Optional: normalize trailing slash
const baseURL = httpClient?.defaults?.baseURL; if (!baseURL) { throw new Error('httpClient baseURL is not configured'); } const results = []; for (const endpoint of endpoints) { try { // Generate proper OAuth headers with full URL for signature - const fullUrl = `${baseURL}${endpoint.path}`; + const fullUrl = `${baseURL.replace(/\/+$/, '')}${endpoint.path}`; const headers = authManager.getAuthHeaders('GET', fullUrl, { per_page: 1 });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/config/auth.js` around lines 328 - 336, The concatenation of baseURL and endpoint.path can yield a double slash if baseURL ends with '/', so normalize baseURL before building fullUrl: trim any trailing '/' (or ensure exactly one '/' separator) prior to creating fullUrl used by getAuthHeaders and the httpClient call; update the code around fullUrl, baseURL, endpoint.path, and getAuthHeaders to perform this one-line normalization to make OAuth signature generation resilient.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/config/auth.js`:
- Around line 320-325: The current change correctly guards against missing
defaults by using optional chaining to read baseURL (const baseURL =
httpClient?.defaults?.baseURL) and throws a clear error (throw new
Error('httpClient baseURL is not configured')) to avoid generating malformed
URLs; keep this defensive check in src/config/auth.js, ensure the
httpClient?.defaults?.baseURL expression is used wherever baseURL is required
(e.g., OAuth signature generation) and preserve the early throw to fail fast
when baseURL is absent.
---
Nitpick comments:
In `@src/config/auth.js`:
- Around line 328-336: The concatenation of baseURL and endpoint.path can yield
a double slash if baseURL ends with '/', so normalize baseURL before building
fullUrl: trim any trailing '/' (or ensure exactly one '/' separator) prior to
creating fullUrl used by getAuthHeaders and the httpClient call; update the code
around fullUrl, baseURL, endpoint.path, and getAuthHeaders to perform this
one-line normalization to make OAuth signature generation resilient.
Tests written first (22 regression tests), then code fixed: - #2: Replace console.log with logger in 7 files (prevents stdout corruption) - #4: Update mcp.json: remove phantom gf_submit_form, add 4 field ops, fix count - #5: Add MCP tool annotations to all 27 tools (readOnlyHint, destructiveHint, etc.) - #7: Strip _variant/_meta from validated fields before API payload - #8: Let field operation errors propagate to wrapHandler (sets isError: true) - #9: Fix name field sub-input IDs (.2=prefix not first, .3=first not prefix) - #20: Remove deprecated crypto npm dependency (Node built-in) - #21: Remove unused form-data dependency - #23: Sync mcp.json version with package.json (1.4.0) - #24: Fix feature filter key: supportsConditional → supportsConditionalLogic - #25: Add ALLOW_DELETE to gf_delete_feed description Test runner expanded from 7 to 10 suites. 234 tests, 100% pass rate.
…— TDD All four findings fixed test-first (RED watched, then GREEN). Logic for the three index.js issues was extracted into src/server-runtime.js so it's unit-testable (index.js self-runs main() and isn't importable). - #1 serial init: WordPress plane now starts before the Gravity Forms REST probe is awaited, so a slow/bad GF config no longer stalls the WP plane or the abilities load. (runPlaneInit) - #2 tool advertising: gf_* + field-op tools are listed only when the GF plane is live, so a WP-only install doesn't advertise tools that error on call. (buildToolList, gated on gravityFormsClient) - #3 dispatch: the call router no longer hard-codes name.startsWith('gv_'). It routes by ability-handler-map membership, so any GravityKit product prefix (gc_, …) dispatches, not just GravityView's gv_. (classifyAbilityCall) - #4 wp-client: WordPressClient refuses to send Basic auth over a remote plain-HTTP URL (credential exposure) unless GRAVITY_FORMS_ALLOW_HTTP_BASIC_AUTH =true, reusing isLocalUrl — matching the GF plane's guard. Tests: test/server-runtime.test.js (10), test/wp-client.test.js (4), wired into test:node. server-runtime.js added to the AGENTS.md repo map. Verified: test:node 165, test:unit 269, prepublishOnly gate green; live MCP smoke (SDK client → node src/index.js) shows 76 tools (26 gf_ + 49 gv_ + gk_reload_abilities) and gv_layouts_list dispatches.
…ped in transit TICKET items GravityKit#2 and GravityKit#3. gf_submit_form_data reported every field empty ('This field is required' for all of them) and gf_update_entry returned a clean 200 over a no-op. Root cause proven before writing code (scripts/dynamic-key-probe.mjs): the values DO reach the server over stdio and land correctly. An MCP client strips loose top-level input_N / numeric keys before the request is sent, and additionalProperties:true — which BOTH tools already declared — does not protect them. Third instance of this failure mode today, after markupVersion and set_content's object params. Adds a declared `values` object to both tools, expanded server-side: bare '1', prefixed 'input_1' and sub-input '1.3' all normalise to GF's wire form. Loose keys still work and are merged first, so direct/stdio callers are unaffected. gf_update_entry now REFUSES an update carrying neither field values nor status, naming the stripping as the likely cause. That call could previously only be a no-op returning 200 — the exact shape that cost the ticket author a debugging session. Verified 8/8 on aaru65 (scripts/values-param-verify.mjs): submit lands and the value reads back off the created entry, input_-prefixed keys normalise, update lands, the empty update is refused, and a status-only update still works. One fixture note worth keeping: the first run failed the submit cases because aaru65 rejects @example.com addresses outright. That looked exactly like the values param not working. Fixture now uses a real domain. Suite unchanged at 11/16. Scratch forms 27 and 28 trashed; entry 2357 — the blank row the ticket left on the live waitlist, which this test had filled with a visible marker — moved to trash so it stops rendering in View 4839.
Summary
validateRestApiAccessfunctionProblem
The
validateRestApiAccessfunction was callinggetAuthHeaders()without the required URL parameter for OAuth 1.0a signature generation. This caused authentication failures when using HTTP connections (which fall back to OAuth instead of Basic Auth).Steps to Reproduce
Clone and configure GravityMCP with HTTP base URL:
Add to Claude Code:
Check MCP server status:
Expected: gravitymcp shows "✓ Connected"
Actual: gravitymcp shows "✗ Failed to connect" with error "REST API validation failed: undefined"
Solution
baseURLfromhttpClient.defaults.baseURLmethod,url,params) togetAuthHeaders()Test plan
npm run check-envon HTTP localhost connectionclaude mcp listshows gravitymcp as connected after fixSummary by CodeRabbit
Bug Fixes
Tests