Add google signin/signup
This commit is contained in:
147
back/package-lock.json
generated
147
back/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
12
back/prisma/migrations/20230621090510_google/migration.sql
Normal file
12
back/prisma/migrations/20230621090510_google/migration.sql
Normal 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");
|
||||
@@ -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[]
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
34
back/src/auth/google.strategy.ts
Normal file
34
back/src/auth/google.strategy.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user