Menu
CoddyTech

Sesi Login

Termasuk bagian Autentikasi dan Keamanan dalam perjalanan Node.js di Coddy. Pelajaran 5 dari 17.

Login mengembalikan 200, lalu permintaan berikutnya sudah lupa siapa yang login. Sesi mengingatnya. Saat login, server membuat id yang tidak bisa ditebak siapa pun, menyimpan pengguna yang terkait dengannya, lalu mengirimkannya sebagai cookie. Setiap permintaan berikutnya mengirimkan id itu kembali, lalu server mencarinya.

Id harus acak. Cookie yang menyimpan nama pengguna memungkinkan siapa pun mengetik Cookie: sid=ada dan menjadi Ada. randomBytes(24) menghasilkan 192 bit acak. Atur cookie ke httpOnly agar script di halaman tidak dapat membacanya.

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

// pada permintaan berikutnya
const cart = await carts.findOne({ _id: parseCookies(req.get('cookie')).cart });
challenge icon

Tantangan

Mudah
  • POST /login menyimpan { _id: sid, userId } di sessions dan menetapkan cookie sid dengan httpOnly, menggunakan randomBytes(24).toString('base64url').
  • GET /me mengembalikan { username } untuk sesi tersebut, atau 401 { "error": "Log in first" }.

Buat akun, login, kirim GET /me, lalu lihat Data.

Coba sendiri

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

// Menghitung berapa kali browser ini memanggil 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 });
});

// Catatan. Untuk saat ini, siapa pun bisa membaca dan menulisnya.
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');
});

Semua pelajaran di Autentikasi dan Keamanan

Latihan sendiri: Playground Express