refreshFn when using client_credentials
Hi, I want to double check that my implementation does work forever, and I'm not sure whether my implementation of client_credentials will keep itself up and running forever using refreshFn:
const salesforce = new jsforce.Connection({
oauth2: {
clientSecret: SALESFORCE_CLIENT_SECRET!,
loginUrl: SALESFORCE_INSTANCE_URL!,
clientId: SALESFORCE_CLIENT_ID!,
},
instanceUrl: SALESFORCE_INSTANCE_URL!,
version: SALESFORCE_VERSION,
refreshFn: async (connection, callback) => {
try {
await connection.authorize({ grant_type: 'client_credentials' });
if (!connection.accessToken) {
throw new Error('Salesforce client_credentials authorize error');
}
callback(null, connection.accessToken);
} catch (error: unknown) {
callback(error as Error);
}
},
});
await salesforce.authorize({ grant_type: 'client_credentials' });
export { salesforce };
My questions are:
- will this work?
- do I need this refreshFn
- in the refreshFn, I call connection.authorize on the already exisitng connection, and than return connection.accessToken, this feels weird as you are returning the 'old connection's' access token that should be refreshed using the
authorizecall - i'm seeing the exact same access token when comparing the token before authorize and after authorize?
it would be amazing if you could clarify this for me, I see it working in my tests, but I am very afraid that after deployment and after x time, it all of a suddens tops working
do I need this refreshFn
yes, the CC returns an access token that expires (depending on your CA/ECA settings) so you will always want a refreshFn for long-running processes.
in the refreshFn, I call connection.authorize on the already exisitng connection, and than return connection.accessToken, this feels weird as you are returning the 'old connection's' access token that should be refreshed using the authorize call
conn.authorize does set the new access token in the connection instance here:
https://github.com/jsforce/jsforce/blob/f0d93730693b0c22fc1018c3159da65d2b078bcc/src/connection.ts#L489-L494
so in your example, the callback should send the new access token.
i'm seeing the exact same access token when comparing the token before authorize and after authorize?
can you share the exact steps you are using to verify this?
@cristiand391 thank you very much for your response (in short your response seems to be: this should work), I'm glad that you took the time to respond to me! My exact steps are:
- create an external client app with a running as user
- set
Session Timeout in Minutesto 1
then, in the code I shared above, I did the following:
const lastToken = connection.accessToken;
await connection.authorize({ grant_type: 'client_credentials' });
console.log(lastToken === connection.accessToken);
which yielded true. My thoughts were that timeout != expiry, but I wasn't able to test this in a situation where the accessToken actually changed
you can set the JSFORCE_LOG_LEVEL=DEBUG env var to see jsforce logs, here I did it with the Salesforce CLI (uses jsforce under the hood) running sf org display and you can see the session-refresh-delegate logs when it calls its the refreshFn func:
which yielded true. My thoughts were that timeout != expiry, but I wasn't able to test this in a situation where the accessToken actually changed
try this:
- grab the fresh access token and revoke it, here's how to do it with curl:
curl "https://cristianalexisdominguez-devhub.my.salesforce.com/services/oauth2/revoke" -d "@token.txt" -H 'Content-Type: application/x-www-form-urlencoded' -i
where token.txt is a file with this content:
token=<access-token>&token_type_hint=access_token
you should get HTTP 200 on a successful revokation:
- run a soql query via
conn.query(or any other conn method that would trigger the call to refreshFn), the first HTTP call would try to use the expired token from step 1 -> call refreshFn -> retry the request with the new token from the callback, return the soql results. Then compare the old and new access token.
try doing the token revoke steps from curl in your JS script and set the env var to see the full process, refreshFn is only called when an API request done by jsforce returns a 401 (invalid session ID).