Skip to content

Upgrade saloonphp/saloon from v2 to v4#45

Closed
tance77 wants to merge 5 commits into
mainfrom
feature/saloon-upgrade
Closed

Upgrade saloonphp/saloon from v2 to v4#45
tance77 wants to merge 5 commits into
mainfrom
feature/saloon-upgrade

Conversation

@tance77
Copy link
Copy Markdown
Member

@tance77 tance77 commented Mar 26, 2026

Summary

  • Upgrades saloonphp/saloon from ^2.0 to ^4.0, applying all breaking changes from the v2→v3 and v3→v4 migration guides
  • Replaces removed AccessTokenAuthenticator::serialize()/unserialize() (CVE security fix) with JSON-based credential storage
  • Adds allowBaseUrlOverride to requests using full URLs in resolveEndpoint() (SSRF protection now opt-in in v4)

Breaking Changes

restoreExistingOAuthConnection() now accepts AccessTokenAuthenticator instead of a serialized string

Saloon v4 removed AccessTokenAuthenticator::serialize() and ::unserialize() to fix an insecure deserialization CVE. You must now store the token properties individually and construct the authenticator yourself.

Before (v2):

// Storing credentials
$authenticator = $salesforceApi->completeOAuthLogin($oauthConfig, $code, $state);
$serialized = $authenticator->serialize();
// save $serialized to your database

// Restoring credentials
$salesforceApi->restoreExistingOAuthConnection($serialized, function ($authenticator) {
    // save $authenticator->serialize() to your database
});

After (v4):

use Saloon\Http\Auth\AccessTokenAuthenticator;

// Storing credentials
$authenticator = $salesforceApi->completeOAuthLogin($oauthConfig, $code, $state);
// save these individually to your database
$accessToken = $authenticator->getAccessToken();
$refreshToken = $authenticator->getRefreshToken();
$expiresAt = $authenticator->getExpiresAt(); // DateTimeImmutable|null

// Restoring credentials
$authenticator = new AccessTokenAuthenticator(
    accessToken: $accessToken,       // from your database
    refreshToken: $refreshToken,     // from your database
    expiresAt: $expiresAt,           // from your database (DateTimeImmutable|null)
);
$salesforceApi->restoreExistingOAuthConnection($authenticator, function ($authenticator) {
    // save the refreshed token properties to your database
    $newAccessToken = $authenticator->getAccessToken();
    $newRefreshToken = $authenticator->getRefreshToken();
    $newExpiresAt = $authenticator->getExpiresAt();
});

restoreExistingOAuthConnectionWithCodeVerification() — same change

Before (v2):

$salesforceApi->restoreExistingOAuthConnectionWithCodeVerification(
    $serialized, $oauthConfig, $codeVerifier, function ($authenticator) {
        // save $authenticator->serialize()
    }
);

After (v4):

$authenticator = new AccessTokenAuthenticator(
    accessToken: $accessToken,
    refreshToken: $refreshToken,
    expiresAt: $expiresAt,
);
$salesforceApi->restoreExistingOAuthConnectionWithCodeVerification(
    $authenticator, $oauthConfig, $codeVerifier, function ($authenticator) {
        // save $authenticator->getAccessToken(), etc.
    }
);

refreshToken() — same change

Before (v2):

$salesforceApi->refreshToken($serialized, function ($authenticator) {
    // save $authenticator->serialize()
});

After (v4):

$authenticator = new AccessTokenAuthenticator(
    accessToken: $accessToken,
    refreshToken: $refreshToken,
    expiresAt: $expiresAt,
);
$salesforceApi->refreshToken($authenticator, function ($authenticator) {
    // save refreshed token properties
});

Test plan

  • PHPStan passes clean at level 5
  • All unit tests pass (FolderNameTest, SoqlQueryTest — 45 tests)
  • Integration tests authenticate and communicate with Salesforce successfully

🤖 Generated with Claude Code

tance77 added 4 commits March 26, 2026 09:35
Rename HasBody trait to HasStringBody and replace removed
Saloon\Contracts\Response with Saloon\Http\Response.
- Add allowBaseUrlOverride to requests using full URLs in
  resolveEndpoint (SSRF protection now requires opt-in)
- Replace removed AccessTokenAuthenticator::unserialize() with
  direct AccessTokenAuthenticator parameter
- Remove stale @phpstan-ignore-line comments
Replace removed serialize/unserialize with JSON encoding of
access_token, refresh_token, and expires_at properties.
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

Upgrades this Salesforce PHP client to Saloon v4, updating OAuth token handling to avoid insecure serialization and aligning request behavior with Saloon v4’s SSRF/base URL override protections.

Changes:

  • Upgrade saloonphp/saloon dependency from ^2 to ^4 and adjust code for breaking changes.
  • Replace removed AccessTokenAuthenticator::serialize/unserialize with JSON-based token persistence and manual authenticator construction.
  • Opt-in to full-URL endpoints by enabling allowBaseUrlOverride on affected requests / OAuth config.

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
composer.json Bumps Saloon requirement to ^4.0 (but PHP requirement not yet aligned).
composer.lock Locks Saloon to v4.0.0 and updates dependency graph accordingly.
src/SalesforceApi.php Updates OAuth restore/refresh APIs to accept AccessTokenAuthenticator objects; removes old unserialize flow.
src/Connectors/SalesforceOAuthLoginConnector.php Updates Response import and enables base URL override on OAuth config.
src/Requests/OAuth2/UserInfo.php Enables base URL override for full-URL endpoint resolution.
src/Requests/OAuth/GetAccessTokenWithPKCERequest.php Enables base URL override for token endpoint resolution.
src/Requests/Auth/LoginApiUser.php Enables base URL override for full-URL token endpoint.
src/Requests/BulkApi/UploadJobData.php Switches to HasStringBody for CSV upload payloads.
tests/Pest.php Updates test helper to restore/store OAuth credentials via JSON.
.tinkerwell/SalesforcePhpTinkerwellDriver.php Updates local Tinkerwell bootstrap to restore/store OAuth credentials via JSON.
index.php Updates OAuth demo script to persist credentials as JSON.
.gitignore Adds scratch project path and normalizes ignore entries.

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

Comment thread composer.json Outdated
Comment thread tests/Pest.php
Comment thread .tinkerwell/SalesforcePhpTinkerwellDriver.php
Comment thread index.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@tance77
Copy link
Copy Markdown
Member Author

tance77 commented Mar 26, 2026

Oh someone else already did this and on the same day lol.

Closing switching to other one.

#44

@tance77 tance77 closed this Mar 26, 2026
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