Laravel PHP中无法生成Salesforce refresh_token问题求助
Salesforce refresh_token生成失败排查方案

- 问题现象:调用Salesforce OAuth接口可正常生成有效期20分钟的
access_token,但始终无法获取refresh_token - 原有问题代码:
$client = new Client(); $res = $client ->request("POST", "https://test.salesforce.com/services/oauth2/token",[ 'body'=> json_encode([ 'grant_type'=>'refresh_token', 'client_id'=>Config::get('forrest.info.client_id'), 'client_secret'=>Config::get('forrest.info.client_secret'), 'Authorization'=>'Bearer 00D3H0000008olE!ARYAQOzGGJDDIaygn7GokNk4T319XlDwwBXIDYPkJfej74dfhFT3CMDvBD2pZBQK_x.v2amiRe5Y29ZNXMp9EOA6ZTYyqrTW', 'access_token'=>'00D3H0000008olE!ARYAQOzGGJDDIaygn7GokNk4T319XlDwwBXIDYPkJfej74dfhFT3CMDvBD2pZBQK_x.v2amiRe5Y29ZNXMp9EOA6ZTYyqrTW', ]) ] ); dd ($res->getStatusCode());
核心错误点
- 流程逻辑颠倒:
grant_type=refresh_token接口的作用是用已存储的refresh_token换取新的有效access_token,并非生成refresh_token的接口。refresh_token只会在用户首次完成OAuth授权流程时,和access_token一同返回,无法通过已生成的access_token反向兑换。 - 请求格式错误:Salesforce OAuth2的token接口仅支持
application/x-www-form-urlencoded格式传参,当前代码将参数转为JSON格式放在请求体中,接口无法识别参数。 - 参数配置错误:
Authorization是HTTP请求头字段,不应放在请求体中;调用refresh_token类型的接口时不需要传access_token,必须传入提前存储的refresh_token值。 - 权限配置缺失:如果Connected App侧未开启refresh_token权限、首次授权请求未申请
offline_access作用域,接口不会返回refresh_token字段。
正确实现步骤
1. 前置配置
登录Salesforce Sandbox,打开对应Connected App的配置页:
- 在OAuth策略设置中,开启refresh_token权限,可选择
Refresh Token is valid until revoked(永久有效直到手动撤销)或自定义有效期 - 在允许的OAuth作用域列表中,添加
Perform requests on your behalf at any time (refresh_token, offline_access)权限
2. 首次授权获取refresh_token
首次发起授权请求(支持授权码模式、密码模式等)时,必须在scope参数中包含refresh_token offline_access,接口返回结果中会同步返回refresh_token,需要将该值持久化存储。
以Guzzle调用密码模式为例的正确代码:
$client = new Client(); $res = $client->request("POST", "https://test.salesforce.com/services/oauth2/token",[ 'form_params' => [ // 必须用form_params传表单格式参数,不能传JSON 'grant_type' => 'password', 'client_id' => Config::get('forrest.info.client_id'), 'client_secret' => Config::get('forrest.info.client_secret'), 'username' => '你的Salesforce账号用户名', 'password' => '你的账号密码+个人安全令牌', 'scope' => 'full refresh_token offline_access' // 必须加该scope申请refresh_token权限 ] ] ); $result = json_decode($res->getBody(), true); // 从返回结果中取出两个token,持久化存储refresh_token $accessToken = $result['access_token']; $refreshToken = $result['refresh_token'];
3. 用refresh_token刷新access_token
当存储的access_token过期时,再调用grant_type=refresh_token接口换取新的access_token,不需要重复传用户名密码:
$client = new Client(); $res = $client->request("POST", "https://test.salesforce.com/services/oauth2/token",[ 'form_params' => [ 'grant_type' => 'refresh_token', 'client_id' => Config::get('forrest.info.client_id'), 'client_secret' => Config::get('forrest.info.client_secret'), 'refresh_token' => '你之前持久化存储的refresh_token值' ] ] ); $result = json_decode($res->getBody(), true); $newAccessToken = $result['access_token'];
排查清单
- 确认Connected App已开启refresh_token相关权限
- 确认首次授权请求的scope参数包含
refresh_token offline_access - 确认所有token接口请求均使用表单格式传参,未使用JSON格式请求体
- 确认没有尝试通过access_token反向生成refresh_token,refresh_token仅在首次授权时返回
内容的提问来源于stack exchange,提问作者Muhammad Adnan
相关产品推荐
相关产品推荐

