From 72476ceb8bca0ce66d999889a84d8b4029910cdb Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Tue, 26 Oct 2021 23:41:25 +0200 Subject: [PATCH] feat: introduce github authentication --- packages/backend/package.json | 1 + .../src/apps/github/assets/favicon.svg | 6 + packages/backend/src/apps/github/index.ts | 77 +++++++ packages/backend/src/apps/github/info.json | 218 ++++++++++++++++++ yarn.lock | 25 +- 5 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 packages/backend/src/apps/github/assets/favicon.svg create mode 100644 packages/backend/src/apps/github/index.ts create mode 100644 packages/backend/src/apps/github/info.json diff --git a/packages/backend/package.json b/packages/backend/package.json index 3e7f5edb..7695543b 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -15,6 +15,7 @@ "db:migrate": "knex migrate:latest" }, "dependencies": { + "@octokit/oauth-methods": "^1.2.6", "axios": "0.24.0", "bcrypt": "^5.0.1", "cors": "^2.8.5", diff --git a/packages/backend/src/apps/github/assets/favicon.svg b/packages/backend/src/apps/github/assets/favicon.svg new file mode 100644 index 00000000..8a75650c --- /dev/null +++ b/packages/backend/src/apps/github/assets/favicon.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/packages/backend/src/apps/github/index.ts b/packages/backend/src/apps/github/index.ts new file mode 100644 index 00000000..9c2a1e51 --- /dev/null +++ b/packages/backend/src/apps/github/index.ts @@ -0,0 +1,77 @@ +import { + getWebFlowAuthorizationUrl, + exchangeWebFlowCode, + checkToken, +} from '@octokit/oauth-methods'; +import App from '../../models/app'; +import Field from '../../types/field'; + +export default class Github { + connectionData: any + appData: any + scopes: string[] = ['repo'] + + constructor(connectionData: any) { + this.connectionData = connectionData; + this.appData = App.findOneByKey('github'); + } + + get oauthRedirectUrl() { + return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value; + } + + async createAuthData() { + const { url } = await getWebFlowAuthorizationUrl({ + clientType: "oauth-app", + clientId: this.connectionData.consumerKey, + redirectUrl: this.oauthRedirectUrl, + scopes: this.scopes, + }); + + return { + url: url, + }; + } + + async verifyCredentials() { + const { data, authentication } = await exchangeWebFlowCode({ + clientType: "oauth-app", + clientId: this.connectionData.consumerKey, + clientSecret: this.connectionData.consumerSecret, + code: this.connectionData.oauthVerifier, + }); + + this.connectionData.accessToken = data.access_token; + + const tokenInfo = await this.getTokenInfo(); + + return { + consumerKey: this.connectionData.consumerKey, + consumerSecret: this.connectionData.consumerSecret, + accessToken: data.access_token, + scope: data.scope, + tokenType: data.token_type, + userId: tokenInfo.data.user.id, + screenName: tokenInfo.data.user.login, + } + } + + async getTokenInfo() { + return checkToken({ + clientType: "oauth-app", + clientId: this.connectionData.consumerKey, + clientSecret: this.connectionData.consumerSecret, + token: this.connectionData.accessToken, + }); + } + + async isStillVerified() { + try { + await this.getTokenInfo(); + + return true; + } catch { + return false; + } + } +} diff --git a/packages/backend/src/apps/github/info.json b/packages/backend/src/apps/github/info.json new file mode 100644 index 00000000..e8789051 --- /dev/null +++ b/packages/backend/src/apps/github/info.json @@ -0,0 +1,218 @@ +{ + "name": "Github", + "key": "github", + "iconUrl": "{BASE_URL}/apps/github/assets/favicon.svg", + "docUrl": "https://automatisch.io/docs/github", + "primaryColor": "000000", + "fields": [ + { + "key": "oAuthRedirectUrl", + "label": "OAuth Redirect URL", + "type": "string", + "required": true, + "readOnly": true, + "value": "https://localhost:3001/app/github/connections/add", + "placeholder": null, + "description": "When asked to input an OAuth callback or redirect URL in Github OAuth, enter the URL above.", + "docUrl": "https://automatisch.io/docs/github#oauth-redirect-url", + "clickToCopy": true + }, + { + "key": "consumerKey", + "label": "Consumer Key", + "type": "string", + "required": true, + "readOnly": false, + "value": null, + "placeholder": null, + "description": null, + "docUrl": "https://automatisch.io/docs/github#consumer-key", + "clickToCopy": false + }, + { + "key": "consumerSecret", + "label": "Consumer Secret", + "type": "string", + "required": true, + "readOnly": false, + "value": null, + "placeholder": null, + "description": null, + "docUrl": "https://automatisch.io/docs/github#consumer-secret", + "clickToCopy": false + } + ], + "authenticationSteps": [ + { + "step": 1, + "type": "mutation", + "name": "createConnection", + "arguments": [ + { + "name": "key", + "value": "{key}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "consumerKey", + "value": "{fields.consumerKey}" + }, + { + "name": "consumerSecret", + "value": "{fields.consumerSecret}" + } + ] + } + ] + }, + { + "step": 2, + "type": "mutation", + "name": "createAuthData", + "arguments": [ + { + "name": "id", + "value": "{createConnection.id}" + } + ] + }, + { + "step": 3, + "type": "openWithPopup", + "name": "openAuthPopup", + "arguments": [ + { + "name": "url", + "value": "{createAuthData.url}" + } + ] + }, + { + "step": 4, + "type": "mutation", + "name": "updateConnection", + "arguments": [ + { + "name": "id", + "value": "{createConnection.id}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "oauthVerifier", + "value": "{openAuthPopup.code}" + } + ] + } + ] + }, + { + "step": 5, + "type": "mutation", + "name": "verifyConnection", + "arguments": [ + { + "name": "id", + "value": "{createConnection.id}" + } + ] + } + ], + "reconnectionSteps": [ + { + "step": 1, + "type": "mutation", + "name": "resetConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + } + ] + }, + { + "step": 2, + "type": "mutation", + "name": "updateConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "consumerKey", + "value": "{fields.consumerKey}" + }, + { + "name": "consumerSecret", + "value": "{fields.consumerSecret}" + } + ] + } + ] + }, + { + "step": 3, + "type": "mutation", + "name": "createAuthData", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + } + ] + }, + { + "step": 4, + "type": "openWithPopup", + "name": "openAuthPopup", + "arguments": [ + { + "name": "url", + "value": "{createAuthData.url}" + } + ] + }, + { + "step": 5, + "type": "mutation", + "name": "updateConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "oauthVerifier", + "value": "{openAuthPopup.code}" + } + ] + } + ] + }, + { + "step": 6, + "type": "mutation", + "name": "verifyConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + } + ] + } + ] +} diff --git a/yarn.lock b/yarn.lock index c447c058..5b5d5630 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2636,6 +2636,22 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/oauth-authorization-url@^4.3.1": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-4.3.3.tgz#6a6ef38f243086fec882b62744f39b517528dfb9" + integrity sha512-lhP/t0i8EwTmayHG4dqLXgU+uPVys4WD/qUNvC+HfB1S1dyqULm5Yx9uKc1x79aP66U1Cb4OZeW8QU/RA9A4XA== + +"@octokit/oauth-methods@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-1.2.6.tgz#b9ac65e374b2cc55ee9dd8dcdd16558550438ea7" + integrity sha512-nImHQoOtKnSNn05uk2o76om1tJWiAo4lOu2xMAHYsNr0fwopP+Dv+2MlGvaMMlFjoqVd3fF3X5ZDTKCsqgmUaQ== + dependencies: + "@octokit/oauth-authorization-url" "^4.3.1" + "@octokit/request" "^5.4.14" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.12.2" + btoa-lite "^1.0.0" + "@octokit/openapi-types@^11.2.0": version "11.2.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" @@ -2675,7 +2691,7 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.6.0": +"@octokit/request@^5.4.14", "@octokit/request@^5.6.0": version "5.6.2" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.2.tgz#1aa74d5da7b9e04ac60ef232edd9a7438dcf32d8" integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== @@ -2697,7 +2713,7 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": +"@octokit/types@^6.0.3", "@octokit/types@^6.12.2", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": version "6.34.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== @@ -4784,6 +4800,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"