jsramverk.se

jsramverk.se / Auth

Auth

Skriven av: Emil Folino. Uppdaterad: 2024-02-27

Denna veckan tittar vi på hur vi kan lägga till autentisering i vårt API och implementerar login-formulär på frontend.

Läsa

Nielsen Norman Group är världsledande inom forskningsbaserad User Experience (UX). Följande två artiklar introducerar bra råd för att skapa användbara formulär:

Path of Least Resistance

I detta kursmomentet är Path of Least Resistance att använda erfarenheterna från webapp-kursen och att implementera med JWT i backend.

Autentisering

När vi vill autentisera en användare kan det gå till på lite olika sätt. Vi tittade i webapp-kursen hur man kan använda JWT för att autentisera sig mot ett API. Det är även det sättet som beskrivs nedan och som är i implementerad i auth_mongo.

Ett annat sätt är att använda sessions-baserad inloggning som vi har tittat på tidigare i programmen. I de allra flesta fallen vill man använda sig av ett befintligt paket för att hantera sessionsinloggning. I node.js är det mest använda paketet passport.js. passport.js hanterar även olika strategier för att hantera autentisering till exempel via olika sociala medier med hjälp av OAuth.

Säker hantering av lösenord

När vi sparar lösenord i en databas vill vi göra det så säkert som möjligt. Därför använder vi bcrypt.

Vi installerar ett bcrypt paket med npm med hjälp av kommandot npm install bcryptjs --save. Dokumentationen för modulen är som alltid vår bästa vän.

För att hasha ett lösenord med bcrypt modulen importerar vi först modulen och sedan använder vi bcrypt.hash funktionen. Antal saltRounds definierar hur svåra lösenord vi vill skapa. Ju fler saltRounds är svårare att knäcka, men tar också längre tid att skapa och jämföra.

const bcrypt = require('bcryptjs');
const saltRounds = 10;
const myPlaintextPassword = 'longandhardP4$w0rD';

bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
    // spara lösenord i databasen.
});

Det finns även en promise version av biblioteket om man gillar promise eller async/await teknikerna. Läs mer om det i dokumentationen.

För att jämföra ett sparad lösenord med det användaren skrivit in använder vi bcrypt.compare.

const bcrypt = require('bcryptjs');
const myPlaintextPassword = 'longandhardP4$w0rD';
const hash = 'superlonghashedpasswordfetchedfromthedatabase';

bcrypt.compare(myPlaintextPassword, hash, function(err, res) {
    // res innehåller nu true eller false beroende på om det är rätt lösenord.
});

JSON Web Tokens

Vi har i tidigare kurser använt både sessioner och tokens för att autentisera klienter mot en server. Vi ska i detta stycke titta på hur vi implementerar logiken bakom att skicka JSON Web Tokens från servern till en klient. Vi använder modulen jsonwebtoken som vi installerar med kommandot npm install jsonwebtoken --save och dokumentationen finns på npm.

Vi använder här de två funktioner sign och verify.

const jwt = require('jsonwebtoken');

const payload = { email: "user@example.com" };
const secret = process.env.JWT_SECRET;

const token = jwt.sign(payload, secret, { expiresIn: '1h'});

I ovanstående exempel skapar vi payload som i detta fallet enbart innehåller klientens e-post. Vi hämtar sedan ut vår JWT_SECRET från environment variablerna. En environment variabel sätts i terminalen, både lokalt på din dator och på servern med kommandot export JWT_SECRET='longsecret', där du byter ‘longsecret’ mot nått långt och slumpmässigt. Se till att denna secret är lång och slumpmässig, gärna 64 karaktärer. payload och secret blir sedan tillsammans med ett konfigurationsobjekt argument till funktionen jwt.sign och returvärdet är vår token.

När vi sen vill verifiera en token använder vi funktionen jwt.verify. Här skickar vi med token och vår secret som argument. Om token kan verifieras får vi dekrypterat payload och annars ett felmeddelande.

const secret = process.env.JWT_SECRET;

jwt.verify(token, secret, function(err, decoded) {
    if (err) {
        // not a valid token
    }

    // valid token
});

JWT middleware

Vi såg i kmom02 hur vi kan skapa routes som tar emot POST anrop och hur vi kan använda middleware för att köra en funktion varje gång vi har ett anrop till specifika routes. Om vi skapar nedanstående route i vår me-api ser vi hur middleware funktionen checkToken ligger som första funktion på routen. Den anropas först och beroende på om next() anropas funktionen efter middleware. Vi observerar även hur vi från klientens sida har skickat med token som en del av headers och hur vi hämtar ut det från request-objektet req.

router.post("/reports",
    (req, res, next) => checkToken(req, res, next),
    (req, res) => reports.addReport(res, req.body));

function checkToken(req, res, next) {
    const token = req.headers['x-access-token'];

    jwt.verify(token, process.env.JWT_SECRET, function(err, decoded) {
        if (err) {
            // send error response
        }

        // Valid token send on the request
        next();
    });
}

Vi ser i kodexemplet ovan att vi använder req.body när vi tar emot en POST request från en klient och skickar med det in till modulen/modellen vi använder för att skapa rapporten. För att kunna använda req.body har vi dessa två rader längst upp i vår app.js.

Vi såg i artikeln Login med JWT kursen webapp hur man kan skicka lösenord med postman. postman är ett utmärkt verktyg för att manuellt testa ett API. I postman kan man även sätta headers under headers fliken för varje request.

Postman

Kravspecifikation

  1. Skapa ett registreringsformulär och ett inloggningsformulär i frontend. Se till att använda de goda råden från läsanvisningarna när du designar formuläret.

  2. Välj antingen inloggning med JWT eller passport.js och implementera möjlighet för att registrera och logga in en användare i backend.

  3. Dokument i editorn ska bara gå att komma åt för autentiserade användare och bara för de användare som har givits tillstånd att ändra i dokumenten.

  4. Fundera över din dokumentstruktur och hur du kan implementera autentiseringen och tillstånden att ändra i dokument på ett effektivt sätt.

  5. Committa alla filer i frontend och backend. Lägg till en tagg som passar in i versionsnumreringen.

  6. Pusha upp repot till GitHub, inklusive taggarna.

  7. Lämna in länk till frontenden och dina GitHub repon som en kommentar till din inlämning i Canvas.

Skriva

Vi flyttar denna och kommande veckan fokus från forskningsfrågorna och syftet till metod. Vi ska nu beskriva för läsaren HUR vi har tänkt att genomföra studien.

I metod beskrivs hur man har tänkt att svara på sina forskningsfrågor. I skrivguiden beskrivs under Uppsatsens delar metod.

Under föreläsningen presenterar Emil ett antal olika metoder, som kan vara intressanta för området. Nedan finns en översikt, som kan vara bra att luta sig mot. Din uppgift är att för varje forskningsfråga detaljerat beskriva hur du har tänkt besvara frågan. En bra struktur är att börja stycken i metodbeskrivningen med: “För att besvara RQ1 används …”.

Lägg metod beskrivningen som ett eget stycke, så inte direkt under forskningsfrågorna och syftet som vi har gjort tidigare.

Från exjobbsmallen läser vi följande om den emperiska metoden:

The method for realisation shall be described in detail! Describe the connection between each research question to be answered and the process of realisation you use for answering each question. It should be possible to understand your set-up for the thesis, and it shall be possible to replicate your study.

Lämna in texten som PDF bilaga till din inlämning på Canvas.

Forskningsmetoder