Menu
CoddyTech

Sessions de connexion

Fait partie de la section Authentification et sécurité du Journey Node.js de Coddy. Leçon 5 sur 17.

La connexion répond 200, puis la requête suivante a oublié qui s’est connecté. Une session s’en souvient. À la connexion, le serveur crée un identifiant impossible à deviner, enregistre à quel utilisateur il appartient et l’envoie sous forme de cookie. Chaque requête ultérieure renvoie l’identifiant, que le serveur recherche.

L’identifiant doit être aléatoire. Un cookie contenant le nom d’utilisateur permettrait à n’importe qui de saisir Cookie: sid=ada et de se faire passer pour Ada. randomBytes(24) fournit 192 bits aléatoires. Définissez le cookie httpOnly afin que les scripts de la page ne puissent pas le lire.

const cartId = randomBytes(24).toString('base64url');
await carts.insertOne({ _id: cartId, items: [] });
res.cookie('cart', cartId, { httpOnly: true });

// lors d’une requête ultérieure
const cart = await carts.findOne({ _id: parseCookies(req.get('cookie')).cart });
challenge icon

Défi

Facile
  • POST /login stocke { _id: sid, userId } dans sessions et définit le cookie sid, avec l’attribut httpOnly, à l’aide de randomBytes(24).toString('base64url').
  • GET /me renvoie { username } pour cette session, ou le code 401 avec { "error": "Log in first" }.

Inscris-toi, connecte-toi, envoie GET /me, puis consulte Data.

Essayez vous-même

import express from 'express';
import { parseCookies } from './cookies.js';
import { notes, users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';

const app = express();
app.use(express.json());

app.post('/signup', async (req, res) => {
    const username = String(req.body.username ?? '').trim().toLowerCase();
    const password = String(req.body.password ?? '');
    if (!username || password.length < 8) {
        return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
    }
    if (await users.findOne({ username })) {
        return res.status(409).json({ error: 'That username is taken' });
    }
    await users.insertOne({ username, passwordHash: await hashPassword(password) });
    res.status(201).json({ username });
});

app.post('/login', async (req, res) => {
    const username = String(req.body.username ?? '').trim().toLowerCase();
    const user = await users.findOne({ username });
    if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
        return res.status(401).json({ error: 'Wrong username or password' });
    }
    res.json({ username: user.username });
});

// Compte le nombre de fois que ce navigateur a appelé GET /visit.
app.get('/visit', (req, res) => {
    const cookies = parseCookies(req.get('cookie'));
    const visits = Number(cookies.visits ?? 0) + 1;
    res.cookie('visits', String(visits));
    res.json({ visits });
});

// Notes. Pour l’instant, tout le monde peut les lire et les écrire.
app.get('/notes', async (req, res) => {
    res.json(await notes.find().toArray());
});

app.post('/notes', async (req, res) => {
    const note = { text: String(req.body.text ?? '') };
    await notes.insertOne(note);
    res.status(201).json(note);
});

app.listen(3000, () => {
    console.log('Vault is running on port 3000');
});

Toutes les leçons de Authentification et sécurité