# RBAC & Private Routes in Next.js with Clerk using NextShield.

Hello everyone! I'm Julián. 
![hello](https://media.giphy.com/media/hvRJCLFzcasrR4ia7z/giphy.gif) 

The past few weeks  [I've introduced my library called NextShield](https://blog.imjulian.com/rbac-and-private-routes-in-nextjs) and show an  [example using it with Supabase](https://blog.imjulian.com/nextshield-supabase). Today's example is using  [NextShield](https://imjulianeral.github.io/next-shield/) with [Clerk](https://clerk.dev/), so let's get started 🚀.

![writing fast.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634784544155/1IPJBFbPT.gif)

## Get a Clerk account.

- Go to [Clerk's site](https://clerk.dev/) and click on `Sign Up`:

![clerk sign up.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634785092097/d8LKr7PsR.png)

- And create your account:

![clerk create account.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634785142446/2p7EAYAt1.png)

## Create a Clerk App.

- Go to your Dashboard and click on `Create application`:

![create app](https://cdn.hashnode.com/res/hashnode/image/upload/v1634785553405/l0fFHlsrr.png)

- Choose `Standard Setup`:

![setup](https://cdn.hashnode.com/res/hashnode/image/upload/v1634785651355/AxZTRHqpw.png)

- Put your App's name and choose a color:

![app name](https://cdn.hashnode.com/res/hashnode/image/upload/v1634785877324/_A3Ij7Gjw.png)

That's it, let's create a Next.js app with NextShield.

## Create a Next.js App with NextShield.

For this tutorial you need to create a Next.js app configured with NextShield, to do so, you can follow the instructions of [my first post](https://blog.imjulian.com/rbac-and-private-routes-in-nextjs) or [just clone the repo](https://github.com/imjulianeral/nextshield-examples) (clone the main branch). 

That's it, let's set up the NextShield with Clerk.

## Set Up NextShield with Clerk.

### Instal Dependencies.

- First install the clerk package:

```bash
npm i @clerk/nextjs
```

### Get API Key.

- Then go to your dashboard and select your app:

![dashboard](https://cdn.hashnode.com/res/hashnode/image/upload/v1634787401999/8N1JyBjkP.png)

- Select the development environment:

![dev env](https://cdn.hashnode.com/res/hashnode/image/upload/v1634787480217/kzIewAuXr.png)

- Scroll down a little bit to see the `Instance Configuration` section to get the `Frontend API` key:

![frontend API key](https://cdn.hashnode.com/res/hashnode/image/upload/v1634787671424/U8uspNWqfM.png)

- Go to your Next.js app and create a `.env.local` file at the root of the project and setup the following variable with your key:

```env
NEXT_PUBLIC_CLERK_FRONTEND_API="FRONTEND API KEY HERE"
```

### Set URLs.

- Now go back to your Clerk app and click on `Settings -> URL & Redirects -> Component URLs`:
 
![redirects.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634788519463/HGkYQ6Psu.png)

Enable `Use Custom URL` on each field and write the following values:

![custom url](https://cdn.hashnode.com/res/hashnode/image/upload/v1634788690105/MXs7xaarX.png)

### Auth Providers.

- Also in `Settings -> User management` select your preferred providers.

![providers](https://cdn.hashnode.com/res/hashnode/image/upload/v1634788885730/69Md8lPaL.png)

### Bind Next.js & Clerk.

- Open your file `_app.tsx` and put the `ClerkProvider`:

```jsx
import type { AppProps } from 'next/app'

import { ClerkProvider } from '@clerk/nextjs'

import { Shield } from '@/components/routes/Shield'

import '@/styles/globals.css'

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ClerkProvider>
      <Shield>
        <Component {...pageProps} />
      </Shield>
    </ClerkProvider>
  )
}
```

- Update the `pages` directory with the following folder structure:

![pages directory](https://cdn.hashnode.com/res/hashnode/image/upload/v1634789619283/Hqz5hMbkS.png)

- Also update the `Nav` component adding the sign in & out routes:

```jsx
import Link from 'next/link'

export function Nav() {
  return (
    <nav>
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/sign-up">
        <a>Sign Up</a>
      </Link>
      <Link href="/sign-in">
        <a>Sign In</a>
      </Link>
      <Link href="/pricing">
        <a>Pricing</a>
      </Link>
      <Link href="/profile">
        <a>Profile</a>
      </Link>
      <Link href="/dashboard">
        <a>Dashboard</a>
      </Link>
      <Link href="/users">
        <a>Users</a>
      </Link>
      <Link href="/users/1">
        <a>Single User</a>
      </Link>
    </nav>
  )
}
```

- Add the following content for the `index.tsx` and `pricing.tsx` files:

```jsx
import { Layout } from '@/components/routes/Layout'

export default function PageName() {
  return (
    <Layout title="PageName">
      <h1>PageName</h1>
    </Layout>
  )
}
```

- For the `dashboard.tsx`, `users/index.tsx` and `users/[id].tsx` files, update the content to:

```jsx
import { Layout } from '@/components/routes/Layout'
import { UserButton } from '@clerk/nextjs'

export default function PageName() {
  return (
    <Layout title="PageName">
      <h1>PageName</h1>

      <div className="flex">
        <UserButton />
      </div>
    </Layout>
  )
}
```

Finally, add the Clerk components on the pages that we defined in our dashboard:

- For `sign-up/[[...index]].tsx`:

```jsx
import { SignUp } from '@clerk/nextjs'

export default function SignUpPage() {
  return (
    <>
      <SignUp path="/sign-up" routing="path" signInUrl="/sign-in" />
    </>
  )
}
```

- For `sign-in/[[...index]].tsx`:

```jsx
import { SignIn } from '@clerk/nextjs'

export default function SignInPage() {
  return (
    <>
      <SignIn path="/sign-in" routing="path" signUpUrl="/sign-up" />
    </>
  )
}
```

And the `profile/[[...index]].tsx`:

```jsx
import { Layout } from '@/components/routes/Layout'
import { UserProfile } from '@clerk/clerk-react'

export default function Profile() {
  return (
    <Layout title="Profile">
      <UserProfile path="/profile" routing="path" />
    </Layout>
  )
}
```

### Bind NextShield & Clerk.

Finally the interesting part 🥳.

- Go to the `Shield` component and import:

```js
import { useUser } from '@clerk/nextjs'
```

- Use this hook passing the parameter `withAssertions` set to `true`, this is because in that way we can access to `isLoading` and `isSignedIn` functions, which are must to have in NextShield parameters.

```js
const { user, isLoading, isSignedIn } = useUser({ withAssertions: true })
```

- Then update the `NextShieldProps` with the following:

```js
const shieldProps: NextShieldProps<
    ['/profile/[[...index]]', '/dashboard', '/users', '/users/[id]'],
    ['/', '/sign-in', '/sign-up']
  > = {
    router,
    isAuth: isSignedIn(user),
    isLoading: isLoading(user),
    privateRoutes: ['/profile/[[...index]]', '/dashboard', '/users', '/users/[id]'],
    publicRoutes: ['/', '/sign-in', '/sign-up'],
    hybridRoutes: ['/pricing'],
    loginRoute: '/sign-in',
    accessRoute: '/dashboard',
    LoadingComponent: <Loading />,
  }
```

- The complete component should look like this:

```jsx
import { useRouter } from 'next/router'
import { NextShield, NextShieldProps } from 'next-shield'
import { useUser } from '@clerk/nextjs'

import { Children } from '@/types/Components'
import { Loading } from './Loading'

export function Shield({ children }: Children) {
  const router = useRouter()
  const { user, isLoading, isSignedIn } = useUser({ withAssertions: true })

  const shieldProps: NextShieldProps<
    ['/profile/[[...index]]', '/dashboard', '/users', '/users/[id]'],
    ['/', '/sign-in', '/sign-up']
  > = {
    router,
    isAuth: isSignedIn(user),
    isLoading: isLoading(user),
    privateRoutes: ['/profile/[[...index]]', '/dashboard', '/users', '/users/[id]'],
    publicRoutes: ['/', '/sign-in', '/sign-up'],
    hybridRoutes: ['/pricing'],
    loginRoute: '/sign-in',
    accessRoute: '/dashboard',
    LoadingComponent: <Loading />,    
  }

  return <NextShield {...shieldProps}>{children}</NextShield>
}
```

- By now, you should have something like this, you only have access to public and hybrid routes when you're logged out:

![logout.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634792258670/ug_SeY_yR.gif)

- And when logged in you have access to private and hybrid routes, the public routes are inaccessible:

![login.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634792460423/V0gvh7kIb.gif)

Let's move things further and add RBAC.

### Add RBAC.

- Go to `Users` section on your dashboard and select your user:

![users](https://cdn.hashnode.com/res/hashnode/image/upload/v1634792606501/SsWP0waSS.png)

- Scroll down until the `Metadata` section and edit the public data:

![metadata](https://cdn.hashnode.com/res/hashnode/image/upload/v1634792785259/7lldn9coK.png)

- Add the following content and save the changes:

![role](https://cdn.hashnode.com/res/hashnode/image/upload/v1634792920743/BHWjBvkwk.png)

- Go back to the Next.js app and create a new type under `types/User.ts`:

```ts
export type Metadata =
  | {
      role?: string
    }
  | undefined
```

- That's it, import the type in the `Shield` component, and create a constant with that type:

```ts
  const { user, isLoading, isSignedIn } = useUser({ withAssertions: true })
  const userMetadata: Metadata = user?.publicMetadata
```

- And define the roles in NextShield 😎

```ts
const shieldProps: NextShieldProps<
    ['/profile/[[...index]]', '/dashboard', '/users', '/users/[id]'],
    ['/', '/sign-in', '/sign-up']
  > = {
    router,
    isAuth: isSignedIn(user),
    isLoading: isLoading(user),
    privateRoutes: ['/profile/[[...index]]', '/dashboard', '/users', '/users/[id]'],
    publicRoutes: ['/', '/sign-in', '/sign-up'],
    hybridRoutes: ['/pricing'],
    loginRoute: '/sign-in',
    LoadingComponent: <Loading />,
    RBAC: {
      ADMIN: {
        grantedRoutes: ['/dashboard', '/profile/[[...index]]', '/users', '/users/[id]'],
        accessRoute: '/dashboard',
      },
      EMPLOYEE: {
        grantedRoutes: ['/profile/[[...index]]', '/dashboard'],
        accessRoute: '/profile/[[...index]]',
      },
    },
    userRole: userMetadata?.role,
  }
```

- The final app should look like this:

![rbac.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634793668894/n3y7XDExf.gif)

## Protect your componentes.

You can move things one step further and protect your components depending on the user's role or a custom condition, even if that user has access to that route.

Test this code in your pricing page and tell me what you think in the comments 😏:

```jsx
import { useUser } from '@clerk/clerk-react'
import { ComponentShield } from 'next-shield'

import { Layout } from '@/components/routes/Layout'
import { Metadata } from '@/types/User'

export default function Pricing() {
  const { user, isLoading, isSignedIn } = useUser({ withAssertions: true })
  const userMetadata: Metadata = user?.publicMetadata

  return (
    <Layout title="Pricing">
      <h1>Pricing</h1>

      <ComponentShield
        showIf={isSignedIn(user) && !isLoading(user)}
        fallback={<p>You are unauthorized</p>}
      >
        <p>You are authorized</p>
      </ComponentShield>

      <ComponentShield RBAC showForRole="ADMIN" userRole={userMetadata?.role}>
        <p>You are an admin</p>
      </ComponentShield>
    </Layout>
  )
}
```

## Conclusion.

NextShield & Clerk make a perfect symbiosis!!!

![venom.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634794075204/_O8HVgXJ3.gif)

- Real-time role change.
- Redirects automatically.
- Absolute 0 flashy content.
- Perfectly protected app.

You can get the completed example [here](https://github.com/imjulianeral/nextshield-examples/tree/clerk).

 [Also you can read the complete NextShield series.
](https://blog.imjulian.com/series/next-shield) 

# Bye!

Hope you like it, see you next time!

![bye.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634794616734/jIgQIdPAq.gif)

