I am trying to put together an incredibly simple OAuth integration with Salesforce, in order to export some Case data for one of my clients. I have set up a Salesforce developer account and my client has given me access to a sandbox for the purposes of testing the integration. I have set up a Connected App within my developer account.
I'm using Node.js with Typescript for the integration using the salesforce-oauth2 library.
I can successfully authorized my Connected App within my client's sandbox account. It seems the OAuth handshake is working just fine. Here are some details of my application:
- IP restrictions are relaxed
- Refresh token is set to expire in 99999 months
- The app is configured with these scopes: "api refresh_token offline_access"
- My session timeout value is "None"
Once I've made the authorization, I can successfully export the data I want from the sandbox. This is to say that it seems the access_token exchange for resources is working just fine.
I have also configured the tokens to be introspectable and, if I introspect the tokens once I've created them, they also look (mostly*) fine:
{
"active": true,
"scope": "api refresh_token",
"client_id": "MY_CLIENT_ID",
"username": "MY_USERNAME",
"sub": "https://test.salesforce.com/id/XXXXXXXXXXXXX/XXXXXXXXXX",
"token_type": "refresh_token",
"iat": 1658159555,
"nbf": 1658159555
}
{
"active": true,
"scope": "api refresh_token",
"client_id": "MY_CLIENT_ID",
"username": "MY_USERNAME",
"sub": "https://test.salesforce.com/id/XXXXXXXXXXXXX/XXXXXXXXXXX",
"token_type": "access_token",
"exp": 1658200540,
"iat": 1658157340,
"nbf": 1658157340
}
- I say mostly because the minted tokens do not have all of the scopes I've requested - where is "offline_access"?
My problems come up when I try to refresh the access token. As far as I can tell from documentation there shouldn't be any issue in refreshing this token at any time. Obviously refreshing it before each call would be wasteful but that's not my problem right now.
I'm following the instructions laid out here to the letter.
I create a request like this:
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d 'token=$MY_REFRESH_TOKEN' + \
'&client_id=$MY_CLIENT_ID' + \
'&client_secret=$MY_CLIENT_SECRET' + \
'&grant_type=refresh_token' \
https://login.salesforce.com/services/oauth2/token
Which gives me the following response:
{
"error": "invalid_grant",
"error_description": "expired access/refresh token"
}
This cannot be correct because in my program I've literally just minted both access and refresh tokens - they are brand new and cannot be expired. This is also proven through token introspection.
I know there is a limit to the number of refresh tokens you can have attached to a user. I have verified that the issue persists even if I revoke all of my tokens.
I have scoured seemingly every article on the internet related to Salesforce refresh token issues. I'm confident (though obviously not certain) that there's something within Salesforce itself that I'm not configuring correctly that's leading to this not working.
Some thoughts I've had:
- is it related to needing the "full" permission?
- is it related to the fact that I have a connection into a sandbox account?
- does a sandbox account have to "unlock" refresh_token permissions even though I've requested them and seemingly have them granted?
- is there an unknown parameter I'm missing in my request?
If anyone has guidance I'd appreciate it - I find Salesforce's documentation to be extremely lacklustre.
https://login.salesforce.com/...
tohttps://test.salesforce.com/...
login = production and developer edition orgs. test = sandbox orgs.