1 00:00:00,480 --> 00:00:01,630 Sending usernames and 2 00:00:01,630 --> 00:00:05,440 passwords in plain text across the wire isn't the best solution? 3 00:00:05,440 --> 00:00:08,320 I know I don't feel safe doing it and I'm sure you don't either. 4 00:00:08,320 --> 00:00:12,580 So let's give our users a safer way of sending most of their requests 5 00:00:12,580 --> 00:00:15,310 by creating a signed token that they can give back to us 6 00:00:15,310 --> 00:00:17,080 to prove they are who they said they are. 7 00:00:18,260 --> 00:00:20,820 So, I think before we start on tokens, 8 00:00:20,820 --> 00:00:23,280 I wanna make sure that I can still get a list of courses. 9 00:00:23,280 --> 00:00:26,332 So let's run the app. 10 00:00:26,332 --> 00:00:29,310 Okay, cool. 11 00:00:29,310 --> 00:00:35,830 And if I come over here, and I do a get on, users won't work. 12 00:00:35,830 --> 00:00:37,630 So we'll do one on courses. 13 00:00:39,830 --> 00:00:41,940 Send that, we get no courses, of course. 14 00:00:42,970 --> 00:00:44,280 Let's try to post one. 15 00:00:48,557 --> 00:00:50,138 Let's dump all of these. 16 00:00:50,138 --> 00:00:54,150 We'll send the new one here, we'll say that title 17 00:00:55,440 --> 00:00:59,195 is Django Basics and we'll say the URL is 18 00:00:59,195 --> 00:01:05,150 https://teamtreehouse.com/library/django-- basics. 19 00:01:05,150 --> 00:01:10,700 Can you tell which things I know the location of the best? 20 00:01:10,700 --> 00:01:12,950 All right so let's try to post that. 21 00:01:14,060 --> 00:01:17,560 And we get back nothing, right? 22 00:01:17,560 --> 00:01:19,470 It didn't actually work. 23 00:01:19,470 --> 00:01:20,220 It didn't go anywhere. 24 00:01:21,300 --> 00:01:22,360 We get back to 200 okay. 25 00:01:22,360 --> 00:01:23,750 Which is kind of funny. 26 00:01:23,750 --> 00:01:25,360 That we get back to 200 okay. 27 00:01:25,360 --> 00:01:27,000 It should not have done anything. 28 00:01:27,000 --> 00:01:28,430 If I was to do a get on this. 29 00:01:29,790 --> 00:01:30,670 I should see nothing. 30 00:01:30,670 --> 00:01:31,170 Okay. 31 00:01:32,220 --> 00:01:36,820 So what if we go ahead and send it through some auth. 32 00:01:36,820 --> 00:01:38,030 So let's do basic auth. 33 00:01:38,030 --> 00:01:40,370 User name is Kenneth Love. 34 00:01:40,370 --> 00:01:44,730 Password is password like we said before and I'll send that. 35 00:01:44,730 --> 00:01:48,240 And now notice I get back actual data, so that's pretty cool. 36 00:01:48,240 --> 00:01:50,510 I don't get this unauthorized thing. 37 00:01:50,510 --> 00:01:52,545 But it didn't let me do anything. 38 00:01:52,545 --> 00:01:53,895 Nothing came through. 39 00:01:53,895 --> 00:01:57,895 For doing tokens you might be expecting that we need to install 40 00:01:57,895 --> 00:01:59,455 another package and all this stuff. 41 00:01:59,455 --> 00:02:01,205 But we actually don't need one. 42 00:02:01,205 --> 00:02:03,295 Flask has a dependency called it's dangerous. 43 00:02:03,295 --> 00:02:05,935 Which does all of the token work for me. 44 00:02:05,935 --> 00:02:08,575 These tokens are actually known as JSON web tokens. 45 00:02:08,575 --> 00:02:11,835 I'm gonna link to a really great breakdown of them in the teacher's notes. 46 00:02:11,835 --> 00:02:14,950 And also of course the docs for it's dangerous. 47 00:02:14,950 --> 00:02:18,580 Before I generate the tokens, though, I want to set up the token auth. 48 00:02:18,580 --> 00:02:21,060 So I'm gonna add all of that into auth.py. 49 00:02:21,060 --> 00:02:24,030 We've gotta add two more imports here. 50 00:02:24,030 --> 00:02:29,040 We're gonna import HTTPTokenAuth, and we're going to import MultiAuth. 51 00:02:29,040 --> 00:02:36,110 So just like with basic_auth, we're gonna do token_auth = HTTPTokenAuth. 52 00:02:36,110 --> 00:02:40,360 And I'm gonna to enter a scheme here that's going to be called token. 53 00:02:40,360 --> 00:02:46,190 Schemes are kind of like domains, areas where a thing is appropriate or applies. 54 00:02:46,190 --> 00:02:52,930 When we're doing this basic auth, if you were to look at, sorry not there, 55 00:02:52,930 --> 00:02:57,050 headers you would see this authorization thing here has the basic. 56 00:02:57,050 --> 00:02:59,940 That's the realm or the scheme is basic. 57 00:02:59,940 --> 00:03:02,480 So for us we're going to call it token. 58 00:03:02,480 --> 00:03:05,060 And then we're going to make instead of basic auth here, 59 00:03:05,060 --> 00:03:07,170 we're going to do multi auth. 60 00:03:07,170 --> 00:03:11,440 And we're going to this is made up of token_auth and basic_auth. 61 00:03:11,440 --> 00:03:17,800 Kind of the reverse of the location thing for the APIs MultiAuth looks 62 00:03:17,800 --> 00:03:22,520 at the first thing first so it tries token_auth first and then it tries basic_auth. 63 00:03:22,520 --> 00:03:24,746 And if you don't want to use token you can leave this blank and 64 00:03:24,746 --> 00:03:29,320 I do believe it defaults to bearer and then there's others you can set that, 65 00:03:29,320 --> 00:03:32,930 maybe a certain client expects a certain thing, okay. 66 00:03:32,930 --> 00:03:35,120 So when I have both types of auth I want to have that all set up. 67 00:03:35,120 --> 00:03:38,880 Now I do you have to tell the token_auth 68 00:03:38,880 --> 00:03:43,600 how to verify a token just like I had to tell basic off how to verify password. 69 00:03:43,600 --> 00:03:46,858 So let me write that function real quick. 70 00:03:46,858 --> 00:03:51,310 So, token_auth.verify_token, 71 00:03:51,310 --> 00:03:55,305 verify_token, and we're gonna get a token. 72 00:03:55,305 --> 00:04:02,340 So user = models.User.verify_auth_token, and we're gonna pass in the token. 73 00:04:03,600 --> 00:04:06,870 If user is not none, so if we get back a user and 74 00:04:06,870 --> 00:04:11,610 it's not none then g.user = user and we gonna return True. 75 00:04:13,080 --> 00:04:15,170 Otherwise we're gonna return False. 76 00:04:16,730 --> 00:04:20,500 Sorry you probably thought that function was going to be more interesting. 77 00:04:20,500 --> 00:04:24,030 It is actually pretty interesting though we have to go add this verify 78 00:04:24,030 --> 00:04:27,150 auth token though to actually make it interesting. 79 00:04:27,150 --> 00:04:29,460 So let's hop over here to our models.py. 80 00:04:29,460 --> 00:04:32,660 And this is where we're gonna create this new method. 81 00:04:32,660 --> 00:04:34,730 So first of all up here. 82 00:04:34,730 --> 00:04:37,506 We have to add from it's dangerous. 83 00:04:37,506 --> 00:04:38,846 And we're gonna import a couple of things here. 84 00:04:38,846 --> 00:04:43,036 We're gonna import TimedJSONWebSignatureSerializer, and 85 00:04:43,036 --> 00:04:48,335 we're going to call that Serializer, because I do not want to type that. 86 00:04:48,335 --> 00:04:55,064 And then we're also going to import BadSignature, and SignatureExpired. 87 00:04:56,477 --> 00:05:00,477 All right, so now inside of class User which this is one of the more 88 00:05:00,477 --> 00:05:04,377 interesting classes I think I've ever built here here at Treehouse, 89 00:05:04,377 --> 00:05:07,387 I'm gonna add another static method down here. 90 00:05:07,387 --> 00:05:12,367 And I'll add it above this one, just so that static method, 91 00:05:12,367 --> 00:05:15,577 set password, and verify password can stay next to each other. 92 00:05:15,577 --> 00:05:16,817 So, static method. 93 00:05:18,480 --> 00:05:20,460 There is no underscore in that, there we go. 94 00:05:20,460 --> 00:05:23,920 And we're going to call this verify_auth_token, and 95 00:05:23,920 --> 00:05:27,100 we're gonna pass in the token. 96 00:05:27,100 --> 00:05:32,820 So, serializer = Serializer(), and this is where we need to 97 00:05:32,820 --> 00:05:37,761 do config.SECRET_KEY, cuz remember we got our SECRET_KEY over here. 98 00:05:39,860 --> 00:05:47,268 And so we're going to try, data equals serializer.loads(token) and, 99 00:05:47,268 --> 00:05:52,518 with the exception of, SignatureExpired, 100 00:05:52,518 --> 00:05:58,330 BadSignature. 101 00:05:58,330 --> 00:05:59,120 We're gonna return None. 102 00:06:01,120 --> 00:06:05,460 Otherwise user is equal to User.get. 103 00:06:05,460 --> 00:06:08,646 User.id is equal to data['id']. 104 00:06:08,646 --> 00:06:10,546 Cuz data's gonna have a key in it named id. 105 00:06:10,546 --> 00:06:13,059 And we're gonna return that user, and 106 00:06:13,059 --> 00:06:18,011 remember like here we are returning this user, and if we look over here this is 107 00:06:18,011 --> 00:06:22,160 going to get a user so that user ends up being right there. 108 00:06:22,160 --> 00:06:27,390 Otherwise we are returning None, because right here we return None. 109 00:06:27,390 --> 00:06:29,030 That's why we check against None. 110 00:06:29,030 --> 00:06:31,990 So now we actually need to make the token. 111 00:06:31,990 --> 00:06:34,940 So this is pretty straight forward. 112 00:06:34,940 --> 00:06:37,240 Thankfully I'm gonna put this one down here, 113 00:06:37,240 --> 00:06:39,690 because it has to deal with a particular user. 114 00:06:39,690 --> 00:06:43,310 So, we'll say generate_auth_token. 115 00:06:43,310 --> 00:06:45,700 Self, and then when it should expire. 116 00:06:45,700 --> 00:06:48,340 By default I'm gonna set it to 3600 seconds. 117 00:06:48,340 --> 00:06:50,030 So, one hour. 118 00:06:50,030 --> 00:06:53,730 So, this one's a timed one, you can do a non timed one or you can make your 119 00:06:53,730 --> 00:06:59,212 expiration super huge, like it expires in a month or a year or something like that. 120 00:06:59,212 --> 00:07:04,620 So, serializer = Serializer(SECRET_KEY), 121 00:07:04,620 --> 00:07:08,393 which should come from config, and 122 00:07:08,393 --> 00:07:14,290 expires_in is equal to this expires argument. 123 00:07:14,290 --> 00:07:17,249 And we're going to return serializer.dumps, and 124 00:07:17,249 --> 00:07:24,060 we're gonna set the key of id to the id of the user that this being called on. 125 00:07:24,060 --> 00:07:26,340 So this is pretty similar to the verification method, 126 00:07:26,340 --> 00:07:27,860 just the inverse of it. 127 00:07:27,860 --> 00:07:30,210 Or make a serializer instance with the secret key. 128 00:07:30,210 --> 00:07:33,240 I want the tokens to expire all that and 129 00:07:33,240 --> 00:07:35,480 like I said I'll link to the it's dangerous notes. 130 00:07:35,480 --> 00:07:37,990 So you can see more about these serializers. 131 00:07:37,990 --> 00:07:42,146 Something else to point out is that if you've used the JSON module before this 132 00:07:42,146 --> 00:07:46,720 .loads and this .dump should be pretty similar, it's actually load string and 133 00:07:46,720 --> 00:07:50,950 dump string so, very close to the JSON module. 134 00:07:50,950 --> 00:07:53,520 Okay there's a lot to do and 135 00:07:53,520 --> 00:07:56,400 I still don't have a way to actually give a user her key. 136 00:07:56,400 --> 00:07:57,720 So how we do that? 137 00:07:57,720 --> 00:08:00,700 I'm actually gonna do that over here in app.py. 138 00:08:00,700 --> 00:08:05,930 I could do this on the user resource but I kind of want to do this in here to 139 00:08:05,930 --> 00:08:10,970 show you how to add end points that aren't part of resources, but look like they are. 140 00:08:10,970 --> 00:08:16,520 And also just because it feels kind of odd doing this on the user resource. 141 00:08:16,520 --> 00:08:24,350 All right, so I need to import g and jsonify and then from auth import auth. 142 00:08:25,950 --> 00:08:29,833 And then, we're gonna to make a new route down here. 143 00:08:29,833 --> 00:08:36,180 So app.route, api/v1/users/token, 144 00:08:36,180 --> 00:08:41,430 methods equals, I'm only gonna allow one method, which is GET. 145 00:08:41,430 --> 00:08:44,510 And then this is also login_required. 146 00:08:44,510 --> 00:08:49,467 And we're gonna get_auth_token and we're gonna 147 00:08:49,467 --> 00:08:54,440 say the token equals g.user.generate_auth_token. 148 00:08:54,440 --> 00:09:03,208 And we're gonna return jsonify({'token': token.decode('ascii')}). 149 00:09:03,208 --> 00:09:04,979 Cool. 150 00:09:04,979 --> 00:09:09,040 So we have to generate the token on the user. 151 00:09:09,040 --> 00:09:11,090 And we're gonna send it back as a JSON response. 152 00:09:11,090 --> 00:09:12,050 Hence the JSONify. 153 00:09:12,050 --> 00:09:18,230 We have to decode it into ascii first, just to make it safe to send across. 154 00:09:18,230 --> 00:09:20,080 Of course we want to make this a login required. 155 00:09:20,080 --> 00:09:23,720 Because we have to actually have a user to give the token to. 156 00:09:23,720 --> 00:09:25,524 All right. 157 00:09:25,524 --> 00:09:28,970 So, I should already be logged in from creating this. 158 00:09:28,970 --> 00:09:37,700 So, I should be able to go to GET, and users/token, hit send. 159 00:09:37,700 --> 00:09:39,000 And there's my token. 160 00:09:39,000 --> 00:09:40,770 So that's pretty cool. 161 00:09:40,770 --> 00:09:42,761 And now I can grab this. 162 00:09:42,761 --> 00:09:46,660 I can go over here to authorization, say No Auth. 163 00:09:48,210 --> 00:09:54,200 Go to headers, change this here to token and paste in my key right there. 164 00:09:55,480 --> 00:09:58,966 And now, I've got my token which is cool. 165 00:09:58,966 --> 00:10:00,307 And my token didn't change, 166 00:10:00,307 --> 00:10:03,536 if you notice my token stayed the same here when I requested that again. 167 00:10:03,536 --> 00:10:05,210 I’m still authenticated. 168 00:10:05,210 --> 00:10:07,920 I'm all set up and I’m ready to do a lot more stuff. 169 00:10:09,720 --> 00:10:12,040 If you give them a token that doesn't timeout, 170 00:10:12,040 --> 00:10:14,810 you could provide them a key when they register their account. 171 00:10:14,810 --> 00:10:18,380 Then they can just send that back every time as their authentication. 172 00:10:18,380 --> 00:10:20,510 This eliminates all of the password submitting, 173 00:10:20,510 --> 00:10:22,650 other than when they sign up for the first time. 174 00:10:22,650 --> 00:10:25,630 But makes your token a little less secure because someone 175 00:10:25,630 --> 00:10:29,470 else could get a valid token and be able to make submissions forever. 176 00:10:29,470 --> 00:10:31,060 Weigh the pros and cons yourself and 177 00:10:31,060 --> 00:10:33,960 pick whatever you think is the better solution for your particular project.