From 05ee1876a1b4e9f22159644911bb5d48d98e7cd2 Mon Sep 17 00:00:00 2001 From: Nabarun Date: Tue, 27 Feb 2024 17:28:09 +0530 Subject: [PATCH] Delete GitHub OAuth app authorization to force re-login --- README.md | 6 ---- packages/backend/.gitignore | 2 +- packages/backend/package.json | 2 ++ packages/backend/src/resolvers.ts | 2 +- packages/backend/src/service.ts | 28 +++++++++++++++++-- packages/frontend/src/components/Sidebar.tsx | 8 ++++-- .../projects/create/ConnectAccount.tsx | 2 +- packages/frontend/src/pages/OrgSlug.tsx | 8 ++---- yarn.lock | 24 ++++++++++------ 9 files changed, 55 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d75ccdf..ce3dd61 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,6 @@ mv environments/local.toml.example environments/local.toml ``` -- Set `gitHub.oAuth.clientId` and `gitHub.oAuth.clientSecret` in backend [config file](packages/backend/environments/local.toml) - - Client ID and secret will be available after [creating an OAuth app](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) - - In "Homepage URL", type `http://localhost:3000` - - In "Authorization callback URL", type `http://localhost:3000/organization/projects/create` - - Generate a new client secret after app is created - ### Backend Production - Let us assume the following domains for backend and frontend diff --git a/packages/backend/.gitignore b/packages/backend/.gitignore index 987e409..3b94789 100644 --- a/packages/backend/.gitignore +++ b/packages/backend/.gitignore @@ -1,3 +1,3 @@ db dist -environments/local.toml \ No newline at end of file +environments/local.toml diff --git a/packages/backend/package.json b/packages/backend/package.json index f629dab..d3f9786 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -21,6 +21,7 @@ "luxon": "^3.4.4", "nanoid": "3", "nanoid-dictionary": "^5.0.0-beta.1", + "node-fetch": "2", "octokit": "^3.1.2", "reflect-metadata": "^0.2.1", "semver": "^7.6.0", @@ -46,6 +47,7 @@ "devDependencies": { "@types/express-session": "^1.17.10", "@types/fs-extra": "^11.0.4", + "@types/node-fetch": "^2.6.11", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "better-sqlite3": "^9.2.2", diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index e2e3339..6b16798 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -294,7 +294,7 @@ export const createResolvers = async (service: Service): Promise => { unauthenticateGitHub: async (_: any, __: object, context: any) => { try { - return service.unauthenticateGitHub(context.user, { gitHubToken: null }); + return service.unauthenticateGitHub(context.user); } catch (err) { log(err); return false; diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index d204537..aac14d8 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -2,6 +2,7 @@ import assert from 'assert'; import debug from 'debug'; import { DeepPartial, FindOptionsWhere } from 'typeorm'; import { Octokit, RequestError } from 'octokit'; +import fetch from 'node-fetch'; import { OAuthApp } from '@octokit/oauth-app'; @@ -792,7 +793,30 @@ export class Service { return { token }; } - async unauthenticateGitHub (user: User, data: DeepPartial): Promise { - return this.db.updateUser(user, data); + async unauthenticateGitHub (user: User): Promise { + const clientId = this.config.gitHubConfig.oAuth.clientId; + const clientSecret = this.config.gitHubConfig.oAuth.clientSecret; + assert(user.gitHubToken, `GitHub access token is not set for user ${user.ethAddress}`); + + // https://docs.github.com/en/rest/apps/oauth-applications#delete-an-app-authorization + const response = await fetch( + `https://api.github.com/applications/${clientId}/grant`, + { + method: 'DELETE', + headers: { + // Basic authentication with client ID & secret is needed for OAuth app REST API + Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`, + Accept: 'application/vnd.github.v3+json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + access_token: user.gitHubToken + }) + } + ); + + assert(response.ok, Error(response.statusText)); + + return this.db.updateUser(user, { gitHubToken: null }); } } diff --git a/packages/frontend/src/components/Sidebar.tsx b/packages/frontend/src/components/Sidebar.tsx index c0ee8fc..09158e6 100644 --- a/packages/frontend/src/components/Sidebar.tsx +++ b/packages/frontend/src/components/Sidebar.tsx @@ -5,7 +5,7 @@ import { Organization } from 'gql-client'; import { Option } from '@material-tailwind/react'; import { useDisconnect } from 'wagmi'; -import { useGQLClient } from '../context/GQLClientContext'; +import { useGQLClient } from 'context/GQLClientContext'; import AsyncSelect from './shared/AsyncSelect'; import { ChevronGrabberHorizontal, @@ -36,10 +36,12 @@ const Sidebar = () => { setSelectedOrgSlug(orgSlug); }, [orgSlug]); - const handleLogOut = useCallback(() => { + const handleLogOut = useCallback(async () => { disconnect(); + await client.unauthenticateGithub(); + navigate('/login'); - }, [disconnect, navigate]); + }, [disconnect, navigate, client]); return (
diff --git a/packages/frontend/src/components/projects/create/ConnectAccount.tsx b/packages/frontend/src/components/projects/create/ConnectAccount.tsx index 084cd23..9b0f8eb 100644 --- a/packages/frontend/src/components/projects/create/ConnectAccount.tsx +++ b/packages/frontend/src/components/projects/create/ConnectAccount.tsx @@ -1,7 +1,7 @@ import React from 'react'; import OauthPopup from 'react-oauth-popup'; -import { useGQLClient } from '../../../context/GQLClientContext'; +import { useGQLClient } from 'context/GQLClientContext'; import { Button } from 'components/shared/Button'; import { GitIcon, diff --git a/packages/frontend/src/pages/OrgSlug.tsx b/packages/frontend/src/pages/OrgSlug.tsx index b2214e4..9bd479c 100644 --- a/packages/frontend/src/pages/OrgSlug.tsx +++ b/packages/frontend/src/pages/OrgSlug.tsx @@ -7,18 +7,16 @@ import { OctokitProvider } from '../context/OctokitContext'; const OrgSlug = () => { return (
- <> +
- - - +
- +
); }; diff --git a/yarn.lock b/yarn.lock index 3ec5b34..477f0bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5075,6 +5075,14 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== +"@types/node-fetch@^2.6.11": + version "2.6.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + "@types/node-forge@^1.3.0": version "1.3.11" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" @@ -8756,7 +8764,7 @@ dotenv@~16.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.2.tgz#3cb611ce5a63002dbabf7c281bc331f69d28f03f" integrity sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ== -downshift@^8.2.3: +downshift@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/downshift/-/downshift-8.3.2.tgz#35cccfdadfe183a9e6bceb1dca68a6a592bb5602" integrity sha512-kO5mnwMbWB1OIPgIO4wxK0HtSJbaPzx3XTOcnN36I6i08iVc4C2Fftye3UZYVN+W03b13o1kEmN2118G5vcgeQ== @@ -13627,6 +13635,13 @@ node-fetch-native@^1.4.0, node-fetch-native@^1.4.1, node-fetch-native@^1.6.1: resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.2.tgz#f439000d972eb0c8a741b65dcda412322955e1c6" integrity sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w== +node-fetch@2, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -13634,13 +13649,6 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - node-forge@^1, node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"