Learning about sails.js one screencast at a time.

Building an Angular Application in Sails: Ep6 - User Authentication in Angular and Sails.

| Comments

Signup for the Treeline Beta here.

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.



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 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 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, 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, 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… 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…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 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,…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.