diff --git a/integrationos-platform-oauth/package.json b/integrationos-platform-oauth/package.json index a8ca3c3b..792dd146 100644 --- a/integrationos-platform-oauth/package.json +++ b/integrationos-platform-oauth/package.json @@ -13,10 +13,12 @@ "dependencies": { "axios": "^1.7.2", "dotenv": "^16.4.5", - "express": "^4.19.2" + "express": "^4.19.2", + "jsonwebtoken": "^9.0.2" }, "devDependencies": { "@types/express": "^4.17.21", + "@types/jsonwebtoken": "^9.0.6", "ts-node": "^10.9.2", "typescript": "^5.4.5" } diff --git a/integrationos-platform-oauth/src/connections/slack/refresh.ts b/integrationos-platform-oauth/src/connections/slack/refresh.ts index 327b78c9..e3b3d80b 100644 --- a/integrationos-platform-oauth/src/connections/slack/refresh.ts +++ b/integrationos-platform-oauth/src/connections/slack/refresh.ts @@ -1,5 +1,3 @@ -import axios from "axios"; -import qs from "qs"; import { DataObject, OAuthResponse } from "../../lib/types"; export const refresh = async ({ body }: DataObject): Promise => { diff --git a/integrationos-platform-oauth/src/connections/xero/init.ts b/integrationos-platform-oauth/src/connections/xero/init.ts new file mode 100644 index 00000000..bf78a669 --- /dev/null +++ b/integrationos-platform-oauth/src/connections/xero/init.ts @@ -0,0 +1,80 @@ +import axios from "axios"; +import jwt from "jsonwebtoken"; +import { DataObject, OAuthResponse } from "../../lib/types"; + +const generateXeroHeaders = (clientId: string, clientSecret: string) => { + const credentials = clientId + ":" + clientSecret; + const encodedCredentials = Buffer.from(credentials).toString("base64"); + + return { + authorization: "Basic " + encodedCredentials, + "Content-Type": "application/x-www-form-urlencoded", + }; +}; + +export const init = async ({ body }: DataObject): Promise => { + try { + const requestBody = { + grant_type: "authorization_code", + code: body.metadata?.code, + redirect_uri: body.metadata?.redirectUri, + }; + + const response = await axios.post( + `https://identity.xero.com/connect/token`, + requestBody, + { + headers: generateXeroHeaders(body.clientId, body.clientSecret), + } + ); + + const { + access_token: accessToken, + refresh_token: refreshToken, + expires_in: expiresIn, + token_type: tokenType, + } = response.data; + + // Get tenant id details + const decodedToken = jwt.decode(accessToken) as { + authentication_event_id: string; + }; + + const tenantId = await axios.get("https://api.xero.com/connections", { + headers: { + authorization: "Bearer " + accessToken, + "Content-Type": "application/json", + }, + }); + + if (!tenantId.data.length) { + throw new Error(`Failed to fetch tenantId from Xero API`); + } + + const extractedTenantId = tenantId.data.find( + (tenant: any) => + tenant.authEventId === decodedToken.authentication_event_id + )?.tenantId; + + if (!extractedTenantId) { + throw new Error(`Failed to extract tenantId from Xero API response`); + } + + const newMetadata = { + ...body?.metadata, + tenantId: extractedTenantId, + }; + + return { + accessToken, + refreshToken, + expiresIn, + tokenType, + meta: { + ...newMetadata, + }, + }; + } catch (error) { + throw new Error(`Error fetching access token for xero: ${error}`); + } +}; diff --git a/integrationos-platform-oauth/src/connections/xero/refresh.ts b/integrationos-platform-oauth/src/connections/xero/refresh.ts new file mode 100644 index 00000000..4d73a9b2 --- /dev/null +++ b/integrationos-platform-oauth/src/connections/xero/refresh.ts @@ -0,0 +1,56 @@ +import axios from "axios"; +import { DataObject, OAuthResponse } from "../../lib/types"; + +const generateXeroHeaders = (clientId: string, clientSecret: string) => { + const credentials = clientId + ":" + clientSecret; + const encodedCredentials = Buffer.from(credentials).toString("base64"); + + return { + authorization: "Basic " + encodedCredentials, + "Content-Type": "application/x-www-form-urlencoded", + }; +}; + +export const refresh = async ({ body }: DataObject): Promise => { + try { + const { + OAUTH_CLIENT_ID: client_id, + OAUTH_CLIENT_SECRET: client_secret, + OAUTH_REFRESH_TOKEN: refresh_token, + OAUTH_REQUEST_PAYLOAD: { redirectUri: redirect_uri }, + } = body; + + const requestBody = { + grant_type: "refresh_token", + client_id, + refresh_token, + }; + + const response = await axios.post( + "https://identity.xero.com/connect/token", + requestBody, + { + headers: generateXeroHeaders(client_id, client_secret), + } + ); + + const { + access_token: accessToken, + refresh_token: refreshToken, + expires_in: expiresIn, + token_type: tokenType, + } = response.data; + + return { + accessToken, + refreshToken, + expiresIn, + tokenType, + meta: { + ...body?.OAUTH_METADATA?.meta, + }, + }; + } catch (error) { + throw new Error(`Error fetching refresh token for xero: ${error}`); + } +};