Signup for the Treeline Beta here.
Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
Transcript
Howdy.
In the last episode we finished out the signup page. We now want to take that information and allow users to login, better known as user authentication. So far we’ve created one Angular module for the signup page connected to our Sails API. And this is under a broader category of public pages or those pages that don’t require a user to be logged in to see them. We also need a homepage that will also be public. Finally we need a view, I’m going to call the dashboard that will require that the user be logged in to our back-end.
So we have three Angular modules for activityOverlordv2.0. You can of course accomplish the same end result in a variety of different ways, but I’ve chosen this approach for simplicity and ease of SEO.
Ok, let’s start with the homepage.
We’ll first replace the current mark-up in homepage.ejs within the view folder with this new mark-up. The markup contains our top level navigation, which includes the sign in button that triggers a PUT
request to /login
, as well as a sign-up button that triggers a GET
request to /signup
. I realize I’m going through this pretty fast but don’t despair, I have a repo that has all of the code for your review.
Let’s look at the Angular module and controller for the homepage. The homepage and signup module’s are identical both inject the toastr and compareTo services. The homepage controller adds a new method to the scope called submitLoginForm
. Similar to signup form, submitLoginForm
initially sets the loading state to true
. We then make a PUT
request to /login
passing in an email
field as well as a password
field from the $scope object. If the request is successful, we’ll make the equivalent of the a GET
request to /
. If we’re unsuccessful we’ll handle the known errors, in this case a bad email/password combination through the toastr
service and return. We’ll also handle any unexpected error here. Finally, in either case we’re toggle the loginForm
loading state back to false.
So now that we have the front-end set up for the Homepage
, let’s switch gears to the back-end Sails API.
The first thing I want to do is handle the PUT
request to /login
. So I’ll add that route
to the routes.js
file here. We want this to trigger a login
action in our UserControlller
. Next, we’ll head over to our user controller in UserController.js
. So let’s add the add the login
action. The next block of code I’ll add first takes the email address that was passed from the login form and searches our user
model for an existing user. If it doesn’t find one we’ll send back a 404
status which will trigger the toastr
message in the HomePageController. Next, we’ll be using another machinepack, called machinepack-passwords. Within the machinepack we’ll use the checkPassword
machine passing in the user provided password from the login form as well as the encrypted password found by the findOne
method of our user
model. Within this machine
we handle any unexpected error first and then if the passwords
don’t match we again send a 404 status
via res.notFound()
. For reference, these responses can be found in the responses folder here. If we get a match, we need to log the user in so we’ll create a new parameter on the session via req.session.me
and give it the value of the id
of the user we found earlier with findOne
method of the user
model. The last thing we need to do is let the front-end know everything went well and we do that by returning with res.ok()
.
Let’s see all of this in action. I’ll start Sails with sails lift
and we’ve got an issue here. I know what it is because I’m old and forgetful. So, I’m going to go into the routes.js
file and you’ll see that I just missed a comma here. So let’s go back and try that again. Okay, Sails is up and running, let’s go to the browser and let’s hit localhost:1337
. Okay, so the reason we’re getting a 404
not found is we don’t have a route that handles a GET
request to /
. Now we could remedy this very quickly by going back to the routes.js
file, and we’ll create a route here, that handles a GET
request to /
, which will trigger the homepage and we’ll restart Sails here…go back into the browser and refresh and ther’s our homepage and that’s great. But what I really want is a way to marshall between whether a user is authenticated, that is, logged in, and if they’re logged in I want to display one front end and if they’re not logged in a different front end. I can do that by adding a page controller to Sails. So let’s do that. First, I’ll add the controller via sails generate controller page
. Now let’s go back into Atom and we have our PageController
here which is empty. I’m going to add an action here showHomePage
.
We first check if the user is logged in via the me
parameter on the session, so, if req.session.me
does not exist, I want to load the homepage and I’m going to do that by return res.view('homepage')
. Otherwise if the user is logged in, I’m going to use the findOne
method of the user
model to look for that user. So I’ll pass in the userId
which is the value of the me
parameter on the session. First I’ll look for an unexpected error here and I’ll handle that with res.negotiate
if it exists. If I don’t find a user, I’m going to return the user to the homepage. Now if I do find a user, I’m going to return a view that we haven’t created yet called dashboard
. I’m going to bootstrap some information that I found via the findOne
method of the user model on the page itself.
Before we create the dashboard view you may be asking what’s going to trigger this showHomePage
action. Well, we’re going to do that by going into, routes.js
and substituting this current view with our PageController
and our PageController
action showHomePage
. So now any time there’s a GET
request to /
our page controller will marshall which front end the user receives.
Now let’s implement our dashboard page. The mark-up for the dashboard page is straightforward. As with the signup and homepage views, the dashboard view will have some top level navigation. The part I want to concentrate on is the bootstrapped data we get from the page controller’s showHomePage
action. So using ejs
we’ll add the parameters of the me
object from the showHOmePage
action on to window.SAILS_LOCALS
.
Next, let’s add the very minimal dashboard module and controller. I’ve added these files under a separate folder named private
to distinguish between the Angular code that will be executed when a user is authenticated and code that is public and does not require authentication.
I want to ensure that the new Angular modules I’ve added will be loaded before any controllers, so I’ll go back to pipeline.js
found in the tasks
folder and add the path to the two new modules — HomepageModule
and `DashboardModule’. This will insure that the modules will be loaded into the page first.
Okay, let’s see this all in action. So I’ll start Sails, with sails lift
, and then navigate my browser to localhost:1337
. In the previous episode we created an account, nikola@tesla.com
with the password 123456
and I’m going to use that now. Now if you don’t have that user created, no big deal, just click on the sign up now button and create that user, nikola@tesla.com
with the password 123456
, then go back to localhost:1337
and start from there…and we have our dashboard page. And if we view the page source, we can see that we have our bootstrap user right here on the page. So let’s go back to the PageController
really quickly here…req.session.me
existed, we used that with the findOne
method of the user
model…it found the user and then passed the user object via the me
parameter and bootstrapped it onto the dashboard page.
So there are a couple of things we still need to implement here. We have this sign-out link, that if i click on it it’s going to give us a 404
and that’s because we don’t have a route that handles that link…so this link is making a GET
request to /logout
and we don’t have a route for that so let’s take care of that first. So now that GET
request to /logout
will be tied to or will trigger the UserController
and this new action on the controller. So let’s go up to UserController
and let’s add a logout
action.
So similar to our login action we’re going to look for the user id on req.session.me
…we’re going to pass that to the findOne
method of our user
model…we’re going to handle the errors if they exist and if it finds a user, we’re going to essentially log that user out by making req.session.me
equal to null
. Then we’re going to return with a new response, backToHomePage()
. So let’s take a look at that response.
So this response is basically for convenience…if logout were an AJAX request, Sails will know that that request just wants a JSON status code response. In our case we’re making a GET
request via the browser, so we just want to redirect back to /
and let our page controller handle it. Okay let’s see this in action.
So I’ll go and restart Sails, and we’ll go back and log-in. So now when I hit the signout link the PageController
sends me back to the homepage.
There’s just a couple of other things we need to do. Within the UserController
, signup
action we want the user to be logged in after they’re signed up. So we’ll do that right here. And within the signup page controller, on a successful signup we made a GET
request via the browser to /user
and now we just want to ake that /
.
Let’s go ahead and restart one more time. And we’ll now go to signup, and I’ll create a new user b
, b@b.com
…and everything works. So the user was created and logged-in…and let’s take a look at the page source…and there we are there’s our bootstrapped data.
Thanks for making it through the many voices of Sailscasts. Although it might seem that there are different Irl’s on this Sailscast, I assure you that it was the same ole guy throughout…al beit Allergy-challenged. So now we know how to create a user, save it to a database using the Sails ORM, and then use that information to authenticate a user marshalling between different Angular front-ends depending upon the user’s log-in status.
There’s been quite a bit going on in Sails and Treeline so expect an update shortly via the Sailscasts mailing list. If you’re not signed up, head over here to subscribe and you can always follow me on Twitter here.