Handling Redirects Safely in User Authentication
00:00 Let's go to our util right here where we're saying require user ID. We're going to take an optional second argument called, or that's just an object that has a redirect to, and we'll default that to an empty object, that way you don't have to provide it.
00:16 We will default this to actually nothing. We're going to allow people to specify a string or if they don't want to specify any redirect, so we're going to go to login. If we don't want them to have a redirect to on the login screen, just let it be, then they will specify null.
00:34 This is going to have to have a type of redirect to, it will be optional and it'll be a string or null if you want to be very explicit. But we are actually going to default it for people, so they don't have to specify a redirect to all the time, because most of the time,
00:50 we don't want to have to specify it. It'll just be like whatever action or loader you're in, when you call require user ID, take that URL and that's where the redirect to should go. That's the idea. We're going to create a URL object,
01:08 so const request URL out of the request.URL, and then we're going to assign the redirect to a couple of different options. If redirect to,
01:23 copilot is wrong here, redirect to is null, then that's fine. We'll leave it to be null. If it's not, then we're going to do whatever it is, redirect to, otherwise, we'll fall back to the exactly copilot.
01:40 Thank you. The request URL path name and its search params. So now, we can default if the user doesn't, or the developer who's using this function, if they don't specify a redirect to, then we'll default it to the request URL.
01:57 If they do, then it'll fall back to, or it'll be whatever they specified. If they specify null, then we don't want to have a redirect to query param in this login URL at all. So with that now, let's do our login params.
02:15 That's going to be based on whether or not there is a redirect to. So redirect to, then new URL search params, redirect to, otherwise, null. Then we get our login redirect is going to be,
02:33 I'm going to actually turn this into an array, we'll join it together after filtering. So we'll say login is where we want to go, and then the login params, yes, that's exactly right, copilot. So it'll be the login params, if they're provided, and we're going to two-string that.
02:51 Then we're going to filter out everything that's falsy. So empty strings, false, null, etc. Then we'll join by the question mark. With that, our login redirect will include the redirect to. So now if I try to go to settings profile,
03:10 now I end up on the login page, but I get a redirect to settings profile. Then if I go to the profile.tsx right here, and I say require user ID redirect to null, then I can say settings profile,
03:29 and it takes me to login without a redirect to. So I have that control as the developer to decide whether I want to have a redirect to or not. But I do, so I remove that, we say settings profile, and that gives me that redirect to, which is exactly what I want. So that takes care of the utility side.
03:48 Now we've got to implement this in the login. So let's go to login, and this means we're going to need some config in Zod for redirect to, and that is an optional string there. Then down here in our submission, we're going to grab the redirect to,
04:04 and then we can just stick redirect to right here, except no, we don't want to do that. That would be super duper bad. And the reason is that somebody could send somebody a link that redirects them to anywhere else. So like, let's have you log in right here,
04:24 and then we'll redirect you to my website that has a login that looks a lot the same. So you'll log in, it will send you over here, and then you see that login, and you're like, oh, I guess it didn't work. So I'll log in again. Oh my gosh, you just lost your credentials. So no, we don't do just a straight up send the redirect to that came from the client.
04:42 We need to do a little bit of safe checking. And it's sufficient just to know that we only redirect to stuff that starts with slash, or that, you know, it doesn't start with HTTP. And if it does, then that's, it could send them offsite. And so we don't want to do that.
05:01 So let's bring in safe redirect from RemixUtils. From RemixUtils, safe redirect, and that is called safe redirect. And we come down here, we wrap this in a safe redirect, and now we can safely redirect. But we're not done because we haven't actually
05:21 done anything in our form to send that safe redirect value. Now you could actually just rely on the query string when the action is submitted, but there's some cases where maybe that query string doesn't get sent to the proper form, like if you're using a fetcher or something like that.
05:41 So in some cases that you don't want to necessarily rely on that. So by default, I just say, let's do it this way. So we're going to grab our search params, search params. This is actually returns a tuple, so we can set search params. So, but we only need the current search params. So use search params from RemixRunReact.
06:00 And then we're going to get the redirect to from search params, get redirect to. And now we're going to add default value with redirect to. And then we'll come down here and add a hidden input for our redirect to. So input, and we'll use the conform.input helper.
06:20 And this will be fields redirect to, and the type is hidden, dun, dun, dun, dun. And with that, now we can take a look at the DOM and we'll see right before our submit button, or let's see, yeah, it's right before the submit button, right around here. Here, let's refresh, just to make sure. There it is.
06:41 So we got our login form redirect to, and that has the redirect to value. So that's going to work just fine. So then if I log in, Cody, Cody loves you, then we end up on the settings profile page. And that will apply to whatever pages. So I come over here and let's say I log out.
07:00 Now I paste that in, that'll send me to the profile photo page. Cody loves you. Dun, dun, dun, dun. So anytime we're requiring a user ID, we're going to send them over to login, but we'll give them a nice way to get back. Now the signup process will be exactly the same.
07:18 And so if you come over here, create an account, this will be the same thing, a hidden input, the URL search params, all that. So feel free to implement that yourself, but yeah, it's pretty repetitive. So I'm not going to bother with that right now. We'll let Kelly, the coworker, is happy to do that for us. So to quickly review, we've got our auth server update.
07:37 We're in require user ID. In the case where there is no user ID, we construct the login redirect that will include the login params if they exist. Otherwise we filter them out. So we don't have this question mark at the end unnecessarily. And those login params consist of the redirect to property
07:56 that we're going to derive from what is given to us by default. So if you give us null, then we'll do that. If you give us a redirect to, we'll use that. But if you don't, then we will create it from the request URL path name combined with those search params. And that gives our users a really awesome experience.
08:14 And we don't have to do much other than use this require user ID, which we were doing already.