Browse Source

feat: github login added

Balu Babu 2 years ago
parent
commit
311ab67ebe

+ 2 - 0
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",

+ 19 - 0
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'}

+ 12 - 0
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);
+  }
 }

+ 8 - 1
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 {}

+ 49 - 0
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<any> {
+    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;
+  }
+}

+ 1 - 1
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();
 };