From 84d2bb3cf591cabf30b654f5b38474a90869649b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sun, 24 Apr 2022 23:32:47 +0100 Subject: [PATCH 1/4] auto fetch access token in gsheets integration --- packages/backend-core/src/db/utils.js | 1 + .../middleware/passport/datasource/google.js | 17 ++++-- .../server/src/integrations/googlesheets.ts | 54 ++++++++++++++++--- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/packages/backend-core/src/db/utils.js b/packages/backend-core/src/db/utils.js index feb17c4129..b0b7794096 100644 --- a/packages/backend-core/src/db/utils.js +++ b/packages/backend-core/src/db/utils.js @@ -460,3 +460,4 @@ exports.getConfigParams = getConfigParams exports.getScopedFullConfig = getScopedFullConfig exports.generateNewUsageQuotaDoc = generateNewUsageQuotaDoc exports.generateDevInfoID = generateDevInfoID +exports.getPlatformUrl = getPlatformUrl diff --git a/packages/backend-core/src/middleware/passport/datasource/google.js b/packages/backend-core/src/middleware/passport/datasource/google.js index 5046815b1f..ff305b01b3 100644 --- a/packages/backend-core/src/middleware/passport/datasource/google.js +++ b/packages/backend-core/src/middleware/passport/datasource/google.js @@ -2,7 +2,7 @@ const google = require("../google") const { Cookies, Configs } = require("../../../constants") const { clearCookie, getCookie } = require("../../../utils") const { getDB } = require("../../../db") -const { getScopedConfig } = require("../../../db/utils") +const { getScopedConfig, getPlatformUrl } = require("../../../db/utils") const environment = require("../../../environment") const { getGlobalDB } = require("../../../tenancy") @@ -21,10 +21,20 @@ async function fetchGoogleCreds() { return config } +async function platformUrl() { + const db = getGlobalDB() + const publicConfig = await getScopedConfig(db, { + type: Configs.SETTINGS, + }) + return getPlatformUrl(publicConfig) +} + async function preAuth(passport, ctx, next) { // get the relevant config const googleConfig = await fetchGoogleCreds() - let callbackUrl = `${environment.PLATFORM_URL}/api/global/auth/datasource/google/callback` + const platUrl = await platformUrl() + + let callbackUrl = `${platUrl}/api/global/auth/datasource/google/callback` const strategy = await google.strategyFactory(googleConfig, callbackUrl) if (!ctx.query.appId || !ctx.query.datasourceId) { @@ -41,8 +51,9 @@ async function preAuth(passport, ctx, next) { async function postAuth(passport, ctx, next) { // get the relevant config const config = await fetchGoogleCreds() + const platUrl = await platformUrl() - let callbackUrl = `${environment.PLATFORM_URL}/api/global/auth/datasource/google/callback` + let callbackUrl = `${platUrl}/api/global/auth/datasource/google/callback` const strategy = await google.strategyFactory( config, callbackUrl, diff --git a/packages/server/src/integrations/googlesheets.ts b/packages/server/src/integrations/googlesheets.ts index c58fdea551..f6d800da7a 100644 --- a/packages/server/src/integrations/googlesheets.ts +++ b/packages/server/src/integrations/googlesheets.ts @@ -10,6 +10,7 @@ import { Table, TableSchema } from "../definitions/common" import { buildExternalTableId } from "./utils" import { DataSourceOperation, FieldTypes } from "../constants" import { GoogleSpreadsheet } from "google-spreadsheet" +import fetch from "node-fetch" import env from "../environment" module GoogleSheetsModule { @@ -28,6 +29,16 @@ module GoogleSheetsModule { refreshToken: string } + interface AuthTokenRequest { + client_id: string + client_secret: string + refresh_token: string + } + + interface AuthTokenResponse { + access_token: string + } + const SCHEMA: Integration = { plus: true, auth: { @@ -40,6 +51,7 @@ module GoogleSheetsModule { friendlyName: "Google Sheets", datasource: { spreadsheetId: { + display: "Google Sheet URL", type: DatasourceFieldTypes.STRING, required: true, }, @@ -135,6 +147,30 @@ module GoogleSheetsModule { return parts.length > 5 ? parts[5] : spreadsheetId } + async fetchAccessToken( + payload: AuthTokenRequest + ): Promise { + const response = await fetch( + "https://www.googleapis.com/oauth2/v4/token", + { + method: "POST", + body: JSON.stringify({ + ...payload, + grant_type: "refresh_token", + }), + headers: { + "Content-Type": "application/json", + }, + } + ) + + if (response.status !== 200) { + throw new Error("Error authenticating with google sheets.") + } + + return response.json() + } + async connect() { try { // Initialise oAuth client @@ -154,14 +190,18 @@ module GoogleSheetsModule { clientId: googleConfig.clientID, clientSecret: googleConfig.clientSecret, }) - oauthClient.on("tokens", tokens => { - oauthClient.setCredentials({ - refresh_token: googleConfig.refreshToken, - access_token: tokens.access_token, - }) + + const tokenResponse = await this.fetchAccessToken({ + client_id: googleConfig.clientID, + client_secret: googleConfig.clientSecret, + refresh_token: this.config.auth.refreshToken, }) - oauthClient.credentials.access_token = this.config.auth.accessToken - oauthClient.credentials.refresh_token = this.config.auth.refreshToken + + oauthClient.setCredentials({ + refresh_token: this.config.auth.refreshToken, + access_token: tokenResponse.access_token, + }) + this.client.useOAuth2Client(oauthClient) await this.client.loadInfo() } catch (err) { From 46b424308fb0acc30d8a955fe09aa0925ebcaf1b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sun, 24 Apr 2022 23:33:50 +0100 Subject: [PATCH 2/4] only save refresh token in google auth --- .../backend-core/src/middleware/passport/datasource/google.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-core/src/middleware/passport/datasource/google.js b/packages/backend-core/src/middleware/passport/datasource/google.js index ff305b01b3..62d0339c11 100644 --- a/packages/backend-core/src/middleware/passport/datasource/google.js +++ b/packages/backend-core/src/middleware/passport/datasource/google.js @@ -59,7 +59,7 @@ async function postAuth(passport, ctx, next) { callbackUrl, (accessToken, refreshToken, profile, done) => { clearCookie(ctx, Cookies.DatasourceAuth) - done(null, { accessToken, refreshToken }) + done(null, { refreshToken }) } ) From 4091642c6705fd237a31a8e0b7f66de64d512e62 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sun, 24 Apr 2022 23:38:52 +0100 Subject: [PATCH 3/4] remove google auth button --- .../TableIntegrationMenu/PlusConfigForm.svelte | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte index 66d7d43841..c94e750c29 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/PlusConfigForm.svelte @@ -15,7 +15,6 @@ import ArrayRenderer from "components/common/renderers/ArrayRenderer.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte" import { goto } from "@roxi/routify" - import GoogleButton from "../_components/GoogleButton.svelte" export let datasource export let save @@ -161,11 +160,6 @@ Fetch tables - {#if integration.auth} - {#if integration.auth.type === "google"} - - {/if} - {/if} From 31412cb83bb400b5a7b5d963b61745120bc09582 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 25 Apr 2022 00:05:32 +0100 Subject: [PATCH 4/4] moving node fetch import into module --- packages/server/src/integrations/googlesheets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/integrations/googlesheets.ts b/packages/server/src/integrations/googlesheets.ts index f6d800da7a..e6d79bfdde 100644 --- a/packages/server/src/integrations/googlesheets.ts +++ b/packages/server/src/integrations/googlesheets.ts @@ -10,13 +10,13 @@ import { Table, TableSchema } from "../definitions/common" import { buildExternalTableId } from "./utils" import { DataSourceOperation, FieldTypes } from "../constants" import { GoogleSpreadsheet } from "google-spreadsheet" -import fetch from "node-fetch" import env from "../environment" module GoogleSheetsModule { const { getGlobalDB } = require("@budibase/backend-core/tenancy") const { getScopedConfig } = require("@budibase/backend-core/db") const { Configs } = require("@budibase/backend-core/constants") + const fetch = require("node-fetch") interface GoogleSheetsConfig { spreadsheetId: string