Browse Source

Added google signle sign on

master
Quildra 1 year ago
parent
commit
9b4a43df9a
  1. 34
      migrations/20240717204437-add-google-id.js
  2. 3
      models/user.model.ts
  3. 3
      src/app.module.ts
  4. 20
      src/auth/auth.controller.ts
  5. 3
      src/auth/auth.module.ts
  6. 39
      src/auth/google.strategy.ts
  7. 50
      src/google-sheets/google-sheets.service.ts
  8. 8
      src/users/users.service.ts

34
migrations/20240717204437-add-google-id.js

@ -0,0 +1,34 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'Users',
'googleId',
{
type: Sequelize.STRING,
allowNull: true,
},
{ transaction }
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down (queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('Users', 'googleId', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
}
};

3
models/user.model.ts

@ -16,4 +16,7 @@ export class User extends Model<User> {
@Column
password: string;
@Column
googleId?: string;
}

3
src/app.module.ts

@ -6,6 +6,7 @@ import { SequelizeModule } from '@nestjs/sequelize';
import { UsersModule } from './users/users.module';
import { User } from '../models/user.model';
import { AuthModule } from './auth/auth.module';
import { GoogleSheetsService } from './google-sheets/google-sheets.service';
@Module({
imports: [
@ -26,6 +27,6 @@ import { AuthModule } from './auth/auth.module';
AuthModule,
],
controllers: [AppController],
providers: [AppService],
providers: [AppService, GoogleSheetsService],
})
export class AppModule {}

20
src/auth/auth.controller.ts

@ -1,8 +1,9 @@
// auth/auth.controller.ts
import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common';
import { Controller, Post, Body, UseGuards, Request, Get, Req, Res, Redirect } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtAuthGuard } from './jwt-auth.guard';
import { User } from '../../models/user.model';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
@ -27,4 +28,21 @@ export class AuthController {
getProfile(@Request() req) {
return req.user;
}
@Get('google')
@UseGuards(AuthGuard('google'))
async googleAuth(@Req() req) {
// Guard initiates Google OAuth
}
@Get('google/callback')
@UseGuards(AuthGuard('google'))
@Redirect('http://localhost:4200/google-callback', 302)
async googleAuthRedirect(@Req() req, @Res() res: Response) {
const user: User = req.user;
const token = await this.authService.login(user);
const frontendRedirectUrl = `http://localhost:4200/google-callback?token=${token.access_token}`;
console.log(frontendRedirectUrl);
return { url: frontendRedirectUrl };
}
}

3
src/auth/auth.module.ts

@ -6,6 +6,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
import { GoogleStrategy } from './google.strategy';
import { UsersModule } from '../users/users.module';
@Module({
@ -21,7 +22,7 @@ import { UsersModule } from '../users/users.module';
inject: [ConfigService],
}),
],
providers: [AuthService, JwtStrategy],
providers: [AuthService, JwtStrategy, GoogleStrategy],
controllers: [AuthController],
})
export class AuthModule {}

39
src/auth/google.strategy.ts

@ -0,0 +1,39 @@
// auth/google.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { ConfigService } from '@nestjs/config';
import { UsersService } from '../users/users.service';
import { User } from '../../models/user.model';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor(
private readonly configService: ConfigService,
private readonly usersService: UsersService,
) {
super({
clientID: configService.get<string>('GOOGLE_CLIENT_ID'),
clientSecret: configService.get<string>('GOOGLE_CLIENT_SECRET'),
callbackURL: configService.get<string>('GOOGLE_CALLBACK_URL'),
scope: ['email', 'profile'],
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback,
): Promise<any> {
const { id, name, emails } = profile;
const user: Partial<User> = {
googleId: id,
email: emails[0].value,
name: name.givenName + ' ' + name.familyName,
};
const userFromDb = await this.usersService.findOrCreate(user);
done(null, userFromDb);
}
}

50
src/google-sheets/google-sheets.service.ts

@ -0,0 +1,50 @@
// google-sheets/google-sheets.service.ts
import { Injectable } from '@nestjs/common';
import { google, sheets_v4 } from 'googleapis';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class GoogleSheetsService {
private sheets: sheets_v4.Sheets;
constructor(private readonly configService: ConfigService) {
const auth = new google.auth.OAuth2(
this.configService.get<string>('GOOGLE_CLIENT_ID'),
this.configService.get<string>('GOOGLE_CLIENT_SECRET'),
this.configService.get<string>('GOOGLE_CALLBACK_URL'),
);
this.sheets = google.sheets({ version: 'v4', auth });
}
async getSheetData(spreadsheetId: string, range: string) {
const response = await this.sheets.spreadsheets.values.get({
spreadsheetId,
range,
});
return response.data.values;
}
async updateSheetData(spreadsheetId: string, range: string, values: any[]) {
const response = await this.sheets.spreadsheets.values.update({
spreadsheetId,
range,
valueInputOption: 'RAW',
requestBody: {
values,
},
});
return response.data;
}
async createSheet(title: string): Promise<string> {
const request = {
requestBody: {
properties: {
title,
},
},
};
const response = await this.sheets.spreadsheets.create(request);
return response.data.spreadsheetId;
}
}

8
src/users/users.service.ts

@ -30,6 +30,14 @@ export class UsersService {
});
}
async findOrCreate(user: Partial<User>): Promise<User> {
const existingUser = await this.findByEmail(user.email);
if (existingUser) {
return existingUser;
}
return this.create(user as User);
}
create(user: User): Promise<User> {
return this.userModel.create(user);
}

Loading…
Cancel
Save