Menu
CoddyTech

Sesiones de inicio de sesión

Parte de la sección Autenticación y seguridad del Journey de Node.js de Coddy. Lección 5 de 17.

El inicio de sesión responde con 200, pero la siguiente solicitud ya no recuerda quién inició sesión. Una sesión lo recuerda. Al iniciar sesión, el servidor crea un id que nadie puede adivinar, guarda a qué usuario pertenece y lo envía como una cookie. Cada solicitud posterior devuelve el id, y el servidor lo busca.

El id debe ser aleatorio. Una cookie que contenga el nombre de usuario permitiría que cualquiera escribiera Cookie: sid=ada y se hiciera pasar por Ada. randomBytes(24) proporciona 192 bits aleatorios. Configura la cookie como httpOnly para que los scripts de la página no puedan leerla.

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

// en una solicitud posterior
const cart = await carts.findOne({ _id: parseCookies(req.get('cookie')).cart });
challenge icon

Desafío

Fácil
  • POST /login guarda { _id: sid, userId } en sessions y establece la cookie sid, httpOnly, con randomBytes(24).toString('base64url').
  • GET /me devuelve { username } para esa sesión, o 401 { "error": "Log in first" }.

Regístrate, inicia sesión, envía GET /me y después mira Datos.

Pruébalo tú mismo

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 });
});

// Cuenta cuántas veces este navegador ha solicitado 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 });
});

// Notas. Por ahora, cualquiera puede leerlas y escribirlas.
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');
});

Todas las lecciones de Autenticación y seguridad