In this chapter, we're going to add authentication to our project. This will include creating a Clerk account, setting up Clerk, creating the necessary components to display the authenticated state, creating protected TRPC procedures, and updating the Prisma schema. Let's start by creating the Clerk account. You can use the link you can see on the screen or the link in the description. And once you get to the landing page, you might see something interesting.
In here, where they mention the companies that use Clerk, you can actually find Ingest, the company that we are using for our background jobs. And you can confirm that yourself by going in their sign in screen and searching for Clerk inside of their network tab. And in here you can see that they are actually making requests for Clerk. And I just think it is super interesting that such a great company uses the same authentication system that we are going to implement in our project right now. So let's go ahead and do that.
Once you create your account here you will be redirected to the dashboard and in here you can click create application. I'm going to call this application vibe and I'm going to enable email in Google. You can of course enable all of these other providers if you want to and I will click create application. After we do that we have to install the Next.js clerk package but just before you do that ensure that you are on your main branch, ensure that your last change was 15 theme and that you have synchronized all of your changes. Now let's go ahead and let's run npm install clerk-nextjs and once it's been installed I'm going to show you the exact version that I will be using 6.23.0.
Now that we have that let's go ahead and add the environment variables to our .environment file. So I'm gonna go ahead and add clerk and paste these two. I like to wrap them in parentheses but I think this might depend on the system. I think Windows might have problems with this, but I think maybe even not. I think all of these will work just fine.
But yeah, in case you were wondering, I like to wrap them in parentheses. They don't have to be in parentheses. So all of these could actually be without parentheses, if that's something you prefer. I just feel like the syntax looks better with parentheses. I keep saying parentheses, I mean quotes, sorry.
Okay, now let's create our middleware file. So that's going to be inside of the source folder, create middleware.ts Make sure to not misspell this, middleware.ts, it's a reserved file name. We import clerk-middleware from clerk-nextjs server and we export default clerk-middleware and we add a matcher so we target all of these files here. Excellent. So now that we have this let's go ahead and let's add the clerk provider to our layout.
So I'm just going to import clerk provider to our root layout. So app folder layout, let's go ahead and import work provider from at clerk. Next, yes. And I'm going to wrap the entire application inside of a clerk provider. Yes, make sure you wrap your TRPC react provider inside of clerk provider as well.
Like that. So let me just confirm that I use the correct package. And just confirm that they've done this as well. Perfect. And now let's go ahead and let's do npm run dev here.
You don't need to start your ingest right now because we will be doing some other things. So what I did is I went to the end here and I clicked on next steps. Utilize your own pages for authentication. The account portal is the fastest way to add authentication. So let's click continue to the next JS guide.
And the first thing we're going to do is we're going to add this sign in pages. So let's go ahead and do that. I'm going to go inside of source app home here. And now I'm going to create a new folder called a sign in. And then inside, I will create another folder, which will use the catch all route, sign in like this, it needs to be exactly like this.
And then page.tsx inside. And now let's go ahead and let's import sign in from clerk next JS. And let's do a page export here with a div, which will have a class name of flex, flex-column, maximum width of three Excel, mx-auto, and width-full. And then let's add a section which includes a class name space y6 padding top of 16vh and 2xl padding top of 48. Inside of this section, add a div with a class name, flex, flex column, items center.
And inside of here, render, sign in, whoops. Sign in like this. And once you've added this, you can go ahead and copy this and you can add sign up here, like so. And then change this to be sign up as well go inside of the sign up page and replace sign in import with sign up import and then let's go ahead and do the following So we are going to set this to be public route. So we are going to go inside of our middleware.ts We're going to import createRouteMatcher right here.
And we're going to define a constant is public route using create route matter and we are going to target sign in and then we're going to change this expert default clerk middleware to include an arrow function which checks if the current request is not a public route and then it will redirect to the protect page. And then what we have to do is we have to modify our environment variables. So let's go ahead and go inside of environment variables here. And let's add that next public clerk sign-in URL is forward slash sign-in. And the fallbacks will be an empty forward slash.
So next public, clerk sign in, fallback redirect, and next clerk sign up, fallback redirect URLs. Perfect. And now I think that already you should be able to see this. If you go to localhost 3000, I think you should immediately be redirected to this page, right? And if you try to visit any other page, like try to visit some older project, like projects 1 to 3, you get immediately redirected back to the sign-in page.
So all pages are now protected. We are of course going to slightly modify this by going inside of the middleware. And let's modify this array of public routes to also include a forward slash. And I like to use this types of quotes and we also actually yeah I think this we also need forward slash API ingest like this so make sure you add this otherwise background jobs will not be able to work. So we need to allow ingest to be contacted.
Great. So once you've done this, you should now be able to visit the localhost 3000 page. There we go. You can see that now we can visit this but we still can't visit the individual project page. So and yes I am in dark mode, you might be in light mode, it doesn't matter.
So now let's go ahead and do the same thing that we did. Let me just go here. The same thing that we just did for sign in, we're now going to do to the sign up page. So we just did this so we didn't have to do it. I told you like already that you can just copy the sign in and do it right here.
But what we need to do is we need to add sign up to the list of our public routes. So let's see, did we do that or not? We didn't, so let's add it. There we go, So sign up is now added to the list, the same as sign in. And then we also need to add all the environment variables here.
So let's go and set up .environment here. And let's just add. So next public clerk sign up URL. And the redirect URLs for sign up fallback and sign in fallback. And it looks like these are duplicates.
So yeah, I think you only need one of these and one of these. So yeah, you can do remove these two and just move these two. There we go. At least I think that they were duplicates, right? I think they were And now you should be able to go Manually you can just enter any other route try going to projects one two three You will be redirected and if you click sign up you should be taken on the same layout.
As you can see, it loads the sign up page. So now you can switch between the two. Perfect. So now what I want to do, before we even log in, I want to go back to localhost 3000 here, and I want to create a navbar. So let's go inside of our home module, So that's going to be inside of source, modules, home, UI, components.
And in here, go ahead and create navbar ESX. Let's go ahead and mark this as use client. And let's import link from next link let's import image from next image let's import all of these from clerk next.js signed in signed out sign in button and sign up button and then let's import button from components UI button. Let's export const navbar here and let's return a nav element. Give this nav element a class name of padding-4, background-color transparent, fixed, top 0, left 0, right 0, z-index of 50, transition all, duration 200, border bottom, and border transparent.
Inside of this nav, add a div with a class name of maximum width, 5xl, mxauto, width full, flex, justify, between, and items center. Add a link inside with an href to a forward slash with a class name of flex, item center and gap of 2. And in here, Render an image with the source of logo SVG out of vibe, width of 24, and height of 24 as well. Add a span with a text vibe inside. Add a class name, font, semi-bold, and text large, like that.
And let's go ahead and let's go inside of our layout, in app folder, home, layout, And let's render it just so we can start seeing some results. So navbar from modules, home, UI components, navbar. And you should now see the vibe right here at the top. And the fun fact, it should also appear if you go into out screens as well, as you can see. So now you can always use it to quickly go back.
So now let's continue developing the navbar here. The first thing we're going to add after the link is we're going to add signed out state like this and then inside add a div with a class name flex gap to an add sign up button and add a normal button inside with a variant of outline and a size of small and render sign up inside. You can copy this and then change this to sign in button. This one will say sign in, and this one won't have the outline variant. It will just have a size small.
So there we go. Now you have sign up and sign in buttons that you can access. Only if you are logged out, of course. And then if you are signed in, let's just add a paragraph to do user control. And now we should be ready to log in.
So I'm going to click sign in here and I'm going to continue with Google. And once you confirm your Google login you will be redirected back here and you should be redirected on the landing page with the text to do user control like that. And you can see how now we can also load these apps. So the reason they previously weren't even loading is because in the middleware, we didn't allow the TRPC to be a public route. So in my case, I'm not going to have any public TRPC procedures, but if you want to, you can also add TRPC here, like this.
Let's just fix this TRPC. And then, if you, well, you can't log out now. So let's just create a component to log out now and then I will demonstrate this it's completely fine to add this here because we are going to protect the rpc routes based on their procedure type as well because right now if you wanted to have any public API routes, they don't exist. We prevent anything other than this to be a public route. Also, you can create the exact opposite, right?
You can call this isPrivateRoute and then all of these will be private routes. Right? And then you would just modify your logic. You would remove the exclamation point and you would do this. So if you have the majority of the public routes and minority of the private ones, you can just reverse the logic of the clerk middleware.
That's the cool thing about this. It doesn't have to, right, we just call this is public route. We could have called it anything and we could just put private routes inside and then use the reverse logic here, right? It's not like you need to add the public ones here. So this is especially useful if you have a bunch of public routes.
Then just do the reverse logic. Great. Now let's add the user control component. So I'm going to go ahead and close everything. I'm going to go instead of source components and I will create user control dot vsx.
I will mark this as user client and I'm going to import user button component here. I will export const user control And I will create an interface props here to show name, which is an optional Boolean. And in here, I will add the props and show name. Then in here, I'm going to return the userButton component, which is a self-closing tag. And I will modify, well, first I will pass the showName prop.
And then I'm going to modify the appearance prop to include the elements. And then get the userButtonBox to be rounded medium with an exclamation point at the end which basically means important user button avatar box rounded medium with size 8 8 and user button trigger rounded medium like so So I don't have to type this every time I have created it in a component like this. So now let's go back and set up our navbar. And instead of signed in render user control and passing show name prop. So just make sure you have imported user control.
And there we go. And in here, you now have this name, which is barely visible because I'm in dark mode. Don't worry, we're going to fix that as well. But from here, you can access your entire account, your security, all of those things. And you can also sign out from here.
And you can see that now, since I enabled inside of my middleware, trpc, I can fetch them, right? But if I remove this and refresh, I'm not able to fetch them, because all the network requests for TRPC are failing, as you can see. All of them are failing. But we will protect our TRPC routes in a different way. So it's completely okay, in my opinion, to allow this.
In fact, you can even just allow your entire API like this. Then you don't have to worry about ingest or tRPC specifically. Because our API should be protected in a different way in the first place. Let's just do a sanity check. What do we have here?
We have ingest and we have trpc. So ingest needs to be publicly available, simply because ingest will contact this no one else, right. And if you're wondering how this works, they probably have some kind of security header, which is checked every time you access this route. Because you can see that we are using the serve from ingest and next. So inside of here, they probably have their own request handler that does all the security features inside.
And as for TRPC, well, we are going to be the ones who are going to have to protect each individual route here and that's what we are going to do with the protected procedure. So I would actually recommend allowing all API endpoints to be public routes. So now let's go ahead and let's create the dark mode for this because you can see that when I log in of course it just looks weird. So what we're going to do is we're going to go and create a hooks folder. So go inside of source, we already have hooks.
Great. So this came with chat CNUI. And now create use current theme dot ds. So we already have use theme from next themes and we already use it in the project header use theme. The problem with this is that it has the following possible values.
It can be system, dark or light, which is fine for this radio group. But if the value is system, what exactly is that? Is it light or is it dark? We don't know. That's why we have to create a custom use current theme here, where we can extract the theme and system theme specifically.
Use theme like that. And then if theme is dark, or if theme is light, we can just return the theme as usual. Otherwise, return the system theme like that. Because you cannot always just return the system theme. This only makes sense if the theme is system, right?
If it's dark or light, then we don't care. We can just return whatever the value is. But if it is something other than dark or light, it means it is system. So then we cannot return theme, we have to return the actual value of the system theme. And now we can go inside of the user control right here, and we can adapt it as follow const current theme use current theme like this and then inside of here let's also import I think we need to install a package first.
So let's just do npm install at clerk forward slash themes. And I'm going to show you my package JSON here. 2.2 point 51 is my version. And then from that package, you can now import dark from clerk themes. And then very simply, in the appearance here, set the base theme to check if current theme is equal to dark, use dark, otherwise use undefined.
And now you can see the text is visible, and this is now in dark mode. So you might be thinking, could I have just added that to the clerk provider? Because clerk provider also allows for the appearance and base theme. Well, you can, but the problem is the theme provider for chat CN needs to be inside of the body, and clerk provider needs to be outside of the HTML. So we kind of have a conflicting situation here.
You can try moving both the clerk provider and TRPC React provider here, maybe. But I'm not sure how that works. I'm not sure if it matters, but from all the documentation I've seen, these two need to be outside of HTML. I'm not sure. I could be wrong.
But basically, if you are able to move these two, like this, inside, then you can create an abstraction around the ClericProvider and then you can do the same thing like this. But for now, I'm going to leave it like this, simply because this is what worked for me initially. Great, so we now have this in the user control and we can now go inside of sign in and do the same thing because right now if I sign out and if I go here you can see that this uses light mode. So let's go instead of sign-in page right here, let's go ahead and mark this as used client and let's import used current theme from hooks used current theme, which we just created, and let's import dark from clerk themes. Now in here we can extract the current theme and for this sign in let's add appearance here.
Base theme will check if current theme is equal to dark and use dark, otherwise use undefined. And let's also modify the elements a bit by adding cardbox here. Do not have any border, not have any shadow, and be rounded or LG. Like this. And there we go, you can see how now this is in dark mode.
And when you click on sign up, you can see it still uses the old theme. So let's just go ahead, we can just copy the entire file, go inside of sign up, paste the entire thing and replace the import to be sign up. I think that's faster. And there we go. Now both of our sign in and sign ups have the proper theme.
Great. So let's see where we are. What did we do? We created a clerk account, we updated our dot environment, we added clerk provider, sign up screens, middleware. Perfect.
We added home layout in the nav bar, we created the user control component. Now let's create protected TRPC procedures and let's update the Prisma schema. So I just want to do one more thing with the user control component and that is inside of the project view. So go inside of this component here. And in here, after the tabs list here, we added this ML auto flex and this button to upgrade.
Now next to it, also add user control like this and don't add the prop show name. So just make sure that you import the user control and let me show you how that will look like now. So now if you of course sign in, so let me just enter an account here And let me just go to any random project here. You can see that I have my user button right here. And let me just switch to light mode to see everything still works fine.
There we go. You can see that now I can log out and access my account information from here as well. And let's just double check the light mode to see everything works fine. Everything works just great. Perfect.
So now what we have to do is we have to create the protected TRPC procedures. So in order to do that, let's go inside of source, TRPC, and let's go inside of init. And now in here, we're going to modify the TRPC context here. So what I'm going to do is I'm going to remove this comment. And I will return alf to be await al from Clerk Next.js server.
After that, I mean, just below it, I'm going to export type context, which uses a weighted return type, type of create TRPC context. And you can actually find these exact instructions in the Clerk documentation here. Let me just find TRPC. Maybe I can find it. Integrate Clerk into your Next.js plus TRPC app.
Let me see if that is what I'm looking for here. Let's try again, TRPC. I think this might be it. So Yes, you can see that they instruct you to wrap the clerk provider around the trpc provider. So we already did that.
Right? We are using trpc react provider simply because the documentation has changed since then. But it's the same thing. It's important that the clerk provider is wrapping around the trpc provider. And now in here we are basically doing this in the create context and we are creating the context type here like that and in here you can find all the other things that we are going to do.
For example, they have a specific instruction to now add create context to this trpc route. But if you look at the trpc route, we already do that. So you don't have to worry about that. You can just follow what I do now. So I'm going to go after this T initialization and I will do const isOuted, like so.
And I will do T.middleware. And from here, the structure next and context. And check if not context out user ID. In that case, throw new TRPC error code. Let me just see what exactly is the problem in this one.
Oh, so I need to import TRPC error from TRPC server. Okay, make sure you do this. Then let's do unauthorized here with a message of not authenticated. Like that. And then after this if clause, return next and extend the context to include out.
Now we have to fix this problem is that out doesn't exist. You can do that quite easily by going back to this T here and simply add dot context, add the context type and execute it. And you can see that now we have the auth property here, which we added here. Perfect. And it's important that we also cache this in React, so this doesn't need to be called every single time.
And we are simply relying on the user ID from it here. So make sure you don't do any mistakes here, because now we're finally going to go ahead down here and do export const protected procedure to be t.procedure.use is authored. There we go. Now we have our protected procedure. So now it's time to replace a lot of our previous procedures with this new one.
So, thankfully, we don't have too many modules. So let's start with the messages procedures here. Let's see. At first we have GetMany instead of BaseProcedure. What you can do is...
Honestly, I don't think there will be a single public procedure here. So what I like to do is I like to highlight BaseProcedure and then I press Command D or Control D. And then this just selects all of the other ones, right? So one, two, three and I can remove them and I can add protected procedure Like this and nothing really changes now So I'm just now using protected procedure for each of these, for getMany or create. And I think that's the only two instances.
And you might think, but nothing really changed now. That's right. But look at this. If I'm using, just for example, I will bring back base procedure here. For the getMany, I'm now using base procedure.
If I try to extract context from here, and if I try to do, let's for example, imagine that we can query the messages by user ID. Imagine if I do user ID here and do context.auth and then I try to do you know user ID. In here it can tell me that the user ID is string or null. So that means what I have to do is I have to first check if there is no context at out.userId and then I have to throw new trpcError blah blah blah. But that makes no sense.
We just created the protected procedure which does that for us and then passes the context further. So instead, what we do when we know that something has to be a protected procedure, that we will always throw an error if the user ID is missing, we can now just use the protected procedure. And this time, the user id is a type of string. See the difference? Base procedure tells me it can be string or it can be null but protected procedure tells me this is definitely a string because it 100% exists at this point.
So that's why we're replacing things with a protected procedure. And then at this point, it doesn't matter that our API is allowing the public route for API, right? An important thing you should know, you should never, never, ever, ever rely on the middleware for authentication. So what I'm doing here is just a nice user experience, right? It is easy to redirect the user using the middleware.
But this isn't my line of defense. This isn't what I'm doing to throw errors. That's why I have a data access layer called trpc and in here I have my protected procedures. So if this middleware fails, even in this case, I explicitly allow the middleware to allow API routes, I'm still very much protected because I'm protecting my data access layer. And you should do the same.
Never ever rely on the middleware to protect your app. If you want to use the middleware for a nice user experience like PR, of course you can do that. But it shouldn't be your last line of defense. You should have a data access layer, and you should protect your routes individually. Because let's imagine this middleware breaks, which can happen.
Next.js had a middleware security issue just a few versions ago. And people who were shocked that that happened, now got a very big security lesson. You shouldn't rely on the middleware. Even if the middleware was perfect, you should not rely on it. Don't confuse this middleware with...
This is also technically a middleware, right? We just created a middleware. This middleware and this middleware are two different things they are not comparable okay what I'm trying to tell you is don't try and do API and then projects and then create. Don't do this. This is not enough for you to protect your API routes.
You should protect your API routes inside of the routes themselves, like I am doing right now. So this is a different type of middleware. They're just using the same word. They're using the same keyword middleware here and middleware here. I'm trying to explain the difference.
So whenever you use the middleware, it should only be used to improve user experience, like adding redirects, which is a very nice thing to use the middleware for. But if your middleware breaks, and if the user actually ends up being able to visit my individual project page, I'm still just going to throw a bunch of errors because when we try to load the messages, it will be a protected procedure and it will just throw the user an error saying, hey, you're not authorized. I don't know how you access this API, but you cannot see that API. That's how authorization should work, not by the middleware. The middleware is just the first layer of security.
The actual layer of security is the data access layer, in our case, trpc. I hope I cleared that up. Sorry for going on this rant, but it is important for you to understand that. Great. So now let's go ahead and once we finish the messages, let's go inside of projects server procedures.
And I think that we have to do the very same thing here. I don't see a single thing that can be a base procedure here. So I'm just going to replace all of these instances. I think this is the last one, yes, with protected procedure. So Let me show you the exact changes.
Get one, get many, and create. So three procedures in this case. And when I search for base procedure now, not a single one exists except the actual instance here in the init file. And when I search for protected procedure, I have eight results in three files. The third one being init file.
So that's how your project should look as well. So now only authorized users can access these API routes and it absolutely doesn't matter that we are not protecting it in the middleware. Perfect. So now let's go ahead and let's add Prisma schema update because now we finally have the user ID. So that means we can go inside of Prisma schema here and we can modify some things.
So let's start with the project. The project from now on will have a user ID, which will be a required string, like this. And since all other entities end up being related to the root project, we don't really have to add it to the message as well. You can of course do that if you want to. If you have any architectural reason for doing that, of course you can add individual user ID for the fragment, individual user ID for the message, right?
But for the same reason, I'm not adding project ID into the fragment because the fragment is related to the message, which has the project ID already. I'm not gonna be adding the user ID to my other entities because it is enough that the user ID is in the project. And once I've done that, I'm going to shut down my app and I'm going to do npx prisma migrate reset. So again, only do this in development, we are clearing up our database because we are in development and we can do that. And once we've done this, let's go ahead and do npx prisma migrate dev.
And once it connects, let's simply call this user ID or auth. Here we go. I'm going to call this user ID and there we go. Now I'm going to do npm run dev and I'm going to run npx ingest cli latest dev. So both things should be running and just double check that it was able to connect to API ingest.
Because as I said, if you accidentally don't allow your API here, then ingest will not be able to connect. There we go, you can see that now it's 404. So that's wrong. That's why you need to make sure to allow all of your API routes and then after some time, let's check again, it can connect to it. Exactly what we need.
So, now let's go ahead and fix all of the issues that we have because we do have them. We just got a new rule and that is that each project needs to have a user ID. So let's go ahead inside of our modules and let's go inside of projects, server procedures and we can already see some errors here such as the create error. So in the create protected procedure we now have to also pass the user ID which we can easily extract from the context here because we are using a protected procedure. So context.auth user ID.
As simple as that, problem fixed. Now what we have to do next is you also have to modify the getMany. So whose projects are we loading? Very simple, the currently logged in users projects. So let's add a where here, like this.
User ID matches context alp user ID. And now we are only loading this currently logged in users project. And same thing for get one. Simply extract context from here, and we can only load the existing project if we have a matching user ID. If we don't, this will be null and we're going to throw the error not found.
So even if the user is logged in and manages to surpass this protected procedure, we will still be able to throw the error because we have no idea which project with that specific ID and that user ID they are looking for. So we just throw, we have no idea what you're talking about. We've never seen that project before. So full security in our application. And now we have to do the same thing, but for messages.
So let's go inside of procedures here and let's check get many. So in here, I'm going to extract the context as well, and I'm going to try and do project user ID. And it seems like I can do that. So let's simply add context out user ID. There we go.
So just like that, we are now also protecting all of our messages. But since we don't have direct user ID in the message, we have to go through the project ID first. Perfect. And for the create, well, we have to do the same thing, but a little bit differently here. So what we're going to do here is we're going to do const existing project first.
Await Prisma project find unique where, and simply add the ID, the input project ID and user ID to be context. Out user ID like this. I mean, maybe we can somehow do it from here. I don't know, I'm not that good with Prisma, but you can do it in two separate queries. It's not the end of the world.
And if there is no existing project, let's throw new trpc error here. Code not found with a message. Project not found. So I have no idea what where what project do you want to create this message into, right? And make sure you have imported the TRPC error from TRPC server.
So this way, even if they somehow surpass the protected procedure, we are still not going to allow them to just create messages in someone else's project, because they need to match the exact user ID who created that project. Great, so we now have this done right here, and then we can safely do the created message. You can even then use, maybe it's even safer to use existing project.id for the project ID. So it's only this one which we can query from our database with the correct user ID that we are going to insert this message into. Excellent.
And you don't have to worry about these background jobs because we are protecting them before we even trigger them. So that is for the create method. Let's see, did I fix it for the getMany? And I think that should be it. So obviously, we should now just test our app to make sure things are still working.
So let's go ahead and do the following. Let's go inside of our projects list now, and I just want to do a slight modification here. And that modification is that I'm going to load the current user from useUser, which you can import from clerk-next.js. And then this will allow us to do the following. We can then do user?firstName and then apostrophes usersVibes.
And then in here also do, if there is no user, return null. So we don't even load the project list if we are not logged in. We can, of course, do this in a million ways, but I think this is just simple enough for now. Great. So you can see that when I am logged out, nothing happens here.
Great. So what I want to do first is I want to go inside of my project form. And in here, in my OnSuccess, specifically on error, I should redirect the user to the out screen as well if this fails. Because right now when I type test, I'm just getting an error not authenticated. I mean you could technically argue that's good enough but let me show you what you can do.
So you can do router and then you can just push the user to sign up or sign in. That's an easy way you can do. But here's a cool thing You can actually do const clerk useClerk from clerkNext.js. So just make sure to add this import. And once you have it, you can do the following.
If error.data.code is equal to unauthorized, you can do clerk.openSignIn, like that. And let me just, so error data is possibly undefined, so Maybe I need to do this. There we go. And let me just move this to the top. So this way, if this fails, it will open the sign-in model.
So I write test. And there we go. It opens this nice model. I think it looks cool. If you want to, you can also just do, you know, router.push sign in.
That also works. There we go. So whichever one you prefer, I just thought I would show you this cool alternative. Great! So I'm pretty sure that we don't have to do anything more besides test the app.
So now I'm going to log in here and as you can see it says John's vibes and no projects found and I will do build a landing page here and let's see will I get any errors I don't think I'm getting any errors at all I don't want to just check my functions that's the s just confirm I'm not getting any errors in here, even though we shouldn't be getting any errors at all. This is a background job, this doesn't need any user ID. I think everything is just fine here and I think that we will be able to normally load the messages. But let's wait and see the result. And there we go.
Once I am logged in, you can see that I can normally create my messages. I can even refresh this and I can load my messages. I can see my fragments. So all of this is obviously working. Perfect.
And let's try something fun. I'm going to copy the URL here, and I will log out. And then I'm gonna go ahead and paste that URL. So obviously I'm getting redirected. But let's say inside of my middleware, I accidentally do projects.
And I did this. Alright, so let's see what happens then. You can see that even if the middleware fails, nothing useful is shown to the user. And finally, an error is thrown. This is, of course, not ideal because we are missing an error boundary.
We're going to fix these details in the last chapters, right? Obviously, it's not ideal that these types of errors are shown, even though this wouldn't show in production. This is what you would see in production, which is not any better. But we will, I'm going to show you how to add proper error boundaries. I just wanted to show you that you can't fool this system we just created, right?
You will either get redirected, or you will be hit with a bunch of errors because you don't belong on that website. Amazing, amazing job. I think that officially marks the end of this chapter. So now let's go ahead and let's open a pull request. So 16 authentication.
I'm going to create a new branch. 16 authentication. I'm going to stage all of my changes. 16 authentication. I'm going to commit and I'm going to publish the branch.
Now let's go ahead and open a pull request and let's review our changes. And here we have the summary. We added user authentication and theming support using Qlerq, including sign-in and sign-up pages with theme-aware styling. We introduced a navigation bar with authentication controls and user display. We added a user control component for displaying user information and actions.
We integrated authentication checks into project and message features, ensuring users can only access their own data. We improved project and message lists to only display personalized content and enforce user-based access. Exactly what we did in this chapter. As always, file by file walkthrough here and a whole sequence diagram explaining how our new clerk provider and authentication TRPC procedures work. And we did a very good job this time.
The only comment is in the migration SQL. We don't really care about this because we are in development phase and no other comments. Amazing, amazing job. Let's go ahead and merge this full request and once we've done that let's go ahead and go back to the main branch and let's click on synchronize changes and OK. And in a few seconds you will see that 16 was just merged.
Authentication. That marks the end of this chapter. Amazing, amazing job and see you in the next one.