Uživatelé, hesla & cookies

V této kapitole si zopakujeme vše, co jsme se během semestru naučili. Většina aplikací vyžaduje registraci a přihlašování, tak přidáme tuto funkcionalitu do naší Todo aplikace.

Uživatelé

Budeme se držet praktik TDD. Vytvoříme si tests/users.spec.js s prvním testem. Do databáze nechceme ukládat hesla v čitelné podobě, proto budeme ukládat jejich hash.

// tests/users.spec.js
test.serial('create new user', async (t) => {
  const user = await createUser('name', 'password')

  t.is(user.name, 'name')
  t.not(user.hash, 'password') // heslo nesmí být v databázi čitelné
})

Pro hashování použijeme vestavěný modul crypto. U uživatele budeme ukládat name, hash, salt (bezpečnostní složka) a token (pro autentifikaci po přihlášení).

// src/db.js
import crypto from 'crypto'

export const createUser = async (name, password) => {
  const salt = crypto.randomBytes(16).toString('hex')
  const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex')
  const token = crypto.randomBytes(16).toString('hex')

  const [user] = await db('users').insert({ name, salt, hash, token }).returning('*')
  return user
}

Nezapomeňte vytvořit migraci pro tabulku users pomocí npx knex migrate:make add_users_table.

// migrations/..._add_users_table.js
export const up = async (knex) => {
  await knex.schema.createTable('users', (table) => {
    table.increments('id')
    table.string('name').notNullable().unique()
    table.string('salt').notNullable()
    table.string('hash').notNullable()
    table.string('token')
  })
}

Registrace

Vytvoříme formulář pro registraci a handler pro jeho zpracování.

// src/app.js
app.post('/register', async (req, res) => {
  const { name, password } = req.body
  await createUser(name, password)
  res.redirect('/')
})

Cookies

Aby si prohlížeč pamatoval, že jsme přihlášeni, využijeme cookies k uložení tokenu. Nainstalujeme cookie-parser:

npm install cookie-parser

A přidáme jej do aplikace:

import cookieParser from 'cookie-parser'
app.use(cookieParser())

Při registraci (nebo přihlášení) nastavíme cookie:

res.cookie('token', user.token)

Pro zjištění přihlášeného uživatele u každého požadavku vytvoříme middleware:

app.use(async (req, res, next) => {
  const token = req.cookies.token
  res.locals.user = token ? await getUserByToken(token) : null
  next()
})

Ochrana cest (Authentication)

Aby k některým částem aplikace měli přístup pouze přihlášení uživatelé, vytvoříme middleware auth:

const auth = (req, res, next) => {
  if (res.locals.user) {
    next()
  } else {
    res.redirect('/register')
  }
}

Tento middleware pak přidáme k cestám, které chceme chránit:

app.get('/', auth, async (req, res) => {
  // ...
})

Tímto jsme zabezpečili naši aplikaci a umožnili uživatelům se registrovat a bezpečně přihlašovat.