WEBVTT

1
00:00:04.520 --> 00:00:05.870
In this video we'll roll our own

2
00:00:05.870 --> 00:00:08.800
authentication from scratch using Postgres one-way hashes,

3
00:00:08.920 --> 00:00:09.380
RPC,

4
00:00:09.400 --> 00:00:10.630
and reactive HTML.

5
00:00:10.840 --> 00:00:12.510
No external services required.

6
00:00:12.680 --> 00:00:13.180
At the end,

7
00:00:13.400 --> 00:00:15.390
Claude Code will build us a fully functioning,

8
00:00:15.520 --> 00:00:16.140
beautiful A

9
00:00:16.300 --> 00:00:16.750
system.

10
00:00:17.190 --> 00:00:18.820
I'm going to open up the project in Vim.

11
00:00:18.910 --> 00:00:21.660
We'll head over to the home template HTML.

12
00:00:21.870 --> 00:00:23.970
I'm going to replace the boilerplate with

13
00:00:24.230 --> 00:00:27.620
a very simple login form that we could use to demonstrate these concepts.

14
00:00:27.790 --> 00:00:29.390
The HTML has two main parts.

15
00:00:29.510 --> 00:00:30.560
If the user's logged in,

16
00:00:30.790 --> 00:00:32.680
we show their username and a logout button,

17
00:00:32.830 --> 00:00:33.780
and if they're not logged in,

18
00:00:33.910 --> 00:00:36.330
we show the login form that you see over on the right.

19
00:00:36.710 --> 00:00:38.500
Let me paste in a couple of functions that we'll

20
00:00:38.500 --> 00:00:41.260
fill out to provide the login and logout functionality.

21
00:00:41.840 --> 00:00:42.890
Inside the login function,

22
00:00:42.920 --> 00:00:44.190
in order to log in a user,

23
00:00:44.240 --> 00:00:46.830
you call the login method of session,

24
00:00:47.000 --> 00:00:48.500
and you can provide it an object

25
00:00:48.720 --> 00:00:51.610
which has a user ID which we'll just hard code to one for now,

26
00:00:51.880 --> 00:00:53.030
and a username

27
00:00:53.320 --> 00:00:56.720
which we'll set to whatever the parameter form username is set to.

28
00:00:58.050 --> 00:00:58.640
For logout,

29
00:00:58.690 --> 00:01:01.630
we call the logout method of the session object.

30
00:01:01.870 --> 00:01:03.750
These two methods are event handlers.

31
00:01:03.930 --> 00:01:07.750
I do this by convention where I put an on prefix in front of the name,

32
00:01:07.890 --> 00:01:11.520
and you'll see why having event handlers can be really useful in a second.

33
00:01:11.970 --> 00:01:12.200
For now,

34
00:01:12.250 --> 00:01:14.960
I'll just proxy straight through to their corresponding methods,

35
00:01:15.090 --> 00:01:15.830
log in

36
00:01:16.170 --> 00:01:17.310
and log out.

37
00:01:19.350 --> 00:01:22.790
Next I'll come down to the HTML tag and add a constructor attribute

38
00:01:23.230 --> 00:01:26.380
that's private called form of type login form.

39
00:01:28.190 --> 00:01:30.660
This declares an attribute that's private and available

40
00:01:30.660 --> 00:01:32.910
for use inside of the template only.

41
00:01:33.390 --> 00:01:33.660
Next,

42
00:01:33.710 --> 00:01:37.180
let's add an event handler to the button such that when the user clicks it,

43
00:01:37.630 --> 00:01:39.310
we call the on logout

44
00:01:39.510 --> 00:01:40.400
function above.

45
00:01:41.450 --> 00:01:45.160
Then on the form tag I'll add the on submit event handler and this time

46
00:01:45.260 --> 00:01:48.490
we're gonna call the on login and pass the form attribute that

47
00:01:48.490 --> 00:01:52.210
was defined in the constructor attributes above for our HTML tag.

48
00:01:53.020 --> 00:01:55.090
I can see in my editor that I have 2 errors,

49
00:01:55.260 --> 00:01:57.010
so I'll open up another panel on the right,

50
00:01:57.300 --> 00:01:58.330
type elements build,

51
00:01:58.540 --> 00:02:00.930
and that will give me a quick snapshot of what's going on.

52
00:02:01.850 --> 00:02:03.320
You can see that we have a security error,

53
00:02:03.340 --> 00:02:05.480
and it's telling me that you cannot call server code

54
00:02:05.480 --> 00:02:07.970
from the browser without going through an RPC function.

55
00:02:08.130 --> 00:02:10.539
The server code that we're calling is logout,

56
00:02:10.810 --> 00:02:12.280
and if I were to scroll down on that area,

57
00:02:12.330 --> 00:02:14.200
you'll also see that login

58
00:02:14.530 --> 00:02:17.160
is not permitted to be called from the browser either.

59
00:02:17.330 --> 00:02:18.220
And that's because if

60
00:02:18.370 --> 00:02:20.200
any browser code could log in a user,

61
00:02:20.250 --> 00:02:21.320
it wouldn't be very secure.

62
00:02:21.380 --> 00:02:22.800
It needs to go through the server.

63
00:02:23.050 --> 00:02:24.330
In order to fix this problem,

64
00:02:24.460 --> 00:02:26.110
we need to annotate these functions

65
00:02:26.250 --> 00:02:27.920
with an RPC build tag.

66
00:02:29.470 --> 00:02:32.170
This tells the compiler to ensure that these

67
00:02:32.430 --> 00:02:34.860
only run on the server and the browser will call

68
00:02:34.860 --> 00:02:38.270
to these functions over the wire using an RPC protocol.

69
00:02:38.590 --> 00:02:39.620
When I save the file,

70
00:02:39.830 --> 00:02:41.140
the air goes away.

71
00:02:41.710 --> 00:02:41.970
Finally,

72
00:02:41.990 --> 00:02:44.450
we need to come down to the form and bind our inputs.

73
00:02:45.360 --> 00:02:47.420
I'm going to set the value to form.

74
00:02:48.160 --> 00:02:48.900
Username

75
00:02:49.120 --> 00:02:51.550
and this is going to two-way bind this value to

76
00:02:51.550 --> 00:02:53.830
this input field such that if the UI changes,

77
00:02:53.880 --> 00:02:55.790
the data changes and vice versa.

78
00:02:56.160 --> 00:02:56.990
Down in the password,

79
00:02:57.040 --> 00:02:57.940
I'll do the same thing,

80
00:02:58.040 --> 00:02:59.910
but I'll set it to the forum password.

81
00:03:00.070 --> 00:03:00.190
Great.

82
00:03:00.280 --> 00:03:02.500
Now I should be able to go over to the right and

83
00:03:03.240 --> 00:03:04.820
provide a username and a password,

84
00:03:05.160 --> 00:03:07.150
hit login and see that the user interface

85
00:03:07.150 --> 00:03:09.710
is updated reactively to the logged in state.

86
00:03:09.800 --> 00:03:10.750
If I click log out,

87
00:03:10.870 --> 00:03:12.140
it logs me back out

88
00:03:12.240 --> 00:03:14.070
and I see the original login form.

89
00:03:14.840 --> 00:03:15.100
Next,

90
00:03:15.120 --> 00:03:16.800
we'll create an elements migration

91
00:03:17.000 --> 00:03:18.330
that adds the user's table

92
00:03:18.650 --> 00:03:21.340
such that we can actually try authentication in the database.

93
00:03:21.860 --> 00:03:23.150
I'll open up the migration file.

94
00:03:23.240 --> 00:03:25.470
I'm going to add two fields to the user's table.

95
00:03:25.520 --> 00:03:27.220
The first one will be the username

96
00:03:27.480 --> 00:03:28.450
that'll be of typed text.

97
00:03:28.520 --> 00:03:29.330
It'll be unique,

98
00:03:29.360 --> 00:03:30.100
and

99
00:03:30.400 --> 00:03:32.140
we wanted to make sure that it's not null.

100
00:03:32.440 --> 00:03:32.930
And

101
00:03:33.160 --> 00:03:35.630
the next one will be password hash.

102
00:03:36.530 --> 00:03:36.740
Note,

103
00:03:36.870 --> 00:03:39.260
we're not going to store the cleartext password.

104
00:03:39.350 --> 00:03:41.460
We're going to store a hash of that password such

105
00:03:41.460 --> 00:03:44.010
that someone looking at it cannot actually tell the password,

106
00:03:44.030 --> 00:03:45.750
and that's very important for security.

107
00:03:45.960 --> 00:03:46.220
Next,

108
00:03:46.270 --> 00:03:49.710
let's add a dummy user into the database so that we can play with it easily.

109
00:03:50.560 --> 00:03:53.380
To store the password hash we're going to call the crypt method

110
00:03:53.600 --> 00:03:55.950
and I'm gonna pass the password as the first parameter.

111
00:03:56.120 --> 00:03:57.910
This is going to be my mother's bank password,

112
00:03:58.000 --> 00:03:59.200
so please keep it safe.

113
00:03:59.560 --> 00:04:00.980
And as the second parameter

114
00:04:01.120 --> 00:04:03.150
I'm gonna call the generate salt method.

115
00:04:03.400 --> 00:04:06.100
I'm gonna use the blowfish algorithm with a.

116
00:04:07.280 --> 00:04:08.390
Count of 12.

117
00:04:09.270 --> 00:04:10.090
When I press save,

118
00:04:10.270 --> 00:04:11.820
my database should be up to date.

119
00:04:12.270 --> 00:04:12.540
Next,

120
00:04:12.590 --> 00:04:14.820
let's use the elements DB command to log into

121
00:04:14.820 --> 00:04:16.899
PostCress and take a look at the user's table.

122
00:04:18.200 --> 00:04:19.470
There's the record we inserted,

123
00:04:19.519 --> 00:04:21.589
and note the password hash is not clear text,

124
00:04:21.640 --> 00:04:23.760
it's encrypted into a one-way hash.

125
00:04:24.600 --> 00:04:27.150
But how do we check whether someone is authenticated?

126
00:04:27.760 --> 00:04:29.360
We issue a select query.

127
00:04:29.640 --> 00:04:29.910
For now,

128
00:04:29.930 --> 00:04:31.500
I'll just select all fields.

129
00:04:31.750 --> 00:04:32.710
We're going to select

130
00:04:33.200 --> 00:04:37.160
a row where the username is equal to me and the password hash

131
00:04:37.160 --> 00:04:40.550
is going to be equal to the value of calling crypt again,

132
00:04:40.760 --> 00:04:42.750
and this time we'll pass a password,

133
00:04:42.920 --> 00:04:45.180
password 1234 I think it was,

134
00:04:45.560 --> 00:04:47.980
and instead of calling generate salt,

135
00:04:48.280 --> 00:04:52.270
we're going to give the name of the column that has the hash.

136
00:04:55.710 --> 00:04:58.930
It will find the record because it figures out

137
00:04:59.470 --> 00:05:03.230
how to hash this value that was provided as the first parameter

138
00:05:03.470 --> 00:05:06.750
using the salt that was stored with the password

139
00:05:07.230 --> 00:05:07.850
in our

140
00:05:08.150 --> 00:05:09.660
in our hidden column here

141
00:05:09.870 --> 00:05:12.170
and we get back a record because it matches.

142
00:05:12.310 --> 00:05:14.320
Now if I change this to a bad password.

143
00:05:16.430 --> 00:05:20.810
I'll get 0 records back and that tells me that the user was not logged in effectively.

144
00:05:21.310 --> 00:05:24.580
Let's finish up by adding that SQL query to our login RPC function.

145
00:05:24.710 --> 00:05:25.130
To do that,

146
00:05:25.150 --> 00:05:28.300
we're going to import the SQL method and the AI

147
00:05:28.420 --> 00:05:30.270
from the Elements application package.

148
00:05:30.590 --> 00:05:31.690
And in the login function,

149
00:05:31.710 --> 00:05:33.650
I'm gonna just replace this entire code

150
00:05:33.860 --> 00:05:35.610
with one that I wrote off camera.

151
00:05:36.530 --> 00:05:37.830
The first line says

152
00:05:38.030 --> 00:05:38.770
to

153
00:05:38.980 --> 00:05:39.840
run the SQL query,

154
00:05:39.890 --> 00:05:42.040
select ID from users where the username is equal

155
00:05:42.040 --> 00:05:44.110
to the one that was provided as a parameter,

156
00:05:44.250 --> 00:05:46.710
and the password hash is equal to the crypt

157
00:05:46.970 --> 00:05:49.520
forum password using the password hash column.

158
00:05:49.610 --> 00:05:51.910
This is similar to what we just did in the DB

159
00:05:52.450 --> 00:05:54.020
in the DB shell itself.

160
00:05:54.130 --> 00:05:56.990
Now one thing that you might be worried about is providing these

161
00:05:57.170 --> 00:05:59.280
values as template parameters

162
00:05:59.610 --> 00:06:02.800
is totally fine in elements because the compiler rewrites it to be safe,

163
00:06:02.930 --> 00:06:05.640
so the no SQL injection attacks are possible here.

164
00:06:06.090 --> 00:06:07.390
Now if the user is

165
00:06:07.810 --> 00:06:08.200
null,

166
00:06:08.290 --> 00:06:10.770
it means that we did not authenticate properly and would throw

167
00:06:10.950 --> 00:06:11.990
an authentication error.

168
00:06:12.110 --> 00:06:14.390
Never tell them whether it was the username or the password,

169
00:06:14.650 --> 00:06:14.710
uh,

170
00:06:14.810 --> 00:06:16.200
just say it was one or the other,

171
00:06:16.220 --> 00:06:18.470
otherwise you're giving the attacker some information.

172
00:06:19.010 --> 00:06:20.500
And if we get past that,

173
00:06:20.530 --> 00:06:22.620
then we'll log in the user this time with the user

174
00:06:22.620 --> 00:06:25.670
ID and with the username that was provided up from the

175
00:06:25.830 --> 00:06:26.230
form.

176
00:06:26.910 --> 00:06:29.190
Since the login RPC function can throw now,

177
00:06:29.310 --> 00:06:30.700
we need to handle that in the client,

178
00:06:30.760 --> 00:06:33.140
otherwise we'll just see an uncaught exception in the console

179
00:06:33.140 --> 00:06:34.860
if the user is not able to log in.

180
00:06:35.190 --> 00:06:36.820
We would normally build a user interface for this,

181
00:06:36.950 --> 00:06:38.300
but for the sake of brevity,

182
00:06:38.430 --> 00:06:41.170
I'll just paste in a tri-catch block that catches the error

183
00:06:41.390 --> 00:06:44.500
and logs to the console that the user was not able to log in.

184
00:06:44.830 --> 00:06:47.350
Notice that this error can be thrown over the wire

185
00:06:47.520 --> 00:06:49.690
and it travels over the RPC protocol

186
00:06:49.810 --> 00:06:50.840
just like everything else.

187
00:06:51.350 --> 00:06:53.150
Let's try to log in with the correct password

188
00:06:53.150 --> 00:06:55.530
first and make sure that the state changes.

189
00:06:55.710 --> 00:06:56.220
Perfect.

190
00:06:56.510 --> 00:06:57.150
When I log out,

191
00:06:57.190 --> 00:06:58.590
I'll try a bad password

192
00:06:58.770 --> 00:07:00.290
and I should see an error

193
00:07:00.430 --> 00:07:02.770
in the console indicating that the login failed.

194
00:07:03.670 --> 00:07:05.100
Now that we've seen the code concepts,

195
00:07:05.210 --> 00:07:06.660
let's have Claude Code build us a

196
00:07:06.660 --> 00:07:09.430
completely functioning authentication system that looks beautiful.

197
00:07:10.070 --> 00:07:13.450
I'll paste in the prompt and press enter and Claude code will get to work.

198
00:07:15.920 --> 00:07:17.940
Already we have a sign-in page that's looking a

199
00:07:17.940 --> 00:07:20.430
lot more beautiful than the one we created previously.

200
00:07:20.950 --> 00:07:23.060
Claude is driving the user interface directly in

201
00:07:23.060 --> 00:07:24.810
the browser to ensure that it works.

202
00:07:25.710 --> 00:07:26.240
Fantastic,

203
00:07:26.290 --> 00:07:30.270
we have a completely working authentication system with tests and

204
00:07:30.410 --> 00:07:33.320
user interface that works in mobile and also on desktop

205
00:07:33.650 --> 00:07:35.480
in the terminal on the lower right I'm going to

206
00:07:35.480 --> 00:07:38.510
check out that the tests work properly by typing elements test

207
00:07:38.770 --> 00:07:41.150
and scrolling down and seeing that they're all passing.

208
00:07:41.570 --> 00:07:45.240
This is one of the fantastic benefits of working with elements with an AI agent is

209
00:07:45.240 --> 00:07:47.120
that the tests get written automatically and you

210
00:07:47.120 --> 00:07:49.430
can see at a glance whether everything is working

211
00:07:49.650 --> 00:07:51.720
and if anything has regressed over time.