Add google signin/signup

This commit is contained in:
2023-06-21 19:10:18 +09:00
committed by GitBluub
parent 857158c6cf
commit 04d288b844
11 changed files with 229 additions and 5 deletions

147
back/package-lock.json generated
View File

@@ -24,6 +24,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
@@ -38,6 +39,7 @@
"@types/express": "^4.17.13",
"@types/jest": "27.4.1",
"@types/node": "^16.0.0",
"@types/passport-google-oauth20": "^2.0.11",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
@@ -1962,6 +1964,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.33.tgz",
"integrity": "sha512-0PJ0vg+JyU0MIan58IOIFRtSvsb7Ri+7Wltx2qAg94eMOrpg4+uuP3aUHCpxXc1i0jCXiC+zIamSZh3l9AbcQA=="
},
"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",
@@ -1976,6 +1987,28 @@
"@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-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/prettier": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.0.tgz",
@@ -2763,6 +2796,14 @@
}
]
},
"node_modules/base64url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
@@ -6870,6 +6911,11 @@
"integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
"dev": true
},
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -7113,6 +7159,17 @@
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-google-oauth20": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
"integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==",
"dependencies": {
"passport-oauth2": "1.x.x"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/passport-jwt": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
@@ -7133,6 +7190,25 @@
"node": ">= 0.4.0"
}
},
"node_modules/passport-oauth2": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
"integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==",
"dependencies": {
"base64url": "3.x.x",
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x",
"utils-merge": "1.x.x"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
@@ -8775,6 +8851,11 @@
"node": ">=4.2.0"
}
},
"node_modules/uid2": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
"integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
},
"node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -10691,6 +10772,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.33.tgz",
"integrity": "sha512-0PJ0vg+JyU0MIan58IOIFRtSvsb7Ri+7Wltx2qAg94eMOrpg4+uuP3aUHCpxXc1i0jCXiC+zIamSZh3l9AbcQA=="
},
"@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",
@@ -10705,6 +10795,28 @@
"@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-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/prettier": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.0.tgz",
@@ -11301,6 +11413,11 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true
},
"base64url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="
},
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
@@ -14452,6 +14569,11 @@
"integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
"dev": true
},
"oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -14624,6 +14746,14 @@
"utils-merge": "^1.0.1"
}
},
"passport-google-oauth20": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
"integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==",
"requires": {
"passport-oauth2": "1.x.x"
}
},
"passport-jwt": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
@@ -14641,6 +14771,18 @@
"passport-strategy": "1.x.x"
}
},
"passport-oauth2": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
"integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==",
"requires": {
"base64url": "3.x.x",
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x",
"utils-merge": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
@@ -15831,6 +15973,11 @@
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
"dev": true
},
"uid2": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
"integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
},
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",

View File

@@ -36,6 +36,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
@@ -50,6 +51,7 @@
"@types/express": "^4.17.13",
"@types/jest": "27.4.1",
"@types/node": "^16.0.0",
"@types/passport-google-oauth20": "^2.0.11",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",

View File

@@ -0,0 +1,12 @@
/*
Warnings:
- A unique constraint covering the columns `[googleID]` on the table `User` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "User" ADD COLUMN "googleID" TEXT,
ALTER COLUMN "password" DROP NOT NULL;
-- CreateIndex
CREATE UNIQUE INDEX "User_googleID_key" ON "User"("googleID");

View File

@@ -12,8 +12,9 @@ datasource db {
model User {
id Int @id @default(autoincrement())
username String @unique
password String
password String?
email String
googleID String? @unique
isGuest Boolean @default(false)
partyPlayed Int @default(0)
LessonHistory LessonHistory[]

View File

@@ -12,6 +12,7 @@ import {
InternalServerErrorException,
Patch,
NotFoundException,
Req,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtAuthGuard } from './jwt-auth.guard';
@@ -32,6 +33,7 @@ import { Profile } from './dto/profile.dto';
import { Setting } from 'src/models/setting';
import { UpdateSettingDto } from 'src/settings/dto/update-setting.dto';
import { SettingsService } from 'src/settings/settings.service';
import { AuthGuard } from '@nestjs/passport';
@ApiTags('auth')
@Controller('auth')
@@ -42,6 +44,21 @@ export class AuthController {
private settingsService: SettingsService,
) {}
@Get("login/google")
@UseGuards(AuthGuard('google'))
googleLogin() { }
@Get("logged/google")
@UseGuards(AuthGuard('google'))
async googleLoginCallbakc(@Req() req: any) {
let user = await this.usersService.user({googleID: req.id});
if (!user) {
user = await this.usersService.createUser(req)
await this.settingsService.createUserSetting(user.id);
}
return this.authService.login(user);
}
@Post('register')
async register(@Body() registerDto: RegisterDto): Promise<void> {
try {

View File

@@ -9,6 +9,7 @@ import { ConfigModule } from '@nestjs/config';
import { ConfigService } from '@nestjs/config';
import { JwtStrategy } from './jwt.strategy';
import { SettingsModule } from 'src/settings/settings.module';
import { GoogleStrategy } from './google.strategy';
@Module({
imports: [
@@ -25,7 +26,7 @@ import { SettingsModule } from 'src/settings/settings.module';
inject: [ConfigService],
}),
],
providers: [AuthService, LocalStrategy, JwtStrategy],
providers: [AuthService, LocalStrategy, JwtStrategy, GoogleStrategy],
controllers: [AuthController],
})
export class AuthModule {}

View File

@@ -15,7 +15,7 @@ export class AuthService {
password: string,
): Promise<PayloadInterface | null> {
const user = await this.userService.user({ username });
if (user && bcrypt.compareSync(password, user.password)) {
if (user && user.password && bcrypt.compareSync(password, user.password)) {
return {
username: user.username,
id: user.id,

View File

@@ -0,0 +1,34 @@
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable } from '@nestjs/common';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_SECRET,
callbackURL: 'http://localhost:3000/google/redirect',
scope: ['email', 'profile'],
});
}
async validate(
_accessToken: string,
_refreshToken: string,
profile: any,
done: VerifyCallback,
): Promise<any> {
console.log(profile);
const { name, emails, photos, username } = profile;
const user = {
email: emails[0].value,
username,
password: null,
// firstName: name.givenName,
// lastName: name.familyName,
// picture: photos[0].value,
};
done(null, user);
}
}

View File

@@ -34,7 +34,8 @@ export class UsersService {
}
async createUser(data: Prisma.UserCreateInput): Promise<User> {
data.password = await bcrypt.hash(data.password, 8);
if (data.password)
data.password = await bcrypt.hash(data.password, 8);
return this.prisma.user.create({
data,
});

View File

@@ -7,6 +7,7 @@ export const en = {
signOutBtn: 'Sign out',
signInBtn: 'Sign in',
signUpBtn: 'Sign up',
continuewithgoogle: 'Continue with Google',
changeLanguageBtn: 'Change language',
search: 'Search',
login: 'Login',
@@ -189,6 +190,7 @@ export const fr: typeof en = {
welcomeMessage: 'Re-Bonjour ',
signOutBtn: 'Se déconnecter',
signInBtn: 'Se connecter',
continuewithgoogle: 'Continuer avec Google',
changeLanguageBtn: 'Changer la langue',
searchBtn: 'Rechercher',
playBtn: 'Jouer',
@@ -362,7 +364,7 @@ export const fr: typeof en = {
noRecentSearches: 'Aucune recherche récente',
};
export const sp: typeof en = {
export const sp: Partial<typeof en> = {
error: 'Error',
anErrorOccured: 'ocurrió un error',
goBackHome: 'regresar a casa',

View File

@@ -78,6 +78,13 @@ const AuthenticationView = ({ isSignup }: RouteProps<AuthenticationViewProps>) =
{translate('forgottenPassword')}
</Button>
)}
<TextButton
translate={{ translationKey: "continuewithgoogle" }}
variant="outline"
marginTop={5}
colorScheme="primary"
onPress={() => window.location.href = "/api/login/google"}
/>
<TextButton
translate={{ translationKey: mode === 'signin' ? 'signUpBtn' : 'signInBtn' }}
variant="outline"