From 7a665c3c2b4b18bb1c4f5ed747058a98a5fd8533 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 29 May 2020 15:16:10 +0200 Subject: [PATCH] Add github as an auth provider to the backend --- plugins/auth-backend/package.json | 14 ++-- plugins/auth-backend/src/providers/config.ts | 8 ++ .../auth-backend/src/providers/factories.ts | 2 + .../src/providers/github/index.ts | 17 ++++ .../src/providers/github/provider.ts | 84 +++++++++++++++++++ yarn.lock | 33 ++++---- 6 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 plugins/auth-backend/src/providers/github/index.ts create mode 100644 plugins/auth-backend/src/providers/github/provider.ts diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 9cb8017f15..505240853d 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -16,21 +16,23 @@ }, "dependencies": { "@backstage/backend-common": "^0.1.1-alpha.6", + "@types/cookie-parser": "^1.4.2", + "@types/passport": "^1.0.3", + "@types/passport-github2": "^1.2.4", + "@types/passport-google-oauth20": "^2.0.3", "compression": "^1.7.4", + "cookie-parser": "^1.4.5", "cors": "^2.8.5", "express": "^4.17.1", "express-promise-router": "^3.0.3", "fs-extra": "^9.0.0", "helmet": "^3.22.0", "morgan": "^1.10.0", - "winston": "^3.2.1", - "yn": "^4.0.0", "passport": "^0.4.1", + "passport-github2": "^0.1.12", "passport-google-oauth20": "^2.0.0", - "cookie-parser": "^1.4.5", - "@types/passport": "^1.0.3", - "@types/passport-google-oauth20": "^2.0.3", - "@types/cookie-parser": "^1.4.2" + "winston": "^3.2.1", + "yn": "^4.0.0" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.6", diff --git a/plugins/auth-backend/src/providers/config.ts b/plugins/auth-backend/src/providers/config.ts index 45098afb89..dad87e0448 100644 --- a/plugins/auth-backend/src/providers/config.ts +++ b/plugins/auth-backend/src/providers/config.ts @@ -23,4 +23,12 @@ export const providers = [ callbackURL: 'http://localhost:7000/auth/google/handler/frame', }, }, + { + provider: 'github', + options: { + clientID: process.env.AUTH_GITHUB_CLIENT_ID!, + clientSecret: process.env.AUTH_GITHUB_CLIENT_SECRET!, + callbackURL: 'http://localhost:7000/auth/github/handler/frame', + }, + }, ]; diff --git a/plugins/auth-backend/src/providers/factories.ts b/plugins/auth-backend/src/providers/factories.ts index 0a1e639082..10e4153714 100644 --- a/plugins/auth-backend/src/providers/factories.ts +++ b/plugins/auth-backend/src/providers/factories.ts @@ -16,10 +16,12 @@ import { AuthProviderFactories, AuthProviderFactory } from './types'; import { GoogleAuthProvider } from './google'; +import { GithubAuthProvider } from './github'; export class ProviderFactories { private static readonly providerFactories: AuthProviderFactories = { google: GoogleAuthProvider, + github: GithubAuthProvider, }; public static getProviderFactory(providerId: string): AuthProviderFactory { diff --git a/plugins/auth-backend/src/providers/github/index.ts b/plugins/auth-backend/src/providers/github/index.ts new file mode 100644 index 0000000000..c3a48d35e0 --- /dev/null +++ b/plugins/auth-backend/src/providers/github/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { GithubAuthProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts new file mode 100644 index 0000000000..1e8022e1cc --- /dev/null +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import express from 'express'; +import { Strategy as GithubStrategy } from 'passport-github2'; +import { + executeFrameHandlerStrategy, + executeRedirectStrategy, + executeRefreshTokenStrategy, +} from '../PassportStrategyHelper'; +import { + OAuthProviderHandlers, + AuthProviderConfig, + RedirectInfo, + AuthInfoBase, + AuthInfoPrivate, +} from '../types'; + +export class GithubAuthProvider implements OAuthProviderHandlers { + private readonly providerConfig: AuthProviderConfig; + private readonly _strategy: GithubStrategy; + + constructor(providerConfig: AuthProviderConfig) { + this.providerConfig = providerConfig; + this._strategy = new GithubStrategy( + { ...this.providerConfig.options }, + ( + accessToken: any, + refreshToken: any, + params: any, + profile: any, + done: any, + ) => { + done( + undefined, + { + profile, + accessToken, + scope: 'user', // params.scope is an empty string here for some reason, so hardcoding for now + expiresInSeconds: params.expires_in, + }, + { refreshToken }, + ); + }, + ); + } + + async start(req: express.Request, options: any): Promise { + return await executeRedirectStrategy(req, this._strategy, options); + } + + async handler( + req: express.Request, + ): Promise<{ user: AuthInfoBase; info: AuthInfoPrivate }> { + return await executeFrameHandlerStrategy(req, this._strategy); + } + + async refresh(refreshToken: string, scope: string): Promise { + const { accessToken, params } = await executeRefreshTokenStrategy( + this._strategy, + refreshToken, + scope, + ); + + return { + accessToken, + expiresInSeconds: params.expires_in, + scope: params.scope, + }; + } +} diff --git a/yarn.lock b/yarn.lock index 8439f5b0b9..30537a6cac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4281,6 +4281,15 @@ resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/passport-github2@^1.2.4": + version "1.2.4" + resolved "https://registry.npmjs.org/@types/passport-github2/-/passport-github2-1.2.4.tgz#f56c386d1fe6435e359430e57adc1747a627bd86" + integrity sha512-dtGtA0Uyzk6ne3SrgQi/I1ClClLE3i7JmSiMaJgkGH8v1nbE9JdBpG7QWJ1XPlLdcf7EvoPdHmkWN2+Kln9y8g== + dependencies: + "@types/express" "*" + "@types/passport" "*" + "@types/passport-oauth2" "*" + "@types/passport-google-oauth20@^2.0.3": version "2.0.3" resolved "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.3.tgz#f554ff6d39f395acff3f1d762e54462194dac8da" @@ -4290,15 +4299,7 @@ "@types/passport" "*" "@types/passport-oauth2" "*" -"@types/passport-oauth2-refresh@^1.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/passport-oauth2-refresh/-/passport-oauth2-refresh-1.1.1.tgz#cbe466d4fcac36182fd75bf55279c0b1e953c382" - integrity sha512-Tw0JvfDPv9asgFPACd9oOGCaD/0/Uyi+QF7fmrJC74cJKC6I8N8wwhJJHyfd1N2E/qaLgTh431lhOa9jicpNdg== - dependencies: - "@types/oauth" "*" - "@types/passport-oauth2" "*" - -"@types/passport-oauth2@*", "@types/passport-oauth2@^1.4.9": +"@types/passport-oauth2@*": version "1.4.9" resolved "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.9.tgz#134007c4b505a82548c9cb19094c5baeb2205c92" integrity sha512-QP0q+NVQOaIu2r0e10QWkiUA0Ya5mOBHRJN0UrI+LolMLOP1/VN4EVIpJ3xVwFo+xqNFRoFvFwJhBvKnk7kpUA== @@ -16103,6 +16104,13 @@ pascalcase@^0.1.1: resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +passport-github2@^0.1.12: + version "0.1.12" + resolved "https://registry.npmjs.org/passport-github2/-/passport-github2-0.1.12.tgz#a72ebff4fa52a35bc2c71122dcf470d1116f772c" + integrity sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw== + dependencies: + passport-oauth2 "1.x.x" + passport-google-oauth20@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz#0d241b2d21ebd3dc7f2b60669ec4d587e3a674ef" @@ -16110,12 +16118,7 @@ passport-google-oauth20@^2.0.0: dependencies: passport-oauth2 "1.x.x" -passport-oauth2-refresh@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/passport-oauth2-refresh/-/passport-oauth2-refresh-2.0.0.tgz#7b19c77ff3cc000819c69f6ad9e318450f57b85e" - integrity sha512-yXvCB6nem/O+WThhiyI3TlPXpzSGY+9+hy9OTx9QF8e9GInplyRHxHaaOhFylKvnof9UmWHAufQFZk8cO1Fb2g== - -passport-oauth2@1.x.x, passport-oauth2@^1.5.0: +passport-oauth2@1.x.x: version "1.5.0" resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz#64babbb54ac46a4dcab35e7f266ed5294e3c4108" integrity sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==