Browse Source

feat: add google strategy - WIP

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5444/head
Pranav C 1 year ago
parent
commit
3259db4e28
  1. 81
      packages/nocodb-nest/package-lock.json
  2. 5
      packages/nocodb-nest/package.json
  3. 1
      packages/nocodb-nest/src/app.module.ts
  4. 42
      packages/nocodb-nest/src/controllers/users/users.controller.ts
  5. 1
      packages/nocodb-nest/src/meta/meta.service.ts
  6. 6
      packages/nocodb-nest/src/modules/users/users.module.ts
  7. 104
      packages/nocodb-nest/src/strategies/google.strategy/google.strategy.ts

81
packages/nocodb-nest/package-lock.json generated

@ -20,7 +20,6 @@
"@nestjs/serve-static": "^3.0.1",
"@sentry/node": "^6.3.5",
"@types/chai": "^4.2.12",
"@types/mocha": "^8.0.1",
"airtable": "^0.11.3",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
@ -116,9 +115,11 @@
"@nestjs/testing": "^9.0.0",
"@nestjsplus/dyn-schematics": "^1.0.12",
"@types/express": "^4.17.13",
"@types/jest": "29.5.0",
"@types/jest": "^29.5.0",
"@types/mocha": "^10.0.1",
"@types/multer": "^1.4.7",
"@types/node": "18.15.11",
"@types/passport-google-oauth20": "^2.0.11",
"@types/passport-jwt": "^3.0.8",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
@ -3350,9 +3351,10 @@
"dev": true
},
"node_modules/@types/mocha": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz",
"integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw=="
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
"integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==",
"dev": true
},
"node_modules/@types/multer": {
"version": "1.4.7",
@ -3390,6 +3392,15 @@
"node": ">= 6"
}
},
"node_modules/@types/oauth": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz",
"integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -3405,6 +3416,17 @@
"@types/express": "*"
}
},
"node_modules/@types/passport-google-oauth20": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.11.tgz",
"integrity": "sha512-9XMT1GfwhZL7UQEiCepLef55RNPHkbrCtsU7rsWPTEOsmu5qVIW8nSemtB4p+P24CuOhA+IKkv8LsPThYghGww==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/passport": "*",
"@types/passport-oauth2": "*"
}
},
"node_modules/@types/passport-jwt": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz",
@ -3416,6 +3438,17 @@
"@types/passport-strategy": "*"
}
},
"node_modules/@types/passport-oauth2": {
"version": "1.4.12",
"resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.12.tgz",
"integrity": "sha512-RZg6cYTyEGinrZn/7REYQds6zrTxoBorX1/fdaz5UHzkG8xdFE7QQxkJagCr2ETzGII58FAFDmnmbTUVMrltNA==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/oauth": "*",
"@types/passport": "*"
}
},
"node_modules/@types/passport-strategy": {
"version": "0.2.35",
"resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
@ -20752,9 +20785,10 @@
"dev": true
},
"@types/mocha": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz",
"integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw=="
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
"integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==",
"dev": true
},
"@types/multer": {
"version": "1.4.7",
@ -20791,6 +20825,15 @@
}
}
},
"@types/oauth": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz",
"integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -20806,6 +20849,17 @@
"@types/express": "*"
}
},
"@types/passport-google-oauth20": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.11.tgz",
"integrity": "sha512-9XMT1GfwhZL7UQEiCepLef55RNPHkbrCtsU7rsWPTEOsmu5qVIW8nSemtB4p+P24CuOhA+IKkv8LsPThYghGww==",
"dev": true,
"requires": {
"@types/express": "*",
"@types/passport": "*",
"@types/passport-oauth2": "*"
}
},
"@types/passport-jwt": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz",
@ -20817,6 +20871,17 @@
"@types/passport-strategy": "*"
}
},
"@types/passport-oauth2": {
"version": "1.4.12",
"resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.12.tgz",
"integrity": "sha512-RZg6cYTyEGinrZn/7REYQds6zrTxoBorX1/fdaz5UHzkG8xdFE7QQxkJagCr2ETzGII58FAFDmnmbTUVMrltNA==",
"dev": true,
"requires": {
"@types/express": "*",
"@types/oauth": "*",
"@types/passport": "*"
}
},
"@types/passport-strategy": {
"version": "0.2.35",
"resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",

5
packages/nocodb-nest/package.json

@ -50,7 +50,6 @@
"@nestjs/serve-static": "^3.0.1",
"@sentry/node": "^6.3.5",
"@types/chai": "^4.2.12",
"@types/mocha": "^8.0.1",
"airtable": "^0.11.3",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
@ -146,9 +145,11 @@
"@nestjs/testing": "^9.0.0",
"@nestjsplus/dyn-schematics": "^1.0.12",
"@types/express": "^4.17.13",
"@types/jest": "29.5.0",
"@types/jest": "^29.5.0",
"@types/mocha": "^10.0.1",
"@types/multer": "^1.4.7",
"@types/node": "18.15.11",
"@types/passport-google-oauth20": "^2.0.11",
"@types/passport-jwt": "^3.0.8",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",

1
packages/nocodb-nest/src/app.module.ts

@ -46,7 +46,6 @@ import type {
LocalStrategy,
AuthTokenStrategy,
BaseViewStrategy,
GoogleStrategy,
],
})
export class AppModule implements OnApplicationBootstrap {

42
packages/nocodb-nest/src/controllers/users/users.controller.ts

@ -20,6 +20,7 @@ import {
ExtractProjectIdMiddleware,
} from '../../middlewares/extract-project-id/extract-project-id.middleware';
import Noco from '../../Noco';
import { GoogleStrategy } from '../../strategies/google.strategy/google.strategy';
import extractRolesObj from '../../utils/extractRolesObj';
import { Audit, User } from '../../models';
import {
@ -31,7 +32,10 @@ import { UsersService } from '../../services/users/users.service';
@Controller()
export class UsersController {
constructor(private readonly usersService: UsersService) {}
constructor(
private readonly usersService: UsersService,
private googleStrategy: GoogleStrategy,
) {}
@Post([
'/auth/user/signup',
@ -131,33 +135,21 @@ export class UsersController {
@Post(`/auth/google/genTokenByCode`)
@HttpCode(200)
async googleSignin(req, res, next) {
// todo
/* passport.authenticate(
'google',
{
session: false,
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath,
},
async (err, user, info): Promise<any> =>
await successfulSignIn({
user,
err,
info,
req,
res,
auditDescription: 'signed in using Google Auth',
})
)(req, res, next);*/
@UseGuards(AuthGuard('google'))
async googleSignin(@Request() req) {
return this.usersService.login(req.user);
}
@Get('/auth/google')
googleAuthenticate() {
/* passport.authenticate('google', {
scope: ['profile', 'email'],
state: req.query.state,
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath,
})(req, res, next)*/
@UseGuards(
AuthGuard('google'),
)
googleAuthenticate(@Request() req) {
// this.googleStrategy.authenticate(req, {
// scope: ['profile', 'email'],
// state: req.query.state,
// callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath,
// });
}
@Get(['/auth/user/me', '/api/v1/db/auth/user/me', '/api/v1/auth/user/me'])

1
packages/nocodb-nest/src/meta/meta.service.ts

@ -1043,7 +1043,6 @@ export class MetaService {
}
public async init(): Promise<boolean> {
NocoCache.init();
await this.connection.migrate.latest({
migrationSource: new XcMigrationSource(),
tableName: 'xc_knex_migrations',

6
packages/nocodb-nest/src/modules/users/users.module.ts

@ -1,12 +1,14 @@
import { Module } from '@nestjs/common';
import { GoogleStrategy, GoogleStrategyProvider } from '../../strategies/google.strategy/google.strategy'
import { GlobalModule } from '../global/global.module';
import { UsersService } from '../../services/users/users.service';
import { UsersController } from '../../controllers/users/users.controller';
import { PassportModule } from '@nestjs/passport';
@Module({
imports: [GlobalModule],
imports: [GlobalModule, PassportModule],
controllers: [UsersController],
providers: [UsersService],
providers: [UsersService, GoogleStrategyProvider],
exports: [UsersService],
})
export class UsersModule {}

104
packages/nocodb-nest/src/strategies/google.strategy/google.strategy.ts

@ -1,4 +1,104 @@
import { Injectable } from '@nestjs/common';
import { promisify } from 'util';
import { Injectable, Optional } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-google-oauth20';
import bcrypt from 'bcryptjs';
import { Plugin, ProjectUser, User } from '../../models';
import { UsersService } from '../../services/users/users.service';
import type { VerifyCallback } from 'passport-google-oauth20';
import type { FactoryProvider } from '@nestjs/common/interfaces/modules/provider.interface';
@Injectable()
export class GoogleStrategy {}
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor(
@Optional() clientConfig: any,
private usersService: UsersService,
) {
super(clientConfig);
}
async validate(
req: any,
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback,
): Promise<any> {
// mostly copied from older code
const email = profile.emails[0].value;
try {
const user = await User.getByEmail(email);
if (user) {
// if project id defined extract project level roles
if (req.ncProjectId) {
ProjectUser.get(req.ncProjectId, user.id)
.then(async (projectUser) => {
user.roles = projectUser?.roles || user.roles;
user.roles =
user.roles === 'owner' ? 'owner,creator' : user.roles;
// + (user.roles ? `,${user.roles}` : '');
done(null, user);
})
.catch((e) => done(e));
} else {
return done(null, user);
}
// if user not found create new user if allowed
// or return error
} else {
const salt = await promisify(bcrypt.genSalt)(10);
const user = await this.usersService.registerNewUserIfAllowed({
firstname: null,
lastname: null,
email_verification_token: null,
email: profile.emails[0].value,
password: '',
salt,
});
return done(null, user);
}
} catch (err) {
return done(err);
}
}
authorizationParams(options: any) {
const params = super.authorizationParams(options) as Record<string, any>;
if (options.state) {
params.state = options.state;
}
return params;
}
}
export const GoogleStrategyProvider: FactoryProvider = {
provide: GoogleStrategy,
inject: [UsersService],
useFactory: async (usersService: UsersService) => {
// const googlePlugin = await Plugin.getPluginByTitle('Google');
//
// if (googlePlugin && googlePlugin.input) {
// const settings = JSON.parse(googlePlugin.input);
// process.env.NC_GOOGLE_CLIENT_ID = settings.client_id;
// process.env.NC_GOOGLE_CLIENT_SECRET = settings.client_secret;
// }
if (
!process.env.NC_GOOGLE_CLIENT_ID ||
!process.env.NC_GOOGLE_CLIENT_SECRET
)
return null;
const clientConfig = {
clientID: process.env.NC_GOOGLE_CLIENT_ID,
clientSecret: process.env.NC_GOOGLE_CLIENT_SECRET,
// todo: update url
callbackURL: 'http://localhost:3000',
passReqToCallback: true,
};
return new GoogleStrategy(clientConfig, usersService);
},
};

Loading…
Cancel
Save