Authentification avec Express et PrismaDB

authentification-avec-express-et-prismadb

Ce qu’on va utiliser

Express

C’est une librairie NodeJS qui rend le fait de crรฉer un serveur REST super facile. Trรจs lรฉger, il nous permet de faire les choix que l’on veut, c’est un trรจs bon point de depart pour celles et ceux qui veulent apprendre Node.

Un ORM

Au lieu d’รฉcrire une requete SQL qui casse les couilles, je me contente tout simplement d’ordonner des choses ร  mon ORM et lui va traduire ce que je peux en une requete SQL qui va bien.

Plus jamais tu รฉcriras ce genre de trucs:

SELECT *
FROM table
WHERE condition
GROUP BY expression
HAVING condition
{ UNION | INTERSECT | EXCEPT }
ORDER BY expression
LIMIT count
OFFSET start

Prisma

En parlant d’ORM justement, Prisma fait partie des plus modernes et plus simples ร  utiliser. Il propose une dรฉfinition de schema (la gueule que va avoir mon User, ma Voiture…etc…) intuitive.
Il gรฉre les migrations, qui est le fait de claquer mon schema en base sans รฉcrire moi-meme les requetes SQL qu’il faut.

JSON Web Token

JWT est une facon de gรฉrer l’authentification, c’est un peu la plus utilisรฉe aujourd’hui car graces au devs fronts chiants comme moi, nous avons une sรฉparation entre le Front et le Back (deux repos, serveurs differents) du coup plus question d’uiliser les cookie. En gros on fait passer un Token qui est une sorte de code (une succession de clin d’oeils que reconnaitra mon serveur).
C’est une phrase cryptรฉs que le back saura dรฉcrypter et que le client sauvegardera en faisant gaffe ร  ne jamais la reveler ร  qui que ce soit, du moins tant qu’elle est valable.

Initialisation du projet

Dans mon terminal:

mkdir levideur # Parce que je vais ce que je veux
cd levideur

On initialise un projet TypeScript (parce que TypeScript c’est bien)

npm init -y
npm install typescript ts-node @types/node --save-dev

Faut ajouter ce fichier aussi tsconfig.json:

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}

Maintenant, je peux installer prisma:

npm install prisma --save-dev

On peut intialiser Prisma:

npx prisma init --datasource-provider sqlite

Je prends une BD SQLite car c’est plus rapide ร  Setup pas besoin d’installer quoi que ce soit mais ca reste du SQL.

Mon premier Schema

Un Schema est une maniere de dรฉcrire la gueule d’une donnรฉe.

Dans le fichier prisma/schema.prisma, j’ajoute:

model User {
  id       Int    @id @default(autoincrement())
  email    String @unique
  password String
}

Maintenant que j’ai dรฉcris les tables de ma base, je suis pret ร  claquer ces infos dans ma abse de donnรฉe:

npx prisma migrate dev --name init

C’est good! nous sommes prets ร  utiliser notre base.

Je crรฉe un fichier main.ts:

// main.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  const user = await prisma.user.create({
    data: {
      email: 'me@sidali.dev',
      password: '123456'
    },
  })
  console.log(user)
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

Est-ce que tout marche, dans mon terminal:

npx ts-node main.ts

Image description

Nickel! ๐Ÿ”ฅ

Le code est ici: Repo du videur

Place au serveur

Dans mon terminal:

npm i express jsonwebtoken cors
npm i -D morgan
  • cors: permettre des appels en dehors du domaine oรน est hรฉbรฉrgรฉ le serveur.
  • morgan: pour les logs
import express from "express"
import morgan from "morgan"
import cors from "cors"
import { PrismaClient } from "@prisma/client"

const PORT = 1234

// Initialisation de Express 4
const app = express()

// Activation de CORS pour les CORS...
app.use(cors())
// Activation de Morgan pour les logs
app.use(morgan("tiny"))
// Activation du raw (json)
app.use(express.json())
// Activation de x-wwww-form-urlencoded

app.use(express.urlencoded({ extended: true }))

// Initialisation du client Prisma
const prisma = new PrismaClient()

// Requete GET /
app.get("/", async (req, res) => {
  const users = await prisma.user.findMany()
  res.send(users)
})

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`)
})

Je lance mon serveur du coup;

npx ts-node main.ts

Je visite (http://localhost:1234/) et c’est good:

Image description

Inscription

On va crรฉer la route /signup qui va prendre en dans sa requete l’email et le mot de passe. Si c’est ta premiere API REST, grosso modo une application Mobile ou une appli Web (moderne) va envoyer des requetes vers le serveur et le serverur rรฉponds, ex: j’envois l’email et le mdp, le serveur me rรฉpond avec un OK ou KO.

ร€ ne pas oublier! Pour tester les appels vers notre API l’idรฉal est d’installer [Insomnia]

(https://insomnia.rest/download) ou Postman

//...
app.post("https://dev.to/signup", async (req, res) => {
  // Crรฉation d'un user ร  partir des attributs du Body
  const user = await prisma.user.create({
    data: {
      email: req.body.email,
      password: req.body.password,
    },
  })
  res.send(user)
})

Image description

C’est pas ouf car j’ai plusieurs soucis:

  • J’enregistre le mot de passe sans le crypter
  • Je ne permets pas au user de se connecter car je ne lui donne pas de JWT

On va rectifier ca, dans mon terminal:

npm i bcrypt

Il ne faut pas oublier de l’importer dans main.ts

// ...
import bcrypt from 'bcrypt'
// ...

app.post("/signup", async (req, res) => {
  // Plus y a de sel plus le mdp sera dur ร  brute force
  const salt = await bcrypt.genSalt(10)
  // Je gen un mdp cryptรฉ
  const crypted_password = await bcrypt.hash(req.body.password, salt)

  const user = await prisma.user.create({
    data: {
      email: req.body.email,
      password: crypted_password,
    },
  })
  res.send(user)
})

Resultat, nickel:
Image description

Il ne me manque plus qu’un truc c’est de donner un gรฉnรฉrer et renvoyer un JWT pour que celle ou celui qui s’est loggรฉ puisse requeter d’autres routes (qui necessitent d’etre loggรฉ) sans soucis.

//...
import jwt from "jsonwebtoken"
// Servira pour coder/decoder le token
const SECRET = "secret"
//...

app.post("/signup", async (req, res) => {
  // Plus y a de sel plus le mdp sera dur ร  brute force
  const salt = await bcrypt.genSalt(10)
  // Je gen un mdp cryptรฉ
  const crypted_password = await bcrypt.hash(req.body.password, salt)

  const { id, email } = await prisma.user.create({
    data: {
      email: req.body.email,
      password: crypted_password,
    },
  })
  // Gรฉrener un token
  const access_token = jwt.sign({ id }, SECRET, { expiresIn: "3 hours" })
  // Envoyer le token avec d'autres infos en bonus
  res.send({ id, email, token: access_token })
})

Connexion

Total
1
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
discussion-and-comment-of-the-week-–-v22

Discussion and Comment of the Week – v22

Next Post
how-to-encrypt-files-with-aes-using-openssl

How to encrypt files with AES using OpenSSL

Related Posts