Now let's go ahead and let's create an admin example. So in order to do that we have to create the admin page. So let me just reload my window. I have some errors in the cache it seems. And let's go and inside the protected here create a new folder called admin like that and inside of here let's go ahead and create a new file page.vsx and let's go ahead and let's mark this as use client and let's export const admin page here and let's return a div admin page.
Like this. And now when you click on the admin here, oh, my Apologies. So yeah, whenever you're working with pages you have to do export default. Like this. There we go.
Great, now we have our admin page here. And now what I want to do is I want to create separate hook and separate lib to directly fetch the role. Right? So let's go ahead and do that. First, since we marked this as useClient, let's go ahead and go inside of our hooks and let's create useCurrentRole.vs.
So if we don't want to fetch the entire user, well, here's the thing, we are not fetching the user here, we are simply decoding the session. So that's important to know, you're not making any unnecessary API requests. But you know, just in case you want a specific hook to get the role, you can do that as well using the exact same method. So what you have to do is import useSession from nextAuthReact and export const useCurrentRole like this. Get the session to be useSession and then simply return session.data userRole like that, that's it and then you can go back inside of that admin page and in here you can get the role using use current role and I can then write current role is gonna be role and there we go current role here is user perfect And let me now show you an alternative way of doing it by creating the lib for this.
So let's say this is a server component. For that you would go inside of the lib here, inside of auth. And similar to useCurrentUser, this would be currentRole for example. And it will directly ask for the role. That's it.
So that's how you can create reusable hooks and libs for Auth. So let's remove useClient from here. Let's make this an asynchronous function. And let's go ahead and await current role from libAuth. And as you can see, no errors.
And we are officially able to fetch this both in server and in client components now. Perfect! So I think you've mastered how to create custom hooks and roles now. So now we're going to demonstrate how to actually create a reusable component which will protect specific content from some users. So let's go ahead and style this page a bit.
So let's import card from components UI card and I'm simply gonna bring this back to use client because I want to work with client components for this. So role is gonna be use current role. Like that. Then I can remove this. Great.
So let's go ahead and let's... In this card, let's give it a class name of width 600 pixels. Let's add a card header from components UI card. Let's add a paragraph which is very simply going to use some emoji here and write admin. Let's give this a class name of text to excel font semi-bold and text center.
There we go. So we have a header for the admin page and now in here let's create a card-content and in this card-content I'm very simply gonna add a class name space y4 and now I want to create a reusable component called a roll gate So you're gonna see how that's gonna look like and it might be useful You know if you're gonna do a lot of stuff, which is role based access So let's go inside of components out and create a new file roll gate dot TSX It's gonna be a client component, which will have an interface rollgate props. It's gonna accept children, which are react react node. And it's gonna have allowed role to be a type of user role from Prisma Client. So let's export const role gate here.
Let's go ahead and structure the props, role gate props. We have the children and allowed role and now let's get the current user role using useCurrentRole which we've just created and in here if role is not allowed role we're gonna go ahead and return, you can return whatever you want. I'm gonna reuse our form error component from ./.formerror or components form error. And in here I'm simply gonna pass a message which will say you do not have permission to view this content. Otherwise, we're simply going to return a fragment which renders the children which will be allowed only for the specific role.
There we go. So that's how you can create a reusable component and hide some content from some users. So now let's go ahead back inside of our admin page. So app protected admin and I just want to tell you something. I don't know if you know this or not but I hope I kind of made it clear.
The only reason this is cold protected is so I organize all of my routes inside of that. It doesn't have to be cold protected. You don't even have to put it inside of this folder, right? I only do that so I can reuse the layout. So I just want to make sure that none of you are confused by that.
So all of our routes are defined in here, in routes.ts. You can see that we don't explicitly tell what are the private routes. All routes are private by default. We only tell the middleware what the public routes are. So if you create something random in the middle of app folder, that's going to be automatically a protected route.
You don't have to put that inside of the protected folder. That just came to my attention. I hope I didn't. I hope I made that clear, right? So this protected is purely organizational.
This can be named absolutely anything. All right, hope I made that clear. Let's go back inside of the admin folder here and let's actually use that role gate component. So we actually don't need use current role here at all. You can remove that.
And let's go ahead inside of here and let's add role gate component from components out role gate so make sure you add the import for role gate here and inside you're going to write something that only admins will be able to see. So for example, I'm going to add a component here, form success, so I'm going to reuse it and I'm going to go ahead and write a message here to be, for example, you are allowed to see this content. And now we have to define which role can see that. So allow the role, let's import user role from Prisma Client here as well. So only admin will be able to see this and there we go since my role is a user I don't have permission to view this but if I go inside of my Prisma Studio here and if I change my role to admin and save this right here.
I hope I have my Prisma Studio running and if I refresh here there we go I am now allowed to see this content. If I change it back to user and save there we go I don't have permission to view this content. Perfect. So our role gate is working. And now I simply want to create an example to test the API route and a server action and show you how you can protect something in there as well.
So let's simply create a div here with a class name flex-flex-row-items-center-justify-between-rounded-lg-border-padding3-and-shadow-md so just to make it a little bit stylish. And let's add Admin Only API Route with a class name of Text Small and Font Medium. And let's add a button component from components UI button so make sure you add this import and in here I'm gonna write click to test like that great and now let's copy this and paste it below and this is gonna be admin only server action and this is going to be click to test as well great! Now let's go ahead and let's create both of them so first let's go ahead and let's create an admin only API route So let's go inside of our app folder and inside of the API here, create a new route called admin or whatever you want and inside route.ts and in here let's export asynchronous function get and let's go ahead and by default return new next response with a status of 403 so you can use next response which you have to import from next server or you can just use a response. I'm going to use next response.
I believe the TypeScript is better for next response. Right, so by default this will always return 403. So let's go ahead back inside of our page and let's add on click to try and go to this route so I'm gonna go inside of protected admin page and let's go ahead and create const on API route click is very simply oops it's going to call fetch to slash API slash admin dot then we're gonna get the response. And then if response dot okay, I'm gonna go ahead and just console log, okay. Else console error forbidden for now.
So just this, right? And now Let's try it out. So I'm going to add onclick to this button for admin only API routes onclick. On API route click and let me just prepare my console here. So when I click here, there we go.
I have forbidden, you can see 403 error right here. So how do we allow this route for someone? So now this is what we're gonna do. We're gonna go inside of our route here and which method can we use inside of an API route to get the current role? Well, we can use the one which we used in server components.
So this right here which we've created, the lib folder auth, this can be used in server components, server actions and API routes. So basically anything server side and those hooks that we created can be used in all client side. So if you want to use this inside of your route, you can do that. So all you have to do is get const role await current role from libauth. If role is equal user role from prisma-client.admin in that case return you next response null with a status of 200.
Like that. That's it. That is all you have to do to check the current role and if they have permission to see this. So now if I go ahead again I'm still gonna get an error in my console log here so let me just expand this but if I go ahead and change my role to admin here and save and try this out again so let me refresh there we go I am allowed to see the content I will test this and there we go I got okay perfect So now I just want to prettify this with toasts. So let's just go ahead and add a new component inside of here.
So let me just open a new terminal. MPX, chat CNUI at latest add Sonar, or you can manually install Sonar if you've already used it before. So it's just, you know, for some toast information here. Then you have to go inside of the app folder layout here and import toaster from add slash components UI Sonar and simply add a toaster somewhere in your project. For example, I'm gonna add it here And then you can go back inside of the admin page right here.
And in here, instead of doing this, we can add toast.success. You can import toast from Sonar itself like that. So toast.success allowed API route. And in here, we're gonna do toast.error forbidden API route. There we go like that.
So in a click here, there we go. Allowed API route. If I go ahead and change this back to user and click again forbidden API route perfect And it is exactly the same for server action, right? So let's just try it out. So let's go ahead and create actions, admin.ts, use server, expert const Asynchronous, sorry, admin asynchronous function.
And in here, let's get the role using await. Current role, if role is equal to user role.admin, we're going to return. Actually we can, well, we have, we can do this return error forbidden, otherwise let's return success allowed, like this. Make sure you've marked it as use server here. Go back to the page here and in here, let's do const on server action click.
So that's gonna tell the admin, make sure you import admin from actions admin like that dot then we're gonna get the data if data dot error toast dot error data dot error if data dot success toast dot success data success as simple as that and let's use the on server action click here for this button, so admin only server action and let's try it out So I should be getting an error for the server action. There we go. Oh, I'm not getting an error Let's see what's going on. Maybe I have to refresh No, I'm still allowed. All right, so we're gonna check that out.
So admin, if role is user role.admin. Oh, my apologies. This is allowed. And this one is forbidden. My apologies, I did the opposite logic.
So let's try it out now. There we go, forbidden server action. And yeah, let's also change just the results. So this is a success and this is an error like that okay sorry about that there we go so now forbidden server action if I get my Prisma Studio and change my role to admin and save and try the server action again there we go allowed server action perfect So now you know how to use our reusable hooks, reusable components and reusable libs inside of server actions, route handlers, server components and client components. All that's left is to create the settings page so I can show you how you can create some best practices for change password, for example, where a user has to verify their password first, how to change email, and we're also gonna create a little toggle to turn a two-factor authentication on or off.
Great, great job!