From 311ab67ebeae532db60643673b0882d21fac4ce5 Mon Sep 17 00:00:00 2001 From: Balu Babu Date: Fri, 13 Jan 2023 02:11:42 +0530 Subject: [PATCH] feat: github login added --- packages/hoppscotch-backend/package.json | 2 + packages/hoppscotch-backend/pnpm-lock.yaml | 19 +++++++ .../src/auth/auth.controller.ts | 12 +++++ .../src/auth/auth.module.ts | 9 +++- .../src/auth/strategies/github.strategy.ts | 49 +++++++++++++++++++ packages/hoppscotch-backend/src/utils.ts | 2 +- 6 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index 011890024..786da1de3 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -44,6 +44,7 @@ "ioredis": "^5.2.4", "luxon": "^3.2.1", "passport": "^0.6.0", + "passport-github2": "^0.1.12", "passport-google-oauth20": "^2.0.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", @@ -65,6 +66,7 @@ "@types/jest": "^27.5.2", "@types/luxon": "^3.2.0", "@types/node": "^18.11.10", + "@types/passport-github2": "^1.2.5", "@types/passport-google-oauth20": "^2.0.11", "@types/passport-jwt": "^3.0.8", "@types/supertest": "^2.0.12", diff --git a/packages/hoppscotch-backend/pnpm-lock.yaml b/packages/hoppscotch-backend/pnpm-lock.yaml index 3e41538d3..a943bff3d 100644 --- a/packages/hoppscotch-backend/pnpm-lock.yaml +++ b/packages/hoppscotch-backend/pnpm-lock.yaml @@ -20,6 +20,7 @@ specifiers: '@types/jest': ^27.5.2 '@types/luxon': ^3.2.0 '@types/node': ^18.11.10 + '@types/passport-github2': ^1.2.5 '@types/passport-google-oauth20': ^2.0.11 '@types/passport-jwt': ^3.0.8 '@types/supertest': ^2.0.12 @@ -46,6 +47,7 @@ specifiers: jwt: link:@types/nestjs/jwt luxon: ^3.2.1 passport: ^0.6.0 + passport-github2: ^0.1.12 passport-google-oauth20: ^2.0.0 passport-jwt: ^4.0.1 passport-local: ^1.0.0 @@ -87,6 +89,7 @@ dependencies: ioredis: 5.2.4 luxon: 3.2.1 passport: 0.6.0 + passport-github2: 0.1.12 passport-google-oauth20: 2.0.0 passport-jwt: 4.0.1 passport-local: 1.0.0 @@ -108,6 +111,7 @@ devDependencies: '@types/jest': 27.5.2 '@types/luxon': 3.2.0 '@types/node': 18.11.18 + '@types/passport-github2': 1.2.5 '@types/passport-google-oauth20': 2.0.11 '@types/passport-jwt': 3.0.8 '@types/supertest': 2.0.12 @@ -1776,6 +1780,14 @@ packages: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: true + /@types/passport-github2/1.2.5: + resolution: {integrity: sha512-+WLyrd8JPsCxroK34EjegR0j3FMxp6wqB9cw/sRCFkWT9qic1dymAn021gr336EpyjzdhjUd2KKrqyxvdFSvOA==} + dependencies: + '@types/express': 4.17.15 + '@types/passport': 1.0.11 + '@types/passport-oauth2': 1.4.11 + dev: true + /@types/passport-google-oauth20/2.0.11: resolution: {integrity: sha512-9XMT1GfwhZL7UQEiCepLef55RNPHkbrCtsU7rsWPTEOsmu5qVIW8nSemtB4p+P24CuOhA+IKkv8LsPThYghGww==} dependencies: @@ -5074,6 +5086,13 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + /passport-github2/0.1.12: + resolution: {integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==} + engines: {node: '>= 0.8.0'} + dependencies: + passport-oauth2: 1.6.1 + dev: false + /passport-google-oauth20/2.0.0: resolution: {integrity: sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==} engines: {node: '>= 0.4.0'} diff --git a/packages/hoppscotch-backend/src/auth/auth.controller.ts b/packages/hoppscotch-backend/src/auth/auth.controller.ts index f9df68573..0021e701a 100644 --- a/packages/hoppscotch-backend/src/auth/auth.controller.ts +++ b/packages/hoppscotch-backend/src/auth/auth.controller.ts @@ -65,4 +65,16 @@ export class AuthController { if (E.isLeft(authTokens)) throwHTTPErr(authTokens.left); authCookieHandler(res, authTokens.right, true); } + + @Get('github') + @UseGuards(AuthGuard('github')) + async githubAuth(@Request() req) {} + + @Get('github/callback') + @UseGuards(AuthGuard('github')) + async githubAuthRedirect(@Request() req, @Res() res) { + const authTokens = await this.authService.generateAuthTokens(req.user.id); + if (E.isLeft(authTokens)) throwHTTPErr(authTokens.left); + authCookieHandler(res, authTokens.right, true); + } } diff --git a/packages/hoppscotch-backend/src/auth/auth.module.ts b/packages/hoppscotch-backend/src/auth/auth.module.ts index 7bad87726..5be9219f7 100644 --- a/packages/hoppscotch-backend/src/auth/auth.module.ts +++ b/packages/hoppscotch-backend/src/auth/auth.module.ts @@ -9,6 +9,7 @@ import { JwtModule } from '@nestjs/jwt/dist'; import { JwtStrategy } from './strategies/jwt.strategy'; import { RTJwtStrategy } from './strategies/rt-jwt.strategy'; import { GoogleStrategy } from './strategies/google.strategy'; +import { GithubStrategy } from './strategies/github.strategy'; @Module({ imports: [ @@ -20,7 +21,13 @@ import { GoogleStrategy } from './strategies/google.strategy'; secret: process.env.JWT_SECRET, }), ], - providers: [AuthService, JwtStrategy, RTJwtStrategy, GoogleStrategy], + providers: [ + AuthService, + JwtStrategy, + RTJwtStrategy, + GoogleStrategy, + GithubStrategy, + ], controllers: [AuthController], }) export class AuthModule {} diff --git a/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts b/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts new file mode 100644 index 000000000..4ca299cc3 --- /dev/null +++ b/packages/hoppscotch-backend/src/auth/strategies/github.strategy.ts @@ -0,0 +1,49 @@ +import { Strategy } from 'passport-github2'; +import { PassportStrategy } from '@nestjs/passport'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { AuthService } from '../auth.service'; +import { UserService } from 'src/user/user.service'; +import * as O from 'fp-ts/Option'; + +@Injectable() +export class GithubStrategy extends PassportStrategy(Strategy) { + constructor( + private authService: AuthService, + private usersService: UserService, + ) { + super({ + clientID: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET, + callbackURL: process.env.GITHUB_CALLBACK_URL, + }); + } + + async validate(accessToken, refreshToken, profile, done): Promise { + const user = await this.usersService.findUserByEmail( + profile.emails[0].value, + ); + + if (O.isNone(user)) { + const createdUser = await this.usersService.createUserSSO( + accessToken, + refreshToken, + profile, + ); + return createdUser; + } + + // Check to see if entry for github is present in the Account table for this user + const providerAccountExists = + await this.authService.checkIfProviderAccountExists(user.value, profile); + + if (O.isNone(providerAccountExists)) + await this.usersService.createProviderAccount( + user.value, + accessToken, + refreshToken, + profile, + ); + + return user.value; + } +} diff --git a/packages/hoppscotch-backend/src/utils.ts b/packages/hoppscotch-backend/src/utils.ts index 2779b1a56..93ee6d44b 100644 --- a/packages/hoppscotch-backend/src/utils.ts +++ b/packages/hoppscotch-backend/src/utils.ts @@ -157,6 +157,6 @@ export const authCookieHandler = ( sameSite: 'lax', }); if (redirect) { - res.status(HttpStatus.OK).redirect('http://localhost:3000/'); + res.status(HttpStatus.OK).redirect('http://localhost:3170/graphql'); } else res.status(HttpStatus.OK).send(); };