Skip to content

fix(salesforce): preserve the caller's OAuth state alongside the PKCE verifier - #100

Merged
d-klotz merged 2 commits into
nextfrom
fix/salesforce-preserve-caller-oauth-state
Sep 4, 2026
Merged

fix(salesforce): preserve the caller's OAuth state alongside the PKCE verifier#100
d-klotz merged 2 commits into
nextfrom
fix/salesforce-preserve-caller-oauth-state

Conversation

@d-klotz

@d-klotz d-klotz commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

getAuthorizationUri() overwrote the OAuth state parameter with the encrypted PKCE verifier and never read this.state, the value Frigg passes through from the adopter. state belongs to the client; adopters that use it could not route their own callback.

Clockwork is the case that surfaces it: one bounce endpoint serves every firm and resolves the firm hostname from state. Every Salesforce connect attempt ended on "Authorization state is missing or refers to an unknown firm." The same request against the HubSpot module returns the caller's state verbatim.

entityType state sent state returned
hubspot testfirma.MYNONCE123 testfirma.MYNONCE123
salesforce testfirma.MYNONCE123 iFEOtKPJEl6iXUHo.J9Ubkra…

Change

  • Append the encrypted verifier behind ~ instead of replacing the caller's state. ~ is unreserved per RFC 3986 (survives the provider redirect unescaped) and is not in the base64url alphabet, so it cannot occur inside the verifier.
  • restoreVerifierFromState() reads the trailing segment and falls back to the whole string.

Strictly additive: a caller that passes no state gets the bare verifier exactly as before, and an authorization already in flight when this ships still completes. Adopters that never read state see no behavioural change.

Tests

Five new cases in test/api.test.js: caller state preserved, first-dot split still yields the hostname, verifier round-trips out of the composed state, no-caller-state emits the bare verifier, legacy bare-verifier state still restores. The jsforce mock now sets codeVerifier when useVerifier is on, matching the real library — previously no test reached the state branch at all.

Suite: 23 passing (was 18), same 17 pre-existing failures that need a live org. The two getAuthorizationUri scope assertions fail at baseline too (the mock supplies no scope) and are unrelated.

Adopter side

Clockwork's oauth_state_nonce now stops at the delimiter — a one-line change, already written.

🤖 Generated with Claude Code

📦 Published PR as canary version: Canary Versions

✨ Test out this PR locally via:

npm install @friggframework/api-module-salesforce@1.0.3-canary.100.ffcd0f5.0
# or 
yarn add @friggframework/api-module-salesforce@1.0.3-canary.100.ffcd0f5.0

Version

Published prerelease version: @friggframework/api-module-salesforce@2.0.0-next.8

Changelog

🐛 Bug Fix

  • @friggframework/api-module-salesforce
    • fix(salesforce): preserve the caller's OAuth state alongside the PKCE verifier #100 (@d-klotz)
  • @friggframework/api-module-microsoft-teams, @friggframework/api-module-slack, @friggframework/api-module-42matters, @friggframework/api-module-asana, @friggframework/api-module-attio, @friggframework/api-module-clio, @friggframework/api-module-connectwise, @friggframework/api-module-contentful, @friggframework/api-module-contentstack, @friggframework/api-module-crossbeam, @friggframework/api-module-deel, @friggframework/api-module-fathom, @friggframework/api-module-fireflies, @friggframework/api-module-frigg-scale-test, @friggframework/api-module-frontify, @friggframework/api-module-gong, @friggframework/api-module-google-calendar, @friggframework/api-module-google-drive, @friggframework/api-module-helpscout, @friggframework/api-module-hubspot, @friggframework/api-module-ironclad, @friggframework/api-module-linear, @friggframework/api-module-otter, @friggframework/api-module-pipedrive, @friggframework/api-module-quo, @friggframework/api-module-reevo, @friggframework/api-module-salesforce, @friggframework/api-module-stripe, @friggframework/api-module-unbabel-projects, @friggframework/api-module-unbabel, @friggframework/api-module-zoho-crm, @friggframework/api-module-zoom
    • Add five conversation-source API modules (Gong, Fireflies, Fathom, Otter, Quo) #99 (@seanspeaks)

Authors: 2

… verifier

getAuthorizationUri() overwrote the `state` query parameter with the
encrypted PKCE verifier and never read `this.state`, the value Frigg passes
through from the adopter. In OAuth 2.0 `state` belongs to the client, and
adopters rely on it: Clockwork serves every firm from one bounce endpoint
and resolves the firm hostname out of `state`, so the Salesforce callback
landed on "unknown firm" for every connect attempt. HubSpot's module appends
`this.state` and works; this module was the outlier.

The verifier still has to survive the round trip, so it is now appended
behind `~` rather than replacing the caller's value. `~` is unreserved in
RFC 3986 and absent from base64url, so it cannot appear inside the verifier.
restoreVerifierFromState() reads the trailing segment and falls back to the
whole string, which keeps authorizations that were in flight before this
change working and leaves callers who pass no state exactly where they were.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@d-klotz d-klotz added the release label Sep 4, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T20:48:48.251078Z 335cf09 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Cut the comments added by the state fix down to the repo's style; the rationale stays in the PR description. Behaviour is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@d-klotz
d-klotz merged commit 357405e into next Sep 4, 2026
4 checks passed
@d-klotz
d-klotz deleted the fix/salesforce-preserve-caller-oauth-state branch September 4, 2026 21:44
@seanspeaks

Copy link
Copy Markdown
Contributor

🚀 PR was released in @friggframework/api-module-salesforce@2.0.0-next.8 🚀

@seanspeaks seanspeaks added the prerelease This change is available in a prerelease. label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prerelease This change is available in a prerelease. release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants