service-titan-api
Version:
A module for authenticating and accessing the ServiceTitan API.
71 lines (66 loc) • 2.48 kB
JavaScript
// src/auth/serviceTitanAuth.mjs
import axios from "axios";
export default class ServiceTitanAuth {
/**
* @param {Object} options
* @param {string} options.clientId - Your ServiceTitan Client ID
* @param {string} options.clientSecret - Your ServiceTitan Client Secret
* @param {string} [options.environment='integration'] - Environment ('integration' or 'production')
*/
constructor({ clientId, clientSecret, environment = "integration" }) {
if (!clientId || !clientSecret) {
throw new Error("clientId and clientSecret are required");
}
this.clientId = clientId;
this.clientSecret = clientSecret;
this.environment = environment;
this.authUrl =
environment === "integration"
? "https://auth-integration.servicetitan.io/connect/token"
: "https://auth.servicetitan.io/connect/token";
this.accessToken = null;
this.expiresAt = 0;
}
/**
* Returns a valid access token. If the current token is expired or about to expire,
* it will automatically fetch a new one.
* @returns {Promise<string>} The ServiceTitan access token.
*/
async getAccessToken() {
if (this.accessToken && Date.now() < this.expiresAt) {
return this.accessToken;
}
return this.fetchNewToken();
}
/**
* Fetches a new access token from the ServiceTitan OAuth API using axios.
* @returns {Promise<string>} The new access token.
*/
async fetchNewToken() {
const body = new URLSearchParams();
body.append("grant_type", "client_credentials");
body.append("client_id", this.clientId);
body.append("client_secret", this.clientSecret);
try {
const response = await axios.post(this.authUrl, body.toString(), {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
const data = response.data;
this.accessToken = data.access_token;
// Subtract a safety margin (30 seconds) from expires_in.
this.expiresAt = Date.now() + (data.expires_in - 30) * 1000;
return this.accessToken;
} catch (error) {
const status = error.response ? error.response.status : "Unknown status";
const statusText = error.response
? error.response.statusText
: "Unknown error";
const errorData = error.response
? JSON.stringify(error.response.data)
: error.message;
throw new Error(
`Error fetching token: ${status} ${statusText}: ${errorData}`
);
}
}
}