Uživatelé, hesla & cookies
V této kapitole si zopakujeme vše, co jsme se během semestru naučili. Hodně aplikací na internetu má nějaký způsob registrace a přihlašování, tak přidáme podobnou funkcionalitu do naší ToDos aplikace.
Uživatelé
Jelikož jsme si ukázali testy a TDD, budeme se této praktiky držet. Vytvoříme si tests/users.spec.js s nutným kódem a prvním testem.
Do databáze nechceme ukládat hesla v plain textu (kdyby nám někdo ukradl databázi aby se nedostal k heslům uživatelů) a tak v testu můžeme i ověřit že user.hash kam budeme ukládat zahashovaná hesla neobsahuje zadané heslo.
Pokud spustíme testy zjistíme, že neexistuje funkce createUser. Tak si ji vytvoříme.
Pro hashování budeme používat knihovnu crypto která je součástí Node.js. Nemusíme nic instalovat a můžeme ji rovnou importovat.
Co budeme u uživatelů ukládat? name a hash (hash hesla) je jasné. Dále budeme potřebovat ukládat sůl (další bezpečnostní složka při ukládání hesel do databáze) a token, který bude mít každý uživatel unikátní a po přihlášení ho budou pouýívat místo jména a hesla k autentifikaci. Sůl i token bude náhodně vygenerovaný řetězec. Hash spočítáme.
100000 reprezentuje počet iterací kolikrát se hash “přepočítá”. Čím větší číslo, tím je heslo bezpečněji zahashováno, ale zároveň výpočet hashe déle trvá. 64 je délka výsledného hashe. sha512 algoritmus použitý k výpočtu hashe.
Nyní můžeme všechny údaje uložit do databáze. .insert vrací pole s IDčkami záznamů, které byli vytvořeny. My budeme chtít z funkce createUser nově vytvořeného uživatele vrátit a tak ho po vytvoření z databáze rovnou vytáhneme.
Teď nám testy poví, že neexistuje tabulka users. Takže přidáme migraci, který tabulku vytvoří.
Nezapomeneme změnit CommonJS export na ESM (z exports.up = na export const up =).
Nyní by testy měli procházet.
Pokud chceme otestovat aplikaci v prohlížeči, nesmíme zapomenout manuálně spustit migrace!
Zároveň si i vytvoříme druhý test pro funkci getUser
A dáme se do psaní getUser funkce.
hash je počítán stejně jako ve funkce createUser ale salt už není náhodně generován, ale je použit salt daného uživatele, který byl zároveň s uživatelem uložen v databázi.
Registrace
A začneme rovnou testem
Express nezná cestu GET /register a tak vytvoříme nový handler pro tuto cestu.
A protože renderujeme nový register view, musíme ho vytvořit.
Formulář se nám již zobrazuje, takže jdeme implementovat jeho odesílání.
Handler (nezapomenout impor createUser funkce)
Po lepší ovládání přídáme odkaz na index.ejs
Cookies
Chtěli bychom, aby registrace nás zároveň přihlásila a aby na domovské obrazovce bylo zobrazeno jméno aktuálně přihlášeného uživatele. Jelikož HTTP je bezstavové, využijeme cookies k uložení tokenu uživatele do prohlížeče. Prohlížeč s každým požadavkem odesílá zároveň i cookies ze kterých vyčteme token a tak poznáme o jakého uživatele se jedná.
Pokud chceme aby si supertest pamatoval cookies mezi requesty, tak jako prohlížeč, potřebujeme instanci agenta kam si cookie ukládá.
Dříve express obsahoval middleware pro zpracování cookies, dnes ho musíme nainstalovat jako extra knihovnu.
A přidáme mezi seznam middlewarů.
Nyní můžeme po registraci uložit prohlížeči mezi cookies nové cookie s tokenem.
Jelikož uživatel je přihlášen “na všech stránkách”, budeme chtít logiku ziskání přihlášeného uživatele mít na jednom místě pro všechny handlery. Využijeme tedy univerzální middleware který přidáme před všechny handlery.
Do src/db.js doplníme getUserByToken.
A jako poslední přidáme vykreslení jména uživatele do view.
Schování cesty nepřihlášeným uživatelům
Představme si, že nechceme aby nepřihlášení uživatelé viděli naši aplikace. Po pokusu zobrazení seznamu ToDos na / chceme nepřihlášené uživatele přesměrovat na formulář s registrací/přihlášením. Podobnou funkcionalitu budeme chtít i u ostatních cest (přidání nového ToDo, úprava existujícího ToDo, ...). Budeme tedy chtít mít nějakou univerzální ochranu request handlerů. Middleware je zde ideální volba.
Tento middleware se podívá zda existuje user (např zda existuje req.cookie.token nebo zda existuje res.locals.user, který jsme nastavili dříve) a pokud ano, pomocí next funkce spustí následující middleware v pořadí (nejspíše finální route handler) a pokud ne, přesměruje uživatele na registrační formulář.
Jelikož budeme tento middleware využívat na více místech, uložíme si ho do proměnné.
Nyní pokud chceme tímto middlewarem “ochránit” nějakou cestu, přidáme ji před daný route handler.
app.get má nyní tři parametry. První je stále cesta na které poslouchá, ale druhý je nyní náš nový middleware. Původní route handler je nyní třetí parametr. Tzn funkce next v auth middlewaru po zavolání předává řízení právě do route handleru.
Tato úprava nám rozbije testy. To není špatně. Změnili jsme veřejné rozhraní aplikace (nově nejde používat když nejsme přihlášeni) a tak nás na to neprocházející testy upozorní.
Samostatné cvičení
Jako samostatné cvičení si zkuste udělat formulář pro přihlášení již registrovaných uživatelů, tlačítko pro odhlášení a popř i schování aplikace za autentifikaci.
