double auth maintenant fonctionne correctement. Il y a un PoC avec svelte

This commit is contained in:
batche
2022-11-15 17:57:21 +01:00
parent ee8033e6d8
commit fd61ea56d2
8 changed files with 153 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
import { Controller, Get, Res, UseGuards, Req, Post, UnauthorizedException, Body, Options } from '@nestjs/common'; import { Controller, Get, Res, UseGuards, Req, Post, UnauthorizedException, Body, Options, Next } from '@nestjs/common';
import { AuthenticateGuard, FortyTwoAuthGuard } from './guards/42guards'; import { AuthenticateGuard, FortyTwoAuthGuard, TwoFactorGuard } from './guards/42guards';
import { AuthenticationService } from './authentication.service'; import { AuthenticationService } from './authentication.service';
import { Response } from 'express'; import { Response } from 'express';
import { TwoFaDto } from './dto/2fa.dto'; import { TwoFaDto } from './dto/2fa.dto';
@@ -31,19 +31,26 @@ export class AuthenticationController {
@Get('redirect') @Get('redirect')
@UseGuards(FortyTwoAuthGuard) @UseGuards(FortyTwoAuthGuard)
async redirect(@Res() response : Response, @Req() request) { async redirect(@Res() response : Response, @Req() request) {
console.log('ON EST DANS REDIRECT AUTH CONTROLLER' + request.user); console.log('ON EST DANS REDIRECT AUTH CONTROLLER');
console.log('On redirige'); console.log('On redirige');
return response.status(200).redirect('http://transcendance:8080'); if (request.user.isEnabledTwoFactorAuth === false)
return response.status(200).redirect('http://transcendance:8080');
return response.status(200).redirect('http://transcendance:8080/#/2fa');
} }
/** /**
* GET /api/v2/auth/logout * GET /api/v2/auth/logout
* Route pour déconnecter l'utilisateur * Route pour déconnecter l'utilisateur
*/ */
@Get('logout') @Post('logout')
logout(@Req() request) { @UseGuards(AuthenticateGuard)
console.log('ON EST DANS LOGOUT AUTH CONTROLLER') logout(@Req() request, @Res() response, @Next() next) {
request.session.destroy(); this.userService.setIsTwoFactorAuthenticatedWhenLogout(request.user.id);
request.logout(function(err) {
if (err) { return next(err); }
response.redirect('/');
});
request.session.cookie.maxAge = 0;
return {msg : 'You are now logged out'}; return {msg : 'You are now logged out'};
} }
@@ -57,7 +64,7 @@ export class AuthenticationController {
@Post('2fa/turn-on') @Post('2fa/turn-on')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
async verify(@Req() request, @Body() {twoFaCode} : TwoFaDto){ async verify(@Req() request, @Body() {twoFaCode} : TwoFaDto, @Res() response){
console.log('ON EST DANS VERIFY POUR 2FA AUTH CONTROLLER') console.log('ON EST DANS VERIFY POUR 2FA AUTH CONTROLLER')
const isCodeIsValid = await this.authService.verify2FaCode(request.user, twoFaCode); const isCodeIsValid = await this.authService.verify2FaCode(request.user, twoFaCode);
if (isCodeIsValid === false) if (isCodeIsValid === false)
@@ -65,5 +72,7 @@ export class AuthenticationController {
throw new UnauthorizedException('Wrong Code.'); throw new UnauthorizedException('Wrong Code.');
} }
await this.userService.enableTwoFactorAuth(request.user.id); await this.userService.enableTwoFactorAuth(request.user.id);
console.log('ON REDIRIGE');
return response.status(200);
} }
} }

View File

@@ -15,8 +15,18 @@ export class FortyTwoAuthGuard extends AuthGuard('42') {
export class AuthenticateGuard implements CanActivate { export class AuthenticateGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
console.log("Is User authenticated : " + request.isAuthenticated()); console.log("AuthenticateGuard : Is User authenticated : " + request.isAuthenticated());
return request.isAuthenticated(); return request.isAuthenticated();
} }
} }
@Injectable()
export class TwoFactorGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
console.log("TwoFactorGuard : Is User authenticated : " + request.isAuthenticated() + " and has 2FA enabled : " + request.user.isEnabledTwoFactorAuth + " and has 2FA verified : " + request.user.isTwoFactorAuthenticated);
return request.isAuthenticated() && (request.user.isEnabledTwoFactorAuth === false
|| request.user.isTwoFactorAuthenticated === true);
}
}

View File

@@ -1,5 +1,5 @@
import { Body, Controller, Delete, Get, HttpCode, HttpException, HttpStatus, Param, Patch, Post, Query, Req, UseGuards } from '@nestjs/common'; import { Body, Controller, Delete, Get, HttpCode, HttpException, HttpStatus, Param, Patch, Post, Query, Req, UseGuards } from '@nestjs/common';
import { AuthenticateGuard } from 'src/auth/42/guards/42guards'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards';
import { User } from 'src/users/entities/user.entity'; import { User } from 'src/users/entities/user.entity';
import { CreateFriendshipDto } from './dto/create-friendship.dto'; import { CreateFriendshipDto } from './dto/create-friendship.dto';
import { FriendshipService } from './friendship.service'; import { FriendshipService } from './friendship.service';
@@ -11,6 +11,7 @@ export class FriendshipController {
// GET http://127.0.0.1:3000/api/v2/network/myfriends // GET http://127.0.0.1:3000/api/v2/network/myfriends
@Get('myfriends') @Get('myfriends')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
findEmpty(@Req() req) { findEmpty(@Req() req) {
const user = req.user; const user = req.user;
return this.friendshipService.findAllFriends(user.id); return this.friendshipService.findAllFriends(user.id);
@@ -19,6 +20,7 @@ export class FriendshipController {
// GET http://127.0.0.1:3000/api/v2/network/myfriends/relationshipId // GET http://127.0.0.1:3000/api/v2/network/myfriends/relationshipId
@Get('myfriends/:relationshipId') @Get('myfriends/:relationshipId')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
findOneFriend(@Param('relationshipId') relationshipId: string, @Req() req) { findOneFriend(@Param('relationshipId') relationshipId: string, @Req() req) {
const user = req.user; const user = req.user;
return this.friendshipService.findOneFriend(relationshipId, user.id); return this.friendshipService.findOneFriend(relationshipId, user.id);
@@ -28,6 +30,7 @@ export class FriendshipController {
@Post('myfriends') @Post('myfriends')
@HttpCode(HttpStatus.CREATED) @HttpCode(HttpStatus.CREATED)
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
create(@Body() createFriendshipDto: CreateFriendshipDto, @Req() req) { create(@Body() createFriendshipDto: CreateFriendshipDto, @Req() req) {
const user = req.user; const user = req.user;
console.log(`User id: ${user.id}\n Friend id: ${createFriendshipDto.requesterId}`); console.log(`User id: ${user.id}\n Friend id: ${createFriendshipDto.requesterId}`);
@@ -39,6 +42,7 @@ export class FriendshipController {
// PATCH http://127.0.0.1:3000/api/v2/network/myfriends/relationshipId?status=A // PATCH http://127.0.0.1:3000/api/v2/network/myfriends/relationshipId?status=A
@Patch('myfriends/:relationshipId') @Patch('myfriends/:relationshipId')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
update(@Param('relationshipId') relationshipId: string, @Query('status') status : string, @Req() req) update(@Param('relationshipId') relationshipId: string, @Query('status') status : string, @Req() req)
{ {
const user : User = req.user; const user : User = req.user;
@@ -48,6 +52,7 @@ export class FriendshipController {
// DELETE http://127.0.0.1:3000/api/v2/network/myfriends/relationshipId // DELETE http://127.0.0.1:3000/api/v2/network/myfriends/relationshipId
@Delete('myfriends/:relationshipId') @Delete('myfriends/:relationshipId')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
remove(@Param('relationshipId') relationshipId: string) { remove(@Param('relationshipId') relationshipId: string) {
return this.friendshipService.removeFriendship(relationshipId); return this.friendshipService.removeFriendship(relationshipId);
} }
@@ -56,6 +61,7 @@ export class FriendshipController {
// GET http://127.0.0.1:3000/api/v2/network/blocked // GET http://127.0.0.1:3000/api/v2/network/blocked
@Get('blocked') @Get('blocked')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
findAllBlocked(@Req() req) { findAllBlocked(@Req() req) {
const user = req.user; const user = req.user;
return this.friendshipService.findAllBlockedFriends(user.id); return this.friendshipService.findAllBlockedFriends(user.id);
@@ -64,6 +70,7 @@ export class FriendshipController {
// GET http://127.0.0.1:3000/api/v2/network/pending // GET http://127.0.0.1:3000/api/v2/network/pending
@Get('pending') @Get('pending')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
findAllPendantFriendshipRequested(@Req() req) { findAllPendantFriendshipRequested(@Req() req) {
const user = req.user; const user = req.user;
return this.friendshipService.findAllPendantRequestsForFriendship(user.id); return this.friendshipService.findAllPendantRequestsForFriendship(user.id);
@@ -72,6 +79,7 @@ export class FriendshipController {
// GET http://127.0.0.1:3000/api/v2/network/received // GET http://127.0.0.1:3000/api/v2/network/received
@Get('received') @Get('received')
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
findAllPendantFriendshipReceived(@Req() req) { findAllPendantFriendshipReceived(@Req() req) {
const user = req.user; const user = req.user;
return this.friendshipService.findAllReceivedRequestsForFriendship(user.id); return this.friendshipService.findAllReceivedRequestsForFriendship(user.id);

View File

@@ -36,6 +36,9 @@ export class User {
@Column({ default: false, nullable: true }) @Column({ default: false, nullable: true })
isEnabledTwoFactorAuth: boolean; isEnabledTwoFactorAuth: boolean;
@Column({ default:false, nullable: true })
isTwoFactorAuthenticated: boolean;
@Column({ nullable: true }) @Column({ nullable: true })
secretTwoFactorAuth: string; secretTwoFactorAuth: string;

View File

@@ -2,7 +2,7 @@ import {
Body, Controller, Delete, Get, HttpCode, Body, Controller, Delete, Get, HttpCode,
HttpStatus, Param, Patch, Post, Query, Req, UseGuards HttpStatus, Param, Patch, Post, Query, Req, UseGuards
} from '@nestjs/common'; } from '@nestjs/common';
import { AuthenticateGuard } from 'src/auth/42/guards/42guards'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto'; import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { ValidationPipe } from 'src/common/validation/validation.pipe'; import { ValidationPipe } from 'src/common/validation/validation.pipe';
import { CreateUsersDto } from './dto/create-users.dto'; import { CreateUsersDto } from './dto/create-users.dto';
@@ -17,6 +17,7 @@ export class UsersController {
// par exemple dans postamn ou insomnia http://localhost:3000/users?limit=10&offset=20 // par exemple dans postamn ou insomnia http://localhost:3000/users?limit=10&offset=20
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Get('all') @Get('all')
findAll(@Query() paginationquery : PaginationQueryDto) { findAll(@Query() paginationquery : PaginationQueryDto) {
//const { limit, offset } = query; //const { limit, offset } = query;
@@ -33,6 +34,7 @@ export class UsersController {
*/ */
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Get() @Get()
findOne(@Req() req) { findOne(@Req() req) {
return this.usersService.findOne(req.user.id); return this.usersService.findOne(req.user.id);
@@ -46,6 +48,7 @@ export class UsersController {
// } // }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Patch() @Patch()
update(@Req() req, @Body(new ValidationPipe()) usersUpdateDto: UpdateUsersDto) { update(@Req() req, @Body(new ValidationPipe()) usersUpdateDto: UpdateUsersDto) {
console.log("DANS PATCH USERS"); console.log("DANS PATCH USERS");
@@ -53,6 +56,7 @@ export class UsersController {
} }
@UseGuards(AuthenticateGuard) @UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
@Delete() @Delete()
remove(@Req() req) { remove(@Req() req) {
return this.usersService.remove(req.user.id); return this.usersService.remove(req.user.id);

View File

@@ -82,7 +82,11 @@ export class UsersService {
} }
async enableTwoFactorAuth(id: string) { async enableTwoFactorAuth(id: string) {
return this.userRepository.update(id, {isEnabledTwoFactorAuth: true}); return this.userRepository.update(id, {isEnabledTwoFactorAuth: true, isTwoFactorAuthenticated: true});
}
async setIsTwoFactorAuthenticatedWhenLogout(id: number) {
return this.userRepository.update(id, {isTwoFactorAuthenticated: false});
} }
async setAuthenticatorSecret(id: number, secret: string) { async setAuthenticatorSecret(id: number, secret: string) {

View File

@@ -1,17 +1,24 @@
<script> <script>
import Login from "./pages/auth/login.svelte"; import Login from "./pages/auth/login.svelte";
import Home from "./pages/home/home.svelte"; import Home from "./pages/home/home.svelte";
import Router, {link} from 'svelte-spa-router'; import Router, {link, push} from 'svelte-spa-router';
import Profil from "./pages/profil/profil.svelte"; import Profil from "./pages/profil/profil.svelte";
import UpdateProfil from "./pages/profil/updateProfil.svelte"; import UpdateProfil from "./pages/profil/updateProfil.svelte";
import DoubleFa from "./pages/auth/DoubleFa.svelte";
const routes = { const routes = {
"/": Home, "/": Home,
"/login": Login, "/login": Login,
"/profil": Profil, "/profil": Profil,
"/update-profil": UpdateProfil, "/update-profil": UpdateProfil,
"/2fa": DoubleFa,
}; };
$: logout = async() => {
await fetch("http://transcendance:8080/api/v2/auth/logout",{
method : 'POST',
}).then(push('/login'));
}
</script> </script>
<header class="p-3 text-bg-dark"> <header class="p-3 text-bg-dark">
@@ -27,7 +34,11 @@
<a href="/profil" use:link type="button" class="btn btn-primary">Profil</a> <a href="/profil" use:link type="button" class="btn btn-primary">Profil</a>
</li> --> </li> -->
</ul> </ul>
<div>
<button on:click={logout} class="w-100 btn btn-lg btn-primary" type="submit">
Deconnexion
</button>
</div>
<div class="text-end"> <div class="text-end">
<a href="/login" use:link type="button" class="btn btn-warning">Connexion</a> <a href="/login" use:link type="button" class="btn btn-warning">Connexion</a>
</div> </div>

View File

@@ -0,0 +1,89 @@
<script>
import { push } from "svelte-spa-router";
let qrCodeImg;
let qrCode = "";
let wrongCode = "";
let maxTry = 3;
const fetchQrCodeImg = (async() => {
await fetch("http://transcendance:8080/api/v2/auth/2fa/generate",
{
method: 'POST',
})
.then(response => {return response.blob()})
.then(blob => {
const url = URL.createObjectURL(blob);
qrCodeImg = url;
});
})()
$: submit = async() => {
const response = await fetch("http://transcendance:8080/api/v2/auth/2fa/turn-on",
{
method : 'POST',
headers : {
"Content-Type": "application/json",
},
body : JSON.stringify({
"twoFaCode" : qrCode,
}),
});
if (response.status === 401)
{
qrCode = "";
wrongCode = `Wrong code, please try again. You have ${maxTry} before end session`;
maxTry--;
}
if (maxTry === 0)
{
await fetch("http://transcendance:8080/auth/logout",
{
method : 'POST',
})
.then(response => response.json())
.then(push("/login"));
}
if (response.status === 200)
{
push("/");
}
}
</script>
<body>
<main class="form-signin w-100 m-auto">
{#await fetchQrCodeImg}
<p>Please Wait...</p>
{:then data}
{#if wrongCode}
<div class="alert alert-danger" role="alert">
{wrongCode}
</div>
{/if}
<img src={qrCodeImg} alt="A QRCodeImg you must scan with google authenticator" id="qrcodeImg" />
<form on:submit|preventDefault={submit}>
<label for="code" class="block text-sm text-gray-600">Code</label>
<input id="code" bind:value={qrCode} type="text" class="block px-1 py-2 mt-2 border-2 border-gray-100 text-gray-800" />
<button type="submit" class="p-2 bg-blue-500 text-white mt-4 px-6">
Send
</button>
</form>
{:catch}
<p>Unable to get QrCodeImg</p>
{/await}
</main>
</body>
<style>
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
}
.form-signin {
max-width: 330px;
padding: 15px;
}
</style>