Skip to content

Commit

Permalink
Merge pull request #1 from Varkoff/04-authentication
Browse files Browse the repository at this point in the history
03: Add Shadcn-UI and Tailwind CSS + Add authentication
  • Loading branch information
Varkoff authored Apr 14, 2024
2 parents bd93a1b + 7509015 commit a49bba8
Show file tree
Hide file tree
Showing 48 changed files with 2,056 additions and 113 deletions.
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ ENV TURBO_TOKEN=$TURBO_TOKEN
ENV TZ=Europe/Paris
ENV NODE_ENV="production"

# ADD api/prisma api/prisma

# RUN cd api && npx prisma generate
ADD backend/prisma backend/prisma
RUN cd backend && npx prisma generate

RUN npm run build

Expand Down
2 changes: 1 addition & 1 deletion backend/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
// plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
// 'plugin:prettier/recommended',
],
root: true,
env: {
Expand Down
21 changes: 18 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,39 @@
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@paralleldrive/cuid2": "^2.2.2",
"@prisma/client": "^5.12.1",
"@remix-run/express": "^2.8.1",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"connect-redis": "^7.1.1",
"express-session": "^1.18.0",
"ioredis": "^5.3.2",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@virgile/eslint-config": "*",
"@virgile/frontend": "*",
"@virgile/typescript-config": "*",
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcryptjs": "^2.4.6",
"@types/body-parser": "^1.19.5",
"@types/express": "^4.17.17",
"@types/express-session": "^1.18.0",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/passport-local": "^1.0.38",
"@virgile/eslint-config": "*",
"@virgile/frontend": "*",
"@virgile/typescript-config": "*",
"nodemon": "^3.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.0",
"prisma": "^5.12.1",
"source-map-support": "^0.5.21",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
Expand Down
31 changes: 31 additions & 0 deletions backend/prisma/migrations/20240414130439_init_prisma/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"password" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Session" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"ipAddress" TEXT,
"userAgent" TEXT,
"sessionToken" TEXT NOT NULL,

CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- CreateIndex
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");

-- AddForeignKey
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions backend/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
34 changes: 34 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id String @id @default(cuid())
email String @unique
name String?
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions Session[]
}

model Session {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
ipAddress String?
userAgent String?
sessionToken String @unique
}
12 changes: 0 additions & 12 deletions backend/src/app.controller.ts

This file was deleted.

13 changes: 7 additions & 6 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthController } from './auth/auth.controller';
import { AuthModule } from './auth/auth.module';
import { PrismaService } from './prisma/prisma.service';
import { RemixController } from './remix/remix.controller';
import { RemixService } from './remix/remix.service';

@Module({
imports: [],
controllers: [AppController, RemixController],
providers: [AppService, RemixService],
imports: [AuthModule],
controllers: [AuthController, RemixController],
providers: [PrismaService, RemixService],
})
export class AppModule {}
export class AppModule { }
8 changes: 0 additions & 8 deletions backend/src/app.service.ts

This file was deleted.

34 changes: 34 additions & 0 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Controller, Get, Next, Post, Redirect, Req, Res, UseGuards } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
import { LocalAuthGuard } from './local-auth.guard';

@Controller()
export class AuthController {
@UseGuards(LocalAuthGuard)
@Get('/authenticate')
@Redirect('/')
async login(@Req() req: Request) {
console.log({ requestUser: req.user });
// return req.user;
}

@Post('auth/logout')
async logout(
@Req() request: Express.Request,
@Res() response: Response,
@Next() next: NextFunction,
) {
// this will ensure that re-using the old session id
// does not have a logged in user
request.logOut(function (err) {
if (err) {
return next(err);
}
// Ensure the session is destroyed and the user is redirected.
request.session.destroy(() => {
response.clearCookie('connect.sid'); // The name of the cookie where express/connect stores its session_id
response.redirect('/'); // Redirect to website after logout
});
});
}
}
21 changes: 21 additions & 0 deletions backend/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { PrismaService } from '../prisma/prisma.service';
import { AuthService } from './auth.service';
import { CookieSerializer } from './cookie-serializer';
import { LocalAuthGuard } from './local-auth.guard';
import { LocalStrategy } from './local.strategy';

@Module({
imports: [
PassportModule.register({
defaultStrategy: 'local',
property: 'user',
session: true
})
],
controllers: [],
providers: [LocalStrategy, LocalAuthGuard, CookieSerializer, PrismaService, AuthService],
exports: [AuthService]
})
export class AuthModule { }
103 changes: 103 additions & 0 deletions backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Injectable } from '@nestjs/common';
import { createId } from '@paralleldrive/cuid2';
import { compare, hash } from 'bcryptjs';
import { PrismaService } from '../prisma/prisma.service';
const PASSWORD_SALT = 10;

@Injectable()
export class AuthService {
constructor(private readonly prisma: PrismaService) { }

public readonly checkIfUserExists = async ({
email,
password,
withPassword,
}: {
email: string;
withPassword: boolean;
password: string;
}) => {
// Renvoie true si l'utilisateur n'existe pas
// Renvoie false si l'utilisateur existe
// 1. Vérifier que l'utilisateur existe sur l'email
// 2. Si withPassword est activé, on vérifie que son mot de passe
// est bien défini.
const existingUser = await this.prisma.user.findUnique({
where: {
email,
},
select: {
id: true,
name: true,
email: true,
password: true,
},
});
if (!existingUser) {
return {
message: "L'email est invalide",
error: true,
};
}

if (withPassword) {
// Rajouter une logique de validation par mot de passez
const isPasswordValid = await compare(password, existingUser.password);

if (!isPasswordValid) {
return {
message: "Le mot de passe est invalide",
error: true,
};
}
}
return {
message: "L'utilisateur existe.",
error: false,
};
};

public readonly createUser = async ({
email,
name,
password,
}: {
email: string;
name: string;
password: string;
}) => {
const hashedPassword = await hash(password, PASSWORD_SALT);
return await this.prisma.user.create({
data: {
email: email.toLowerCase(),
password: hashedPassword,
name,
},
select: {
id: true,
name: true,
email: true,
},
});
};

public readonly authenticateUser = async ({
email,
}: {
email: string;
}) => {
return await this.prisma.session.create({
data: {
user: {
connect: {
email
},
},
sessionToken: createId()
},
select: {
sessionToken: true
}
})
};
}
15 changes: 15 additions & 0 deletions backend/src/auth/cookie-serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable @typescript-eslint/ban-types */
import { Injectable } from '@nestjs/common';
import { PassportSerializer } from '@nestjs/passport';

@Injectable()
export class CookieSerializer extends PassportSerializer {
deserializeUser(payload: any, done: Function) {
// console.log('deserializeUser', { payload });
done(null, payload);
}
serializeUser(user: any, done: Function) {
// console.log('serializeUser', { user });
done(null, user);
}
}
27 changes: 27 additions & 0 deletions backend/src/auth/exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
import { RedirectException } from './redirected-error.exception';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();

if (exception instanceof RedirectException) {
const { redirectUrl, message } = exception
return response.redirect(302, `${redirectUrl}?error=${message}`)
}

response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
Loading

0 comments on commit a49bba8

Please sign in to comment.