Menu
CoddyTech

Giriş Oturumları

Coddy'nin Node.js Journey'sinin Kimlik Doğrulama ve Güvenlik bölümünün bir parçası. Ders 10 / 24.

Giriş isteği 200 yanıtını verir, ancak sonraki istek kimin giriş yaptığını unutmuştur. Bir oturum bunu hatırlar. Giriş yapıldığında sunucu, kimsenin tahmin edemeyeceği bir kimlik oluşturur, bunun hangi kullanıcıya ait olduğunu kaydeder ve çerez olarak gönderir. Sonraki her istek kimliği geri getirir ve sunucu bu kimliği arar.

Kimlik rastgele olmalıdır. Kullanıcı adını tutan bir çerez, herkesin Cookie: sid=ada yazıp Ada olmasına imkân verirdi. randomBytes(24), kimsenin tahmin edemeyeceği 24 rastgele bayt, yani 192 bit üretir. toString('base64url') bunları 32 harf, rakam, - ve _ karakteri olarak yazar; bu karakterler çerezde güvenle kullanılabilir. Sayfadaki betiklerin okuyamaması için çerezin httpOnly özelliğini ayarlayın.

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

// daha sonraki bir istekte
const cart = await carts.findOne({ _id: parseCookies(req.get('cookie')).cart });
challenge icon

Görev

Kolay

Bu adım, Remembering with Cookies ( server.js dâhil) içindeki proje dosyaların yerine, kurs için hazırlanmış özel notlar API projesini açar. Önceki çalışmaların kendi dersinde kalır. server.js dosyasını düzenlemeden önce sağlanan dosyaları oku. Bu kopyada özel notlar için kayıt ve giriş rotaları bulunur.

Giriş çalışıyor, ancak sonraki istekte kimin giriş yaptığı çoktan unutulmuş oluyor. Sunucunun bunu bir oturumla hatırlamasını sağla: sunucuda kaydedilen ve tarayıcıya çerez olarak gönderilen rastgele bir kimlik.

  1. server.js içinde node:crypto modülünden randomBytes içe aktar. ./db.js modülünden sessions öğesini notes ve users öğelerinin yanına içe aktar. Kilitli db.js dosyasında zaten bir sessions koleksiyonu var.
  2. POST /login içinde, parola doğrulandıktan sonra bir oturum kimliği oluştur: base64url metnine dönüştürülmüş 24 rastgele bayt. Bu, - ve _ dâhil harf ve rakamlardan oluşan 32 karakterdir; örneğin oqjRxvxmXl_nRwAwOLxgZyI2sYq9hZtN. Her girişte yeni bir kimlik oluştur.
  3. Oturumu, sessions koleksiyonuna iki alanı olan bir belge olarak kaydet:
    • _id: oturum kimliği.
    • userId: giriş yapan kullanıcının _id değeri.
  4. Kimliği tarayıcıya sid adlı bir çerez olarak, sayfadaki betiklerin çerezi okuyamaması için httpOnly seçeneğiyle gönder. Giriş yanıtı yine {"username": "ada"} olsun.
  5. GET /me rotasını ekle. Bu rota sid çerezini okur, bu _id değerine sahip oturumu bulur ve ardından o oturumun kullanıcısını bulur. {"username": "ada"} yanıtını verir. sid çerezi yoksa veya bu kimliğe sahip bir oturum bulunmuyorsa, 401 durum kodunu ve {"error": "Log in first"} yanıtını verir. sid=ada gibi bir çerezi elle yazan biri de bu 401 yanıtını almalıdır.

Deneyin: “Çalıştır” düğmesine basın. {"username": "ada", "password": "correct horse"} gövdesiyle POST /signup isteği gönderin. GET /me isteği gönderin: 401 durum kodu ve {"error": "Log in first"} yanıtı alın. Kayıt isteğiyle aynı gövdeyi kullanarak POST /login isteği gönderin: 200 durum kodu alın. Yanıtın Headers sekmesinde, noktaların yerine rastgele kimliğiniz gelecek şekilde Set-Cookie: sid=...; Path=/; HttpOnly görünür. GET /me isteğini yeniden gönderin. Tarayıcı çerezi geri gönderir, böylece {"username": "ada"} yanıtını alırsınız. Veri sekmesini açın: sessions koleksiyonunda bir belge bulunur; belgenin _id alanında kimliğiniz, userId alanında ise ada'nın _id değeri yer alır.

Kendin dene

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

// Bu tarayıcının GET /visit isteğini kaç kez yaptığını sayar.
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 });
});

// Notlar. Şimdilik herkes bunları okuyabilir ve yazabilir.
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');
});

Kimlik Doğrulama ve Güvenlik bölümündeki tüm dersler

Kendi başına pratik yap: Express playground