The so-called ‘Login-System’ that we put up so far isn’t quite… secure, to say the least. The way it is currently implemented, anyone could come by, ’login’ and have an account created. Let’s toughen that up and replace it with some proper authentication mechanisms.
First things first - we need more packages!
|
|
We use passport, because it provides an easy to use and secure way to add authentication to ExpressJS.
Bcrypt is the recommended hashing algorithm for storing passwords. Sticking with standards is always wise when it comes to crypto.
Connect-flash lets us generate (error-)messages, store them in session and have them automatically deleted after they’ve been displayed to the user.
Secure hashing
To be able to generate and validate secure passwords from within our user-model, we edit our models/user.js
:
|
|
All we did was providing an interface to methods of the bcrypt-package. Using hashSync
we get a securely generated and salted password-hash to store in the database. The compareSync
checks if the hash calculated from a given password matches the one stored for the user.
Strategies
To use passport, we need to setup a config first. Our database-config gets some company by the config/passport.js
:
|
|
Now THAT’s a lot of code! Let’s break it down:
Serializing usually means converting an object into a byte stream to store it or save it to memory, database, etc. In passport serializing the user means saving a reference to the user (e.g. the ID) in session-store. When deserializing, we use that stored ID to retrieve the user from the database. So id
in deserializeUser
refers to user.id
in serializeUser
.
PassportJS uses its own session-store independent from the express-session we already implemented, so we can use both at the same time.
Next up we define our registration-strategy. First parameter is the name of the strategy so we can refer to it in other parts of the code. The passReqToCallback
parameter enables passing the request-object in the following callback-function so we can access it in there. We’re not using this feature in this tutorial but it might come in handy in the future, so we’ll just leave it here.
Most of code in the actual strategy should look familiar by now, because it’s basically an extended version of our findOrCreate()
. In case the username already exists, we create a flash-message named signupMessage
in session using req.flash
. If not, we generate a secure password-hash using our newly implemented generateHash
function and save the user to the database.
The login-strategy follows the same general structure as the registration-strategy. We’re trying to find the user in the database by username. If it exists and the provided password is valid, the login is successful. Otherwise store an error message called loginMessage
to be displayed to the user.
Notice: in a lot of other tutorials you will see the use of process.nextTick()
. This is only to demonstrate that asynchronous authentication is possible with passport. It is by no means required, so we’ll just leave it out.
With req.session.username = username
in both strategies we’re making the current username available in session for future use.
Telling the server
Now that we’ve configured passport, we tell our server.js
that we want to use it:
|
|
We include the two packages, then setup passport and tell expressJS to use both of them. It’s crucial to use the passport-session after using the express-session to have it working properly.
New routes!
In our controllers/user.js
we also include passport and replace the existing rules with the following:
|
|
In the GET-routes for login and register we get the respective messages stored via the passport-config and pass it to the template.
Handling POST-requests for login and register is mostly left to our passport-strategies. In here we just define where to go on success or failure.
Actually checking for authentication
Restricting access to a page is implemented by adding an authentication-check to that specific route. Here’s how that looks in the controllers/user.js
:
|
|
We don’t have an index-page for the user just yet, but we’ll get to that in a bit!
The isLoggedIn()
will be called before the callback. We’ll also put it in the controllers/user.js
:
|
|
This is more or less our gatekeeper. It does what the name suggests: checking if a user is currently logged in. If not, it will redirect the request to the login-page.
The layout
We already have a template for our login-page att views/user/login.hbs
but we’ll tweak it a little:
|
|
It checks if it has been passed a message and, if so, displays it. This is using handlebars-syntax; the div will only show up in the rendered markup if a message is provided. We also added a handy link to the registration-form.
What registration-form, you ask? Well, this one right here in views/user/register.hbs
:
|
|
The part where we’re displaying error-messages is handlebars-syntax. The div will only show up in the rendered markup if a message is provided.
To showcase a private page we setup a basic views/user/index.hbs
that will only be displayed if the user is logged in (otherwise our controller will redirect the request to the login-page).
|
|