1 00:00:00,000 --> 00:00:05,000 [Master Class] [Designer and Developer Workflow] [Creating the User Session] 2 00:00:05,000 --> 00:00:09,000 So now, we've updated our form view where we can have our password 3 00:00:09,000 --> 00:00:12,000 and password confirmation and our properly crypted password 4 00:00:12,000 --> 00:00:14,000 will be stored in the database. 5 00:00:14,000 --> 00:00:18,000 So, we can pretty much register and edit all the users. 6 00:00:18,000 --> 00:00:21,000 So, the next step is to actually log in. 7 00:00:21,000 --> 00:00:25,000 In order to log in, we'll be using a session, and a session is 8 00:00:25,000 --> 00:00:28,000 a model that sort of handles a particular session. 9 00:00:28,000 --> 00:00:32,000 So, when we log in, we'll be creating a new session for a user, 10 00:00:32,000 --> 00:00:34,000 and when we log out, we'll be deleting a session. 11 00:00:34,000 --> 00:00:39,000 So, it works just like our other models where we'll be creating and deleting things. 12 00:00:39,000 --> 00:00:42,000 So, let's go ahead and generate our session. 13 00:00:42,000 --> 00:00:47,000 So, we saw before that there is a generator that's installed with authlogic 14 00:00:47,000 --> 00:00:53,000 called "authlogic session," and basically, we can use this to generate our user session 15 00:00:53,000 --> 00:00:57,000 which is a session that authenticates a user. 16 00:00:57,000 --> 00:01:07,000 So, we'll do "rails generate authlogic:session" 17 00:01:07,000 --> 00:01:13,000 and we'll call this a "UserSession." 18 00:01:13,000 --> 00:01:18,000 So, now it's gone ahead and created a file called "app/models/user_session.rb," 19 00:01:18,000 --> 00:01:25,000 so let's go check that out, and here we are in app/models/user_session.rb. 20 00:01:25,000 --> 00:01:28,000 Now, you'll notice this is a little bit different than our typical model. 21 00:01:28,000 --> 00:01:32,000 It doesn't inherit from ActiveRecord base. 22 00:01:32,000 --> 00:01:35,000 In fact, there is no database table for the user session. 23 00:01:35,000 --> 00:01:38,000 The model itself is represented in the database, 24 00:01:38,000 --> 00:01:42,000 so it can abstract away the actual logging in and logging out. 25 00:01:42,000 --> 00:01:47,000 So instead, it inherits from Authlogic Session Base, 26 00:01:47,000 --> 00:01:50,000 and that class provides us with a lot of functionality, 27 00:01:50,000 --> 00:01:53,000 which will ultimately allow us to log in. 28 00:01:53,000 --> 00:01:56,000 Now, since it's called a user session, it's assuming that 29 00:01:56,000 --> 00:02:00,000 what it's going to be authenticating against is a user class, 30 00:02:00,000 --> 00:02:05,000 which will act as authentic, which we've already gone ahead and set up. 31 00:02:05,000 --> 00:02:08,000 So, those assumptions should be true. 32 00:02:08,000 --> 00:02:13,000 So now, the steps are we're going to create a new controller for handling our authentication, 33 00:02:13,000 --> 00:02:18,000 or handling our actual user sessions, and when we want to log in 34 00:02:18,000 --> 00:02:22,000 we'll call the create method, and we'll generate a new user session 35 00:02:22,000 --> 00:02:26,000 based on the authentication credentials that have been passed. 36 00:02:26,000 --> 00:02:31,000 And if that's successful, then we'll have a new session where somebody is logged in, 37 00:02:31,000 --> 00:02:35,000 and if it's not successful, it will redirect them back to the new user session screen 38 00:02:35,000 --> 00:02:37,000 or the log in screen. 39 00:02:37,000 --> 00:02:41,000 So, let's go ahead and generate a new controller for our user sessions. 40 00:02:41,000 --> 00:02:52,000 So, we'll do "rails generate controller" and we'll call this "UserSessions," 41 00:02:52,000 --> 00:02:58,000 and what I'm going to do is define a method called "new" which will be our log in, 42 00:02:58,000 --> 00:03:02,000 and this will just generate a view for the new field, which is really the only 43 00:03:02,000 --> 00:03:06,000 user visible action that the user sessions will implement. 44 00:03:06,000 --> 00:03:10,000 So, if we run that, it's created a new controller called "user sessions controller," 45 00:03:10,000 --> 00:03:17,000 and it's created a new view file under views/user_sessions/new.html.haml, 46 00:03:17,000 --> 00:03:21,000 and that new .html file will be our log in page. 47 00:03:21,000 --> 00:03:27,000 We'll go ahead and adjust our routes later so the route will look like /login. 48 00:03:27,000 --> 00:03:30,000 So, let's go to our new user sessions controller because 49 00:03:30,000 --> 00:03:32,000 it's just generated an empty controller. 50 00:03:32,000 --> 00:03:37,000 What we have to do is now add the logic that we want to add to it. 51 00:03:37,000 --> 00:03:42,000 So, let's go ahead, open up controllers, user session controller. 52 00:03:42,000 --> 00:03:47,000 So, here it's generated a new method stub, and this is what's going to be called 53 00:03:47,000 --> 00:03:52,000 when we go to usersessions/new, and what we need to do is 54 00:03:52,000 --> 00:03:58,000 instantiate a new user session, and this will be useful because in our form 55 00:03:58,000 --> 00:04:03,000 our user session instance is actually what we're going to be basing our form off of. 56 00:04:03,000 --> 00:04:06,000 So, since we have a real object of the user session, 57 00:04:06,000 --> 00:04:10,000 it allows us to treat it just like any other model in our application. 58 00:04:10,000 --> 00:04:20,000 So, we'll just go ahead and say "@user_session = UserSession.new." 59 00:04:20,000 --> 00:04:28,000 Then our new .html view will render and display an empty form for user session. 60 00:04:28,000 --> 00:04:32,000 So, when that form submits or they hit log in and it submits, 61 00:04:32,000 --> 00:04:35,000 that's going to go to create. 62 00:04:35,000 --> 00:04:39,000 And this is the method where we're actually going to do our authentication. 63 00:04:39,000 --> 00:04:43,000 So, in this create method is actually where we're going to do our authentication. 64 00:04:43,000 --> 00:04:46,000 So, the first step is to get a user session instance. 65 00:04:46,000 --> 00:04:48,000 This will be a lot like our new method. 66 00:04:48,000 --> 00:04:52,000 We'll do "UserSession.new." 67 00:04:52,000 --> 00:04:56,000 However, this time we've been passed in parameters, so we want to instantiate 68 00:04:56,000 --> 00:05:01,000 our user session with the, let's say, email and password that were submitted. 69 00:05:01,000 --> 00:05:09,000 So, to do that we'll just pass in (params[user_session]). 70 00:05:09,000 --> 00:05:13,000 Now, this just looks like any other create method where we instantiate a new object 71 00:05:13,000 --> 00:05:16,000 based on the parameters, and that's what's really great about authlogic 72 00:05:16,000 --> 00:05:21,000 is it really has the same sort of pattern as any other model. 73 00:05:21,000 --> 00:05:24,000 So, the next step is where the real magic happens. 74 00:05:24,000 --> 00:05:26,000 We're going to try to save this user session. 75 00:05:26,000 --> 00:05:33,000 So, we'll say "if @user_session.save," 76 00:05:33,000 --> 00:05:37,000 and then there's going to be an else and end here. 77 00:05:37,000 --> 00:05:42,000 So, when we try to save a user session, what authlogic does is it takes the information 78 00:05:42,000 --> 00:05:47,000 that it was passed, basically our email and password, and it tried to find a user 79 00:05:47,000 --> 00:05:50,000 who matches those credentials. 80 00:05:50,000 --> 00:05:55,000 If it finds one and we save it, it will go ahead and say that we are authenticated 81 00:05:55,000 --> 00:06:00,000 and return true, and it will set the cookie saying this person is logged in 82 00:06:00,000 --> 00:06:05,000 so we can handle our true session here, so at this point they are logged in. 83 00:06:05,000 --> 00:06:09,000 So, why don't we go ahead and just leave a flash here, 84 00:06:09,000 --> 00:06:11,000 and this is just a notice at the top of the page, 85 00:06:11,000 --> 00:06:19,000 so we'll say "flash(notice) = "Welcome to EasyJobs" 86 00:06:19,000 --> 00:06:21,000 or something along those lines. 87 00:06:21,000 --> 00:06:23,000 We can go ahead and change this easily later. 88 00:06:23,000 --> 00:06:25,000 So, if we're logged in, we want to go ahead and redirect them somewhere. 89 00:06:25,000 --> 00:06:33,000 So, we'll just say "redirect_to jobs_url." 90 00:06:33,000 --> 00:06:36,000 Now, in this else clause here, we were not able to save our user session, 91 00:06:36,000 --> 00:06:41,000 which means that based on the information that was passed in via params, 92 00:06:41,000 --> 00:06:43,000 which I just realized I misspelled. 93 00:06:43,000 --> 00:06:45,000 It is not "parms." 94 00:06:45,000 --> 00:06:47,000 There we go. 95 00:06:47,000 --> 00:06:51,000 Based on that information, we are not able to log in, so what we want to do is actually 96 00:06:51,000 --> 00:06:54,000 go ahead and just render our new form again. 97 00:06:54,000 --> 00:07:01,000 So, we'll say "render action of new" and what this does is 98 00:07:01,000 --> 00:07:06,000 since we have our @user_session set with the information that was submitted 99 00:07:06,000 --> 00:07:12,000 when we render this new action here, it will actually prepopulate the email field 100 00:07:12,000 --> 00:07:14,000 with what they pass through. 101 00:07:14,000 --> 00:07:17,000 The actual password field should be blank because we don't want to pass 102 00:07:17,000 --> 00:07:21,000 the password back to them, but it's a nice little tip that we will be able to give 103 00:07:21,000 --> 00:07:25,000 to the user that you don't have to enter your email again, or you can review it. 104 00:07:25,000 --> 00:07:27,000 So, this handles our logging in. 105 00:07:27,000 --> 00:07:31,000 Basically, with the user session we just pass it some information like email and password. 106 00:07:31,000 --> 00:07:34,000 If it saves, they're logged in. 107 00:07:34,000 --> 00:07:40,000 If not, they need to try again, and destroying is pretty much the same thing. 108 00:07:40,000 --> 00:07:47,000 When we log out, we want to go ahead and destroy this user session. 109 00:07:47,000 --> 00:07:50,000 So, we'll define destroy. 110 00:07:50,000 --> 00:07:54,000 So, how do we find our current user session? 111 00:07:54,000 --> 00:07:57,000 Well, this is code that we're going to need throughout the entire application. 112 00:07:57,000 --> 00:08:00,000 We're going to need to be able to search for the current user session 113 00:08:00,000 --> 00:08:06,000 and the current user in order to, for instance, display who's logged in, 114 00:08:06,000 --> 00:08:10,000 to perform authorization checks to see if they're allowed to perform certain actions. 115 00:08:10,000 --> 00:08:13,000 So, what we're going to do is write different methods, and we'll save them 116 00:08:13,000 --> 00:08:17,000 in our application controller so they're available to us at any place in our application. 117 00:08:17,000 --> 00:08:21,000 So, we're going to take a break from writing our user sessions controller 118 00:08:21,000 --> 00:08:25,000 to go to our application controller, and our application controller 119 00:08:25,000 --> 00:08:30,000 is in app, controllers, application controller. 120 00:08:30,000 --> 00:08:33,000 So here, we're going to define two private methods. 121 00:08:33,000 --> 00:08:38,000 So, we'll define private and down here is where we'll define our method. 122 00:08:38,000 --> 00:08:46,000 The first method we want to do is current user session, "current_user_session." 123 00:08:46,000 --> 00:08:52,000 And we'll define another method which will be current user, 124 00:08:52,000 --> 00:08:56,000 and where current session will actually retrieve the current user session 125 00:08:56,000 --> 00:09:00,000 more often we actually want the user that's associated with the session, 126 00:09:00,000 --> 00:09:04,000 so this convenience method "current_user" will provide that. 127 00:09:04,000 --> 00:09:08,000 Now, the way we can get the current user session is by using the user session 128 00:09:08,000 --> 00:09:13,000 class method called "find," and authlogic will use its ways to find out 129 00:09:13,000 --> 00:09:18,000 if there is a current user session and return that instance to you if it exists, otherwise no. 130 00:09:18,000 --> 00:09:23,000 Now, while this will technically work, we may be calling current user session 131 00:09:23,000 --> 00:09:26,000 multiple times in our request. 132 00:09:26,000 --> 00:09:29,000 So, what we want to do is make sure we're not calling user.find all the time. 133 00:09:29,000 --> 00:09:34,000 Instead, we want to cache or memoize the result into an instance variable 134 00:09:34,000 --> 00:09:37,000 so we're not calling user.find over and over again. 135 00:09:37,000 --> 00:09:47,000 So, the first thing we'll do is we'll say "@current_user_session = UserSession.find." 136 00:09:47,000 --> 00:09:51,000 So, after the first time we've called it, @current_user_session is defined. 137 00:09:51,000 --> 00:09:55,000 So, if we call it a second time, what we want to do is check if @current_user_session 138 00:09:55,000 --> 00:10:01,000 has been defined and if so, we want to return that since we already did the find call on it. 139 00:10:01,000 --> 00:10:06,000 We can just return the cached result, otherwise we'll actually want to do 140 00:10:06,000 --> 00:10:09,000 the User.Session find here. 141 00:10:09,000 --> 00:10:16,000 So, what we'll do is we'll say "return @current_user_session" 142 00:10:16,000 --> 00:10:26,000 if we have current user session defined, so we'll do "defined?(@current_user_session). 143 00:10:26,000 --> 00:10:29,000 Another reason we can't just say "return @current_user_session 144 00:10:29,000 --> 00:10:33,000 if @current_user_session" is because if they are logged out, 145 00:10:33,000 --> 00:10:40,000 current_user_session will be nil and it will try to reevaluate User.Session.find every time 146 00:10:40,000 --> 00:10:42,000 if somebody's logged out. 147 00:10:42,000 --> 00:10:45,000 So, this is just a way of us caching so don't have to call User.Session.find 148 00:10:45,000 --> 00:10:49,000 over and over again if we call current user session over and over again. 149 00:10:49,000 --> 00:10:51,000 Now, let's say we want to get the current user. 150 00:10:51,000 --> 00:10:54,000 Well, to do that we will need to get the current user session, 151 00:10:54,000 --> 00:10:59,000 so we can just type in "current_user_session" and call that method, 152 00:10:59,000 --> 00:11:02,000 and we can call ".user" on it. 153 00:11:02,000 --> 00:11:05,000 Now remember, if somebody's logged out, current user session will be null 154 00:11:05,000 --> 00:11:09,000 and if we try to call user on that, well, we're going to get an error. 155 00:11:09,000 --> 00:11:14,000 So, what we need to do is check if current user session is true 156 00:11:14,000 --> 00:11:17,000 or a truthy value or just not nil. 157 00:11:17,000 --> 00:11:21,000 So, by using this here, we'll say "current_user_session," make sure it's true. 158 00:11:21,000 --> 00:11:24,000 If it is, then we'll call "current_user_session.user." 159 00:11:24,000 --> 00:11:27,000 Now, if current user session is false or they're logged out, 160 00:11:27,000 --> 00:11:32,000 when this evaluates to nil, or false rather, it will not evaluate this side 161 00:11:32,000 --> 00:11:35,000 so we won't get an error. 162 00:11:35,000 --> 00:11:38,000 Now you can see that we've called current user session twice in quick succession, 163 00:11:38,000 --> 00:11:41,000 which is why this caching is important. 164 00:11:41,000 --> 00:11:43,000 Again, we don't want to continue doing this over and over again, 165 00:11:43,000 --> 00:11:46,000 so let's implement the same sort of caching, so let's implement 166 00:11:46,000 --> 00:11:49,000 the same sort of value caching here that we did here. 167 00:11:49,000 --> 00:11:57,000 So, instead of just returning, we'll return it while setting it to current user, 168 00:11:57,000 --> 00:11:59,000 and then before we try to do that, we'll say 169 00:11:59,000 --> 00:12:08,000 "return @current_user if defined?(@current_user)." 170 00:12:08,000 --> 00:12:11,000 So now, from anywhere in our controller we could type in "current user," 171 00:12:11,000 --> 00:12:15,000 and if somebody's logged in, we'll get a user instance of the person who's logged in, 172 00:12:15,000 --> 00:12:18,000 and if they're not logged in, then we'll get nil. 173 00:12:18,000 --> 00:12:21,000 We could do the same thing for current user session if we actually wanted 174 00:12:21,000 --> 00:12:25,000 to get the session which we actually do in our log out method. 175 00:12:25,000 --> 00:12:33,000 Now, before I switch away from this, there is one last thing I want to do. 176 00:12:33,000 --> 00:12:39,000 So, we'll call helper method and pass it the symbol "current_user_session" 177 00:12:39,000 --> 00:12:43,000 and "current_user." 178 00:12:43,000 --> 00:12:46,000 What this does is it makes the methods current user session and current user 179 00:12:46,000 --> 00:12:51,000 available in our views as helper methods because quite often in our views 180 00:12:51,000 --> 00:12:53,000 we're going to want to access current user. 181 00:12:53,000 --> 00:12:56,000 So now, we can go ahead and save that out. 182 00:12:56,000 --> 00:12:59,000 So, let's just hop back to our user sessions controller 183 00:12:59,000 --> 00:13:01,000 to see if we can't implement destroy now. 184 00:13:01,000 --> 00:13:11,000 Well, now, this is as simple as doing "current_user_session.destroy." 185 00:13:11,000 --> 00:13:15,000 Now, you'll realize that if somebody's logged out already then current user session 186 00:13:15,000 --> 00:13:20,000 will return null and trying to call destroy on it will cause an error. 187 00:13:20,000 --> 00:13:22,000 Now, there's a couple of different ways we could solve this. 188 00:13:22,000 --> 00:13:28,000 The most correct, in my opinion, way to do this would be to restrict the destroy method 189 00:13:28,000 --> 00:13:32,000 so it's only visible to people who are logged in, and that will happen 190 00:13:32,000 --> 00:13:35,000 when we start doing our authorization. 191 00:13:35,000 --> 00:13:38,000 Right now what I want to do is just to make sure we don't get this error, 192 00:13:38,000 --> 00:13:46,000 I'm going to call "current_user_session && current_user_session.destroy." 193 00:13:46,000 --> 00:13:51,000 That way, if there is no current user session, the destroy method will not be called. 194 00:13:51,000 --> 00:13:53,000 No harm, no foul. 195 00:13:53,000 --> 00:13:59,000 It will be a little bit weird to log out while you're logged out, but right now it avoids the error. 196 00:13:59,000 --> 00:14:04,000 So, then I'm just going to go ahead and just type in "flash[:notice]" 197 00:14:04,000 --> 00:14:11,000 and just say "You are now logged out." 198 00:14:11,000 --> 00:14:18,000 And let's go ahead and just redirect to the jobs url again, the home page. 199 00:14:18,000 --> 00:14:21,000 So, now we've got the controllers and models for our session set up. 200 00:14:21,000 --> 00:14:25,000 We have a user session which handles all of the logic of logging in 201 00:14:25,000 --> 00:14:27,000 and authenticating the user. 202 00:14:27,000 --> 00:14:30,000 We have the user sessions controller which will ultimately 203 00:14:30,000 --> 00:14:32,000 be our log in and log out controller. 204 00:14:32,000 --> 00:14:36,000 We still need to create the views for it, and in our application controller 205 00:14:36,000 --> 00:14:39,000 we've created a couple of methods that will allow us to get the current session 206 00:14:39,000 --> 00:14:41,000 and the current user. 207 00:14:41,000 --> 00:14:44,000 So, up next we're going to go ahead and jump towards the front end 208 00:14:44,000 --> 00:14:48,000 and create our log in form and some information that will be displayed 209 00:14:48,000 --> 00:14:51,000 when you're logged in or when you're logged out.