Skip to content

Commit

Permalink
feat: add platform oauth api (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulkr authored Jun 5, 2024
1 parent 85f4f5a commit dbc4b9a
Show file tree
Hide file tree
Showing 14 changed files with 491 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/build-platform-oauth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
on:
push:
branches:
- main
tags:
- "[0-9]+.[0-9]+.[0-9]+"
paths:
- .github/workflows/build-platform-oauth.yaml
- "integrationos-platform-oauth/**"

env:
docker_image_tag: ${{ github.ref == 'refs/heads/main' && github.sha || github.ref_name }}

jobs:
build:
runs-on: ubuntu-latest

permissions:
contents: read
id-token: write

steps:
- uses: actions/checkout@v3
- uses: integration-os/google-artifact-registry-action@v2
with:
image: "us-docker.pkg.dev/integrationos/docker-oss/platform-oauth:${{ env.docker_image_tag }}"
service_account: [email protected]
workload_identity_provider: projects/356173785332/locations/global/workloadIdentityPools/github-actions/providers/github-actions
16 changes: 16 additions & 0 deletions integrationos-platform-oauth/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ARG DOCKER_IMAGE="node:20-slim"

FROM ${DOCKER_IMAGE} AS builder
COPY . /app/platform-oauth
WORKDIR /app/platform-oauth
RUN npm install && npm run build

FROM ${DOCKER_IMAGE}
RUN apt-get update && apt-get install -y tini
COPY --from=builder /app/platform-oauth/dist /app/platform-oauth/dist
COPY --from=builder /app/platform-oauth/node_modules /app/platform-oauth/node_modules
COPY --from=builder /app/platform-oauth/package.json /app/platform-oauth/package.json
COPY --from=builder /app/platform-oauth/package-lock.json /app/platform-oauth/package-lock.json
WORKDIR /app/platform-oauth
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["node", "/app/platform-oauth/dist/index.js"]
16 changes: 16 additions & 0 deletions integrationos-platform-oauth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# IntegrationOS OAuth API

The API for OAuth based Connections.

### Setup

To change the default `PORT`:

```bash
export EXPRESS_SERVER_PORT=3009
```

```bash
> npm install
> npm start
```
23 changes: 23 additions & 0 deletions integrationos-platform-oauth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "platform-oauth",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "ts-node src/index.ts",
"build": "tsc"
},
"author": "@integrationos",
"license": "ISC",
"description": "",
"dependencies": {
"axios": "^1.7.2",
"dotenv": "^16.4.5",
"express": "^4.19.2"
},
"devDependencies": {
"@types/express": "^4.17.21",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
}
}
31 changes: 31 additions & 0 deletions integrationos-platform-oauth/src/connections/clover/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import axios from "axios";

import { DataObject, OAuthResponse } from "../../lib/types";

export const init = async ({ body }: DataObject): Promise<OAuthResponse> => {
try {
const requestBody = {
grant_type: "authorization_code",
code: body.metadata?.code,
client_id: body.clientId,
client_secret: body.clientSecret,
};

const response = await axios.post(`${body.metadata.formData.CLOVER_REGION_DOMAIN}/oauth/token`, requestBody);

const accessToken = response.data?.access_token;

return {
accessToken,
refreshToken: accessToken,
expiresIn: 2147483647,
tokenType: "Bearer",
meta: {
merchantId: body.metadata?.additionalData?.merchant_id,
employeeId: body.metadata?.additionalData?.employee_id,
}
};
} catch (error) {
throw new Error(`Error fetching access token for ${body.params?.platform}: ${error}`);
}
};
11 changes: 11 additions & 0 deletions integrationos-platform-oauth/src/connections/clover/refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { OAuthResponse } from "../../lib/types";

export const refresh = async (payload: any): Promise<OAuthResponse> => {
return {
accessToken: payload.accessToken,
refreshToken: payload.refreshToken,
expiresIn: payload.expiresIn,
tokenType: payload.tokenType,
meta: payload.meta
};
};
58 changes: 58 additions & 0 deletions integrationos-platform-oauth/src/connections/freshbooks/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import axios from "axios";

import { DataObject, OAuthResponse } from "../../lib/types";

export const init = async ({ body }: DataObject): Promise<OAuthResponse> => {
try {
const requestBody = {
grant_type: "authorization_code",
code: body.metadata?.code,
client_id: body.clientId,
client_secret: body.clientSecret,
redirect_uri: body.metadata?.redirectUri,
};

const response = await axios.post(
`https://api.freshbooks.com/auth/oauth/token`,
requestBody
);

const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
token_type: tokenType,
} = response.data;

// Get Additional Information required by hitting me URL
const additionalData = await axios.get(
"https://api.freshbooks.com/auth/api/v1/users/me",
{
headers: {
Authorization: `${tokenType} ${accessToken}`,
},
}
);

if (!additionalData?.data) {
throw new Error(`Access token validation failed.`);
}

const {
business: { business_uuid: businessId, account_id: accountId },
} = additionalData.data.response.business_memberships[0];

return {
accessToken,
refreshToken,
expiresIn,
tokenType,
meta: {
businessId,
accountId,
},
};
} catch (error) {
throw new Error(`Error fetching access token for freshbooks: ${error}`);
}
};
67 changes: 67 additions & 0 deletions integrationos-platform-oauth/src/connections/freshbooks/refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import axios from "axios";

import { DataObject, OAuthResponse } from "../../lib/types";

export const refresh = async ({ body }: DataObject): Promise<OAuthResponse> => {
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,
client_secret,
redirect_uri,
};

const response = await axios.post(
`https://api.freshbooks.com/auth/oauth/token`,
requestBody
);

const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
token_type: tokenType,
} = response.data;

// Get Additional Information required by hitting me URL
const additionalData = await axios.get(
"https://api.freshbooks.com/auth/api/v1/users/me",
{
headers: {
Authorization: `${tokenType} ${accessToken}`,
},
}
);

if (!additionalData?.data) {
throw new Error(`Access token validation failed.`);
}

const {
business: { business_uuid: businessId, account_id: accountId },
} = additionalData.data.response.business_memberships[0];

return {
accessToken,
refreshToken,
expiresIn,
tokenType,
meta: {
businessId,
accountId,
},
};
} catch (error) {
throw new Error(
`Error fetching access token for ${body.params?.platform}: ${error}`
);
}
};
42 changes: 42 additions & 0 deletions integrationos-platform-oauth/src/connections/square/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import axios from "axios";

import { DataObject, OAuthResponse } from "../../lib/types";
import { convertToTimestamp } from "../../lib/helpers";

export const init = async ({ body }: DataObject): Promise<OAuthResponse> => {
try {
const requestBody = {
grant_type: "authorization_code",
code: body.metadata?.code,
client_id: body.clientId,
client_secret: body.clientSecret,
redirect_uri: body.metadata?.redirectUri,
};

const response = await axios.post(`https://connect.squareup.com/oauth2/token`, requestBody);

const {
data: {
access_token,
refresh_token,
expires_at,
token_type,
merchant_id,
short_lived
}
} = response;

return {
accessToken: access_token,
refreshToken: refresh_token,
expiresIn: Math.floor((await convertToTimestamp(expires_at) - (new Date().getTime())) / 1000),
tokenType: token_type === "bearer" ? "Bearer" : token_type,
meta: {
merchantId: merchant_id,
shortLived: short_lived
}
};
} catch (error) {
throw new Error(`Error fetching access token for ${body.params?.platform}: ${error}`);
}
};
47 changes: 47 additions & 0 deletions integrationos-platform-oauth/src/connections/square/refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios from "axios";

import { DataObject, OAuthResponse } from "../../lib/types";
import { convertToTimestamp } from "../../lib/helpers";

export const refresh = async ({ body }: DataObject): Promise<OAuthResponse> => {
try {
const {
OAUTH_CLIENT_ID: client_id,
OAUTH_CLIENT_SECRET: client_secret,
OAUTH_REFRESH_TOKEN: refresh_token
} = body;

const requestBody = {
grant_type: "refresh_token",
client_id,
client_secret,
refresh_token,
};

const response = await axios.post(`https://connect.squareup.com/oauth2/token`, requestBody);

const {
data: {
access_token: accessToken,
refresh_token: refreshToken,
expires_at: expiresAt,
token_type: tokenType,
merchant_id: merchantId,
short_lived: shortLived
}
} = response;

return {
accessToken,
refreshToken,
expiresIn: Math.floor((await convertToTimestamp(expiresAt) - new Date().getTime()) / 1000),
tokenType: tokenType === "bearer" ? "Bearer" : tokenType,
meta: {
merchantId,
shortLived
}
};
} catch (error) {
throw new Error(`Error fetching access token for ${body.params?.platform}: ${error}`);
}
};
Loading

0 comments on commit dbc4b9a

Please sign in to comment.