

Validatie, authenticatie en autorisatie
Validatie, authenticatie en autorisatie
INTRODUCTIE


Installatie
Installeer bcrypt
npm install bcrypt
Installeer cookie-parser
npm install cookie-parser
Installeer express-validator
npm install express-validator
Installeer jsonwebtoken
npm install jsonwebtoken

Voorbereiding
- Om cookies te kunnen lezen, hebben we de cookieParser nodig. Update je app.js.
app.use(cookieParser());
- Vergeet niet om de body-parser te importeren
import cookieParser from "cookie-parser";

Voorbereiding
- We hebben 3 pagina's in de startcode: home, register & login.



Validatie, authenticatie en autorisatie
MIDDLEWARE


Middleware functions are functions that have access to the request object ( req ), the response object ( res ), and the next function in the application's request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.

Middleware


Middleware
- Eerder gebruikte middleware is o.a. bodyparser
request
response
body-parser
bodyparser kan o.a. de data die wordt meegegeven vanaf de client omzetten naar json.

Middleware
- In deze les gebruiken we de cookie-parser en schrijven we eigen schakels in de middleware ketting.
Validatie, authenticatie en autorisatie
VALIDATIE


Validatie is het controleren van een waarde of een methode op geldigheid of juistheid. Meestal wordt deze werkwijze gebruikt bij het versturen van data met behulp van een formulier.

Validatie
- Om er voor te zorgen dat de data die vanaf de client wordt verstuurd hetgeen is wat we verwachten, gebruiken we express-validator.
- Voeg een post method toe om te registreren, aan te melden en af te melden in app.js.
app.post('/register', postRegister);
app.post('/login', postLogin);
app.post('/logout', logout);

Validatie
- postRegister, postLogin en logout zijn controllers die we plaatsen in controllers/authentication.js.
- Maak naast de bestaande functies ook deze functies aan.


express-validator
- Om express validator te kunnen gebruiken, zullen we vlak vooraleer we de data doorgeven naar ons post-functie deze laten controleren door express-validator via middleware.
request
response
express-
validation
postRegister
register controller

express-validator
- De express-validator middleware zal errors toevoegen aan de request van de client wanneer de body niet voldoet aan criteria.
- Er is tal van ingebouwde criteria van express-validator.
- Stel, je hebt in je body een email adres dat je wil valideren. Je gebruikt de ingebouwde validator als volgt:
body('email').isEmail()

express-validator
- Wil je hier een gebruiksvriendelijke foutmelding aan toevoegen, dan gebruik je withMessage
body('email')
.isEmail()
.withMessage('Onjuist e-mail adres');
- Wil je controleren of je e-mail wel is ingevuld én of het ingevulde effectief een e-mail adres is, dan maak je nieuwe schakels in de ketting.
body('email')
.notEmpty()
.withMessage('E-mail is een verplicht veld.')
.isEmail()
.withMessage('Onjuist e-mail adres'),

express-validator
- In bovenstaand voorbeeld zal de foutmelding de laatste boodschap zijn (Onjuist e-mail adres) TENZIJ je de ketting stopt met de bail functie.
body('email')
.notEmpty()
.withMessage('E-mail is een verplicht veld.')
.bail()
.isEmail()
.withMessage('Onjuist e-mail adres'),

Authenticatie Validatie
- We gaan extra middleware toevoegen met een array aan validatie die gebruikt wordt om de authenticatie-body te kunnen valideren. Ofwel, valideren of het e-mail adres en wachtwoord correct is.
- Maak een folder middleware aan, met daarbinnen een folder validation en de file authentication.js


Authenticatie Validatie
- Telkens wanneer we aanmelden of registeren, willen we de data controleren. Vul authentication.js aan met meerdere validators.
import { body } from 'express-validator';
export default [
body('email')
.notEmpty()
.withMessage('E-mail is een verplicht veld.')
.bail()
.isEmail()
.withMessage('Onjuist e-mail adres'),
body('password')
.isLength({ min: 6 })
.withMessage('Wachtwoord moet bestaan uit minstens zes tekens.'),
]

Authenticatie Validatie
- Voeg nu de validators middleware(s) toe vlak voor we de controller postRegister ingaan.
app.post('/register', ...validationAuthentication, postRegister);
app.post('/login', ...validationAuthentication, postLogin);
- Let op: zorg dat je de validationAuthentication ook importeert in app.js

postRegister
- Eenmaal aangekomen in de postRegister controller kunnen we het resultaat van de validatie opvragen.
export const postRegister = async (req, res, next) => {
try {
const errors = validationResult(req);
// if we have validation errors
if (!errors.isEmpty()) {
console.log("We\'ve got some errors, dude...");
}
} catch(e) {
next(e.message);
}
}

postRegister
- Er zijn nu twee opties
- De validatie loopt fout, dan moeten we opnieuw de de registratie pagina tonen via next()
- De validatie loopt goed, dan registreren we de gebruiker en navigeren we naar de login-pagina
- In het eerste geval gaan moeten we dus nog een extra schakel in de middleware toevoegen:
app.post('/register', ...validationAuthentication, postRegister, register);
app.post('/login', ...validationAuthentication, postLogin, login);

postRegister
- We parsen de fouten en geven de foutmeldingen per input veld door naar de register route via de next() functie.
// if we have validation errors
if (!errors.isEmpty()) {
// errors on a field
req.formErrorFields = {};
errors.array().forEach(({ msg, param }) => {
req.formErrorFields[param] = msg
});
// we have errors, so re-render the registration page
return next();
}

contact
export const register = async (req, res) => {
// do we have errors?
const formErrors = [];
// input fields
const inputs = [{
name: 'email',
label: 'E-mail',
type: 'text',
value: req.body?.email ? req.body.email : '',
error: req.formErrorFields?.email ? req.formErrorFields['email'] : ''
}, {
name: 'password',
label: 'Password',
type: 'password',
value: req.body?.password ? req.body.password : '',
error: req.formErrorFields?.password ? req.formErrorFields['password'] : ''
}]
// render the register page
res.render('register', {
layout: 'authentication',
inputs,
formErrors,
});
}

postRegister
- Zijn alle velden correct, dan valideren we ook of de gebruiker al dan niet al bestaat.
[...]
else {
// get the user repository
const userRepository = getConnection().getRepository('User');
// get a user with a specific e-mail address
const user = await userRepository.findOne({ where: { email: req.body.email } });
// validate if the user already exists
if(user) {
req.formErrors = [{ message: "Gebruiker bestaat al." }];
return next();
}
// hash the password
const hashedPassword = bcrypt.hashSync(req.body.password, 12);
// create a new user
await userRepository.save({ email: req.body.email, password: hashedPassword });
// go to login page
res.redirect('/login');
}
[...]

postRegister
- We gebruiken ook de bcrypt bibliotheek om het ingevoerde wachtwoord te encrypteren.

Oefening
- Zorg er nu voor dat ook je login formulier wordt voorzien van validatie door je login en postLogin controller verder aan te vullen.
- Controleer of de gebruiker bestaat.
Validatie, authenticatie en autorisatie
AUTHENTICATIE


Authenticatie is het proces waarbij iemand nagaat of een gebruiker, een andere computer of applicatie daadwerkelijk is wie hij beweert te zijn.

JWT
- Om ervoor te zorgen dat een user de homepagina te zien krijgt op voorwaarde dat deze is ingelogd, gebruiken we een zogenaamd JSON Web Token ofwel JWT.
Client
Aanmelden
Server
JWT

JWT
- Een token wordt bewaard op de client als een http-only cookie, dat betekent dat ze niet door javascript kan worden onderschept (en dus ook niet door malafide scripts).
- Bij elke request wordt de token verstuurd naar de server en kan de server er van uitgaan dat een gebruiker correct is aangemeld.

JWT
- We maken een token met de jsonwebtoken module.
// create the webtoken
const token = jwt.sign(
{ userId: user.id, email: user.email },
process.env.TOKEN_SALT,
{ expiresIn: '1h' }
);
// add token to cookie in response
res.cookie('token', token, { httpOnly: true });
// redirect to home page
res.redirect('/');
- We hebben salt nodig om er zeker van te zijn dat onze token moeilijker te kraken is.

jwtAuth middleware
- We schrijven een nieuwe middleware functie die we plaatsen vooraleer we de controller aanspreken.
- Maak een nieuwe file in de middleware folder, jwtAuth.js.
import jwt from 'jsonwebtoken';
export const jwtAuth = (req, res, next) => {
const token = req.cookies.token;
try {
const user = jwt.verify(token, process.env.TOKEN_SALT);
req.user = user;
next();
} catch(e) {
res.clearCookie('token');
return res.redirect('/login');
}
}
app.get('/', jwtAuth, home);

jwtAuth middleware
- We controleren of we correct zijn aangemeld
- jwt.verify()
- Zijn we correct aangemeld, dan kunnen we door naar de volgende schakel in de middleware ketting met next().
- Indien de token expired is, dan komen we terecht in de catch blok.
- We verwijderen de token op de client
- We navigeren terug naar de login pagina
Validatie, authenticatie en autorisatie
AUTORISATIE


Autorisatie in de informatica is het proces waarmee een gebruiker rechten krijgt op het benaderen van een object. Je krijgt toegang via autorisatie, wat je mag bekijken of aanpassen hangt af van de rollen die de gebruiker kreeg.

Oefening
In deze oefening breid je je applicatie uit tot een forum achtige landingspagina met slecht één message board. Een gebruiker kan al dan niet berichten toevoegen of verwijderen naargelang de rol die hij/zij/hen heeft verkregen.
- Je maakt een entity Message aan. Deze omvat
- Een primary key: id
- Tekst: text
- De User ID, een één-op-één relatie met de User-entity: userId

Oefening
- Je implementeert drie rollen:
- admin: kan berichten maken én verwijderen
- editor: kan berichten maken
- reader: kan enkel berichten raadplegen
- Een ingelogde user heeft één van de drie rollen
- Maak een entity Role. Deze omvat:
- Een primary key: id
- Een label (admin, editor of reader): label
- Breid User uit met een roleId, een één-op-één relatie met Role.

Oefening
- Je breidt je jwtAuth middleware uit zodat je naast de userId en e-mail ook doorgeeft welke rol de aangemelde user heeft.
- Vang de rol op in je home controller en render énkel hetgeen uit wat de ingelogde user mag zien, conform zijn/haar/hun rol.
- Gebruik hiervoor Handlebars, schrijf eventueel een custom block helper.
- Controleer bij elke POST/GET/PUT of de gebruiker toegang heeft om de HTTP method uit te voeren, indien niet, geef je de respectievelijke HTTP error code terug:
- Bijv. 401: Niet geautoriseerd
- Controleer deze lijst op specifiekere error codes.
Validatie, authenticatie & autorisatie
By timdpaep
Validatie, authenticatie & autorisatie
- 420