Files
Joe Au-YeungGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
01ae6f1b9e fix: Salesforce tokens add token_lifetime (#27264)
* Create `getSalesforceTokenLifetime`

* When connecting salesforce, add token_lifetime

* Migrate token_lifetime and refetch if token expiry doesn't match

* Add tests for Salesforce token lifetime feature

- Add unit tests for getSalesforceTokenLifetime function
- Add integration tests for token lifecycle management in CrmService
- Fix type error in CrmService.ts by extracting refreshToken variable

Co-Authored-By: [email protected] <[email protected]>

* chore: re-trigger CI

Co-Authored-By: [email protected] <[email protected]>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-27 10:00:48 -03:00

42 lines
1.2 KiB
TypeScript

import { getSalesforceAppKeys } from "./getSalesforceAppKeys";
/**
* Calls Salesforce's token introspection endpoint to get the token lifetime.
* Returns token lifetime in seconds (calculated from exp - iat).
*
* @see https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oidc_token_introspection_endpoint.htm
*/
export async function getSalesforceTokenLifetime({
accessToken,
instanceUrl,
}: {
accessToken: string;
instanceUrl: string;
}): Promise<number> {
const { consumer_key, consumer_secret } = await getSalesforceAppKeys();
const response = await fetch(`${instanceUrl}/services/oauth2/introspect`, {
method: "POST",
headers: {
Authorization: `Basic ${Buffer.from(`${consumer_key}:${consumer_secret}`).toString("base64")}`,
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
token: accessToken,
token_type_hint: "access_token",
}),
});
if (!response.ok) {
throw new Error(`Token introspection failed: ${response.statusText}`);
}
const data = await response.json();
// Calculate lifetime from exp and iat (both in seconds)
// exp = expiration timestamp, iat = issued at timestamp
const tokenLifetime = data.exp - data.iat;
return tokenLifetime;
}