Nur deine Notizen
Teil des Abschnitts Authentifizierung und Sicherheit der Node.js-Journey von Coddy. Lektion 13 von 17.
Angemeldet zu sein, beweist, wer du bist, aber nicht, dass eine Notiz dir gehört. GET /notes/:id sucht eine Notiz nur anhand ihrer ID. Daher kann jede angemeldete Person, die eine ID kennt – aus einem geteilten Link oder einem Protokoll –, diese Notiz lesen. Die Prüfung, wem die Notiz gehört, nennt man Autorisierung.
Nimm den Besitzer in die Abfrage selbst auf, damit die Datenbank niemals das Dokument einer anderen Person zurückgibt. Antworte mit 404 statt mit 403, wenn nichts gefunden wird: 403 würde dem Besucher verraten, dass die Notiz existiert.
const order = await orders.findOne({
_id: new ObjectId(req.params.id),
customer: req.user.id,
});
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}Jede Abfrage, die auf die Daten eines Benutzers zugreift, benötigt diesen Filter: beim Lesen, Aktualisieren und Löschen.
Aufgabe
EinfachGET /notes/:idundDELETE /notes/:idfinden nur Notizen, derenownerreq.user.idist.- Bei der Notiz einer anderen Person wird 404 zurückgegeben:
{ "error": "Note not found" }.
Melde dich als ada an und füge eine Notiz hinzu. Registriere dich dann und melde dich als bob an und frage anhand ihrer _id nach Adas Notiz.
Probier es selbst
import express from 'express';
import { ObjectId } from 'mongodb';
import { randomBytes } from 'node:crypto';
import { parseCookies } from './cookies.js';
import { notes, sessions, users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const app = express();
app.use(express.json());
// Das einzige Admin-Konto. Sein Passwort stammt aus der Umgebung.
await users.insertOne({ username: 'admin', role: 'admin', passwordHash: await hashPassword(process.env.ADMIN_PASSWORD) });
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' });
}
const sid = randomBytes(24).toString('base64url');
await sessions.insertOne({ _id: sid, userId: user._id, createdAt: new Date() });
res.cookie('sid', sid, {
httpOnly: true,
sameSite: 'lax',
secure: true,
maxAge: 24 * 60 * 60 * 1000,
});
res.json({ username: user.username });
});
// Findet den Benutzer anhand des Session-Cookies oder antwortet mit 401.
async function requireUser(req, res, next) {
const { sid } = parseCookies(req.get('cookie'));
const session = sid && (await sessions.findOne({ _id: sid }));
const user = session && (await users.findOne({ _id: session.userId }));
if (!user) {
return res.status(401).json({ error: 'Log in first' });
}
req.user = { id: user._id.toHexString(), username: user.username, role: user.role };
next();
}
app.get('/me', requireUser, (req, res) => {
res.json({ username: req.user.username });
});
app.post('/logout', async (req, res) => {
const { sid } = parseCookies(req.get('cookie'));
if (sid) {
await sessions.deleteOne({ _id: sid });
}
res.clearCookie('sid');
res.status(204).end();
});
// Deine Notizen als Webseite.
app.get('/', requireUser, async (req, res) => {
const list = await notes.find({ owner: req.user.id }).toArray();
const items = list.map((note) => `<li>${note.text}</li>`).join('');
res.send(`<!doctype html><title>Vault</title><h1>${req.user.username}'s notes</h1><ul>${items}</ul>`);
});
// Notizen: Jeder Benutzer liest und schreibt nur seine eigenen.
app.get('/notes', requireUser, async (req, res) => {
res.json(await notes.find({ owner: req.user.id }).toArray());
});
app.post('/notes', requireUser, async (req, res) => {
const note = { owner: req.user.id, text: String(req.body.text ?? '') };
await notes.insertOne(note);
res.status(201).json(note);
});
app.get('/notes/:id', requireUser, async (req, res) => {
if (!ObjectId.isValid(req.params.id)) {
return res.status(404).json({ error: 'Note not found' });
}
const note = await notes.findOne({ _id: new ObjectId(req.params.id) });
if (!note) {
return res.status(404).json({ error: 'Note not found' });
}
res.json(note);
});
app.delete('/notes/:id', requireUser, async (req, res) => {
if (!ObjectId.isValid(req.params.id)) {
return res.status(404).json({ error: 'Note not found' });
}
const { deletedCount } = await notes.deleteOne({ _id: new ObjectId(req.params.id) });
if (!deletedCount) {
return res.status(404).json({ error: 'Note not found' });
}
res.status(204).end();
});
// Für Admins: Wie viele Konten es gibt.
app.get('/admin', requireUser, async (req, res) => {
res.json({ users: await users.countDocuments() });
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Authentifizierung und Sicherheit
4Eigentümer und Sicherheit
Nur deine NotizenAdmin-Rollen2Sitzungen und Cookies
Mit Cookies merkenLogin-Sitzungen