From a96691dc453f8974330d8c40e56892cf248bf3bd Mon Sep 17 00:00:00 2001 From: Quildra Date: Thu, 18 Jul 2024 14:27:18 +0100 Subject: [PATCH] Improved google claims for sheets interactions --- .../20240718092718-add-google-tokens.js | 34 +++++++++++++++++++ models/user.model.ts | 3 ++ src/auth/auth.controller.ts | 21 +++++++++++- src/auth/auth.module.ts | 3 +- src/auth/auth.service.ts | 9 +++++ src/auth/google.strategy.ts | 7 ++-- src/google-sheets/google-sheets.service.ts | 29 ++++++++++++---- 7 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 migrations/20240718092718-add-google-tokens.js diff --git a/migrations/20240718092718-add-google-tokens.js b/migrations/20240718092718-add-google-tokens.js new file mode 100644 index 0000000..8d51003 --- /dev/null +++ b/migrations/20240718092718-add-google-tokens.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', + 'googleTokens', + { + 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', 'googleTokens', { transaction }); + await transaction.commit(); + } catch (err) { + await transaction.rollback(); + throw err; + } + } +}; diff --git a/models/user.model.ts b/models/user.model.ts index f1587d1..f1288af 100644 --- a/models/user.model.ts +++ b/models/user.model.ts @@ -19,4 +19,7 @@ export class User extends Model { @Column googleId?: string; + + @Column + googleTokens?: string; } diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index b1f9533..3b9beac 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -4,10 +4,14 @@ import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; import { User } from '../../models/user.model'; import { AuthGuard } from '@nestjs/passport'; +import { GoogleSheetsService } from '../google-sheets/google-sheets.service'; @Controller('auth') export class AuthController { - constructor(private readonly authService: AuthService) {} + constructor( + private readonly authService: AuthService, + private readonly googleSheetsService: GoogleSheetsService + ) {} @Post('login') async login(@Body() body) { @@ -45,4 +49,19 @@ export class AuthController { console.log(frontendRedirectUrl); return { url: frontendRedirectUrl }; } + + @Post('create-sheet') + @UseGuards(AuthGuard('jwt')) + async createSheet(@Req() req) { + const user: User = req.user; + const title = "Jedi-Archive"; + const tokens = await this.authService.getGoogleTokens(user); + + console.log(user); + console.log(tokens); + + this.googleSheetsService.setCredentials(tokens); + const sheetId = await this.googleSheetsService.createSheet(title); + return { sheetId }; + } } diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 4d2a502..e2dd60f 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -8,6 +8,7 @@ import { AuthController } from './auth.controller'; import { JwtStrategy } from './jwt.strategy'; import { GoogleStrategy } from './google.strategy'; import { UsersModule } from '../users/users.module'; +import { GoogleSheetsService } from '../google-sheets/google-sheets.service'; @Module({ imports: [ @@ -22,7 +23,7 @@ import { UsersModule } from '../users/users.module'; inject: [ConfigService], }), ], - providers: [AuthService, JwtStrategy, GoogleStrategy], + providers: [AuthService, JwtStrategy, GoogleStrategy, GoogleSheetsService], controllers: [AuthController], }) export class AuthModule {} diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 3576ef6..389e226 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -32,4 +32,13 @@ export class AuthService { user.password = bcrypt.hashSync(user.password, 10); return this.usersService.create(user); } + + async saveGoogleTokens(user: User, tokens: any) { + user.googleTokens = JSON.stringify(tokens); // Save tokens as JSON string + await user.save(); + } + + async getGoogleTokens(user: User): Promise { + return JSON.parse(user.googleTokens || '{}'); + } } diff --git a/src/auth/google.strategy.ts b/src/auth/google.strategy.ts index 4ea3249..4d75077 100644 --- a/src/auth/google.strategy.ts +++ b/src/auth/google.strategy.ts @@ -5,18 +5,20 @@ import { Strategy, VerifyCallback } from 'passport-google-oauth20'; import { ConfigService } from '@nestjs/config'; import { UsersService } from '../users/users.service'; import { User } from '../../models/user.model'; +import { AuthService } from './auth.service'; @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor( private readonly configService: ConfigService, private readonly usersService: UsersService, + private readonly authService: AuthService, ) { super({ clientID: configService.get('GOOGLE_CLIENT_ID'), clientSecret: configService.get('GOOGLE_CLIENT_SECRET'), callbackURL: configService.get('GOOGLE_CALLBACK_URL'), - scope: ['email', 'profile'], + scope: ['email', 'profile', 'https://www.googleapis.com/auth/drive.file'], }); } @@ -33,7 +35,8 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { name: name.givenName + ' ' + name.familyName, }; - const userFromDb = await this.usersService.findOrCreate(user); + let userFromDb = await this.usersService.findOrCreate(user); + await this.authService.saveGoogleTokens(userFromDb, { accessToken, refreshToken }); done(null, userFromDb); } } diff --git a/src/google-sheets/google-sheets.service.ts b/src/google-sheets/google-sheets.service.ts index a2bac5a..55d348f 100644 --- a/src/google-sheets/google-sheets.service.ts +++ b/src/google-sheets/google-sheets.service.ts @@ -5,19 +5,34 @@ import { ConfigService } from '@nestjs/config'; @Injectable() export class GoogleSheetsService { - private sheets: sheets_v4.Sheets; + private oauth2Client; constructor(private readonly configService: ConfigService) { - const auth = new google.auth.OAuth2( + this.oauth2Client = new google.auth.OAuth2( this.configService.get('GOOGLE_CLIENT_ID'), this.configService.get('GOOGLE_CLIENT_SECRET'), this.configService.get('GOOGLE_CALLBACK_URL'), ); - this.sheets = google.sheets({ version: 'v4', auth }); + } + + setCredentials(tokens: any) { + //this.oauth2Client.setCredentials(tokens); + this.oauth2Client.setCredentials( + { + access_token: tokens.accessToken + } + ); + this.oauth2Client.on('tokens', (tokens) => { + if (tokens.refresh_token) { + // Save the new refresh token to the user's profile + console.log("thing") + } + }); } async getSheetData(spreadsheetId: string, range: string) { - const response = await this.sheets.spreadsheets.values.get({ + const sheets = google.sheets({ version: 'v4', auth: this.oauth2Client }); + const response = await sheets.spreadsheets.values.get({ spreadsheetId, range, }); @@ -25,7 +40,8 @@ export class GoogleSheetsService { } async updateSheetData(spreadsheetId: string, range: string, values: any[]) { - const response = await this.sheets.spreadsheets.values.update({ + const sheets = google.sheets({ version: 'v4', auth: this.oauth2Client }); + const response = await sheets.spreadsheets.values.update({ spreadsheetId, range, valueInputOption: 'RAW', @@ -37,6 +53,7 @@ export class GoogleSheetsService { } async createSheet(title: string): Promise { + const sheets = google.sheets({ version: 'v4', auth: this.oauth2Client }); const request = { requestBody: { properties: { @@ -44,7 +61,7 @@ export class GoogleSheetsService { }, }, }; - const response = await this.sheets.spreadsheets.create(request); + const response = await sheets.spreadsheets.create(request); return response.data.spreadsheetId; } }