DEV Community

Cover image for Create a Simple Authentication System for your Next Application with Clerk
DrPrime01
DrPrime01

Posted on

Create a Simple Authentication System for your Next Application with Clerk

Authentication is a process through which users verify their identity before accessing a system. In web applications, authentication is primarily implemented with email/username and password. The auth process has been further simplified with Google authentication, which enables users to sign up or sign in to a web application with their Gmail account. Google's auth approach has proven useful in many applications, especially for users who frequently forget their credentials. The limitation of Google auth is that you can only sign in or sign up with your Gmail account and nothing else. However, Clerk provides more than one option to log in or sign up, even including Google auth as one of its several options.
Clerk is a powerful authentication tool for your Next application that provides more than one way to authenticate your users. In this article, you will learn how to create a simple auth system for your Next app with Clerk.

What is Clerk?

Clerk is an authentication solution for your Next application. It is a suite of embeddable UIs and flexible APIs created to make your app’s authentication process seamless. It features customizable SignIn and SignUp components, multifactor authentication, advanced security, session management, social sign-on, email and SMS OTPs, webhooks, and the recent magic link auth. Compared to other auth solutions, Clerk tops the game as it is an all-in-one auth solution for your application while others focus on only a single auth system. Also, it is not limited to Next applications. Clerk has SDKs for modern mobile and web frameworks like React, Remix, Astro, Expo, and iOS.

Setting up Clerk

Into the course of the article, create a Next app, but before you begin, mark out the prerequisites:

  • A basic understanding of React/Next
  • A Clerk account.

Although you will integrate Clerk in a Next app, you only need a basic understanding of React to get your app working. You won’t be getting into the intricacies of a Next app.

Step 1: Creating a Next app

  • Create a new folder named clerk-demo on your machine, drag it into your code editor, and open the terminal.
  • In the terminal, run this command npx create-next-app@latest . The “.” at the end allows the next app to be created in the already opened folder, clerk-demo, in your code editor.
  • Run npm run dev to start your newly created Next app in development mode and open the app on your browser using the provided domain. It’s usually localhost:3000

Step 2: Setting up your Clerk account

  • Go to clerk.com and sign up with your GitHub or Google account.
  • After signing up, you’ll be redirected to a page to create a new application.
  • Create a new application, give it a name, clerk-demo, and select your preferred auth options. There are about 15 or more auth options to choose from. Clerk allows you to see the UI of your selected auth options in real-time to help your decision-making.
  • After selecting the auth options and clicking the Create Application button, you’ll be redirected to the app’s dashboard.
  • Select your framework (NextJS) and you’ll see information on integrating Clerk.
  • Copy the installing command and run it in your code editor’s terminal, npm install @clerk/nextjs
  • Copy the environment variables
  • Create a .env.local file in the root of your Next app’s folder and paste the environment variables
  • Copy the middleware.ts code, create a file with the name, and paste it there.
  • Finally, update your layout.tsx file in the app folder of your application with the code from the Clerk dashboard. The ClerkProvider wrapper enables your app to access the Clerk authentication APIs and use the Clerk components.

Now, that you’ve successfully set up Clerk, you may proceed to the Clerk’s authentication implementation phase.

Implementing Authentication

Implementing authentication with Clerk is easy. There are no complex configurations to be done except to use the ready-made components in the right places.

Step 1: Defining your Sign-in and Sign-up routes

Clerk, by default, redirects your users to another link to sign in but if you want to displace that behaviour with your predefined sign-in and sign-up routes, open your .env.local file again and add these variables:

    /* Already existing variables */

    NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
    NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
Enter fullscreen mode Exit fullscreen mode

Step 2: Positioning the Clerk Auth Components

If you remember, you copied the layout.tsx page from your Clerk dashboard and updated your app’s layout.tsx file with it. The <SignedIn />, <SignedOut />, <SignInButton />, and <UserButton /> components should be moved from the layout file to a <Navbar /> component which you will create in your app.

  • In the root folder of your Next app, create a /components folder, and create a Navbar.tsx file in it.
    /components/Navbar.tsx

    export default function Navbar() {
      return (
        <nav>
          <SignedOut>
            <SignInButton />
          </SignedOut>
          <SignedIn>
            <UserButton />
          </SignedIn>
        </nav>
      )
    }
Enter fullscreen mode Exit fullscreen mode
  • Then, import the Navbar.tsx file into your layout.tsx file and render the component between the body element, right about the children prop. In this way, all the children elements will have it right above them.
    import type { Metadata } from "next";
    import { Inter } from "next/font/google";
    import "./globals.css";
    import { ClerkProvider } from "@clerk/nextjs";
    import Navbar from "@/components/Navabr";

    const inter = Inter({ subsets: ["latin"] });
    export const metadata: Metadata = {
      title: "Miles Rentals",
      description: "Generated by create next app",
    };
    export default function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode;
    }>) {
      return (
      <ClerkProvider>
        <html lang="en" suppressHydrationWarning>
          <body className={`${inter.className} noScrollBar`}>
            <Navbar />
            {children}
          </body>
        </html>
      </ClerkProvider>
      );
    }
Enter fullscreen mode Exit fullscreen mode

Step 3: Create the Sign-in and Sign-up pages

  • For the SignIn and SignUp pages, create a /sign-in/[[…sign-in]] and a /sign-up/[[…sign-in]] folder inside the /app folder of your Next app, create a new page.tsx file in each, and paste this code in them.

In this way, users can navigate to /sign-in or /sign-up to log in or sign up, respectively.

    /sign-in/[[...sign-in]]/page.tsx

    import { SignIn } from "@clerk/nextjs";
    import React from "react";
    const page = () => {
      return (
        <main className="auth-page">
          <SignIn />
        </main>
      );
    };
    export default page;

    // replace <SignIn /> with <SignUp /> for the sign-up page.tsx file

    // NB: [[...sign-in]] is a folder within the sign-in folder where the sign-in page.tsx file is hosted. 
   //Ensure the folder is named with the square brackets and the "..."
Enter fullscreen mode Exit fullscreen mode

With these few lines of code, you have successfully implemented Clerk authentication for your application. Now, explore other authorization concepts you can implement in your application with Clerk.

Managing User Sessions

Clerk has several hooks that give you access to the Clerk object. Among these hooks is the useSession() hook. The useSession() hook manages access to the current user’s session object and other helpers for setting the active session. The helpers are isLoaded, isSignedIn, and session.

    import { useSession } from '@clerk/clerk-react'

    const { isLoaded, session, isSignedIn } = useSession()
Enter fullscreen mode Exit fullscreen mode

isLoaded is a boolean that checks if Clerk is successfully loaded in your app. This boolean is particularly useful when you want to use any of the numerous Clerk methods in your app. To prevent errors, you can check if Clerk is loaded before using any of its methods and objects. The isSignedIn boolean checks if a user is signed in or not. As you’ll see in the next section, this boolean can be useful in creating protected routes. The last helper in the hook is the session object. This object lets you access important information about the currently active section. For instance, you can help the users track the time spent during each session.

    import { useSession } from '@clerk/clerk-react'

    export default function Home() {
      const { session } = useSession()

      return (
        <div>
          <p>This session has been active since {session.lastActiveAt.toLocaleString()}
          </p>
        </div>
      )
    }
Enter fullscreen mode Exit fullscreen mode

Other useful information you can get from the session object includes user, publicUserData, status, lastActiveToken, createdAt, updatedAt, expireAt, actor, end(), touch(), getToken(), and many others. See the Clerk session doc for a comprehensive list.

Protecting Routes

Protected routes are routes in an application where access is restricted to authenticated users only. In your application, you can expose some routes like the landing page, about page, and contact us page; and restrict some to authenticated users only like the user dashboard page or the settings page. Clerk has useful hooks that make it possible to create Protected routes for your application, including the useUser() hook.
The useUser() hook gives access to the current user’s user object and some helpers. It returns isSignedIn and isLoaded booleans, like the useSession hook, and a user object. With the information provided by this hook, you can create a ProtectedRoute wrapper around some pages of your application and prevent unauthorized access.

    // ProtectedRoute.tsx

    import { useUser } from "@clerk/clerk-react";
    import { redirect } from "next/navigation";

    export default function ProtectedRoute({
      children,
    }: {
      children: React.ReactNode;
    }) {
      const { isSignedIn, isLoaded } = useUser();
      if (!isLoaded) {
        return <LoadingComponent />;
      }
      if (!isSignedIn) {
        redirect(`/sign-in`);
      }
      return <>{children}</>;
    }
Enter fullscreen mode Exit fullscreen mode

You can then use this ProtectedRoute component to wrap any page that requires authentication:

    // UserDashboard.tsx

    import ProtectedRoute from "@/components/custom-routes/ProtectedRoute";
    export default function UserDashboard() {
      return <ProtectedRoute>{/* Your User Dashboard component */}</ProtectedRoute>;
    }
Enter fullscreen mode Exit fullscreen mode

Role-Based Access Control (RBAC)

In addition to basic authentication, you might want to implement Role-Based Access Control to restrict access based on user roles. Clerk provides a way to implement RBAC using user metadata. Here's how you can extend your protected routes to include role-based access:

  • Configure the session token: Add user metadata to the session token in the Clerk Dashboard under Sessions > Customize session token.
  • Create a TypeScript definition for roles: Define your roles in a globals.d.ts file.
  • Create a helper function to check roles
    import { Roles } from '@/types/global'
    import { auth } from '@clerk/nextjs/server'

    export const checkRole = (role: Roles) => {
      const { sessionClaims } = auth()
      return sessionClaims?.metadata.role === role
    }
Enter fullscreen mode Exit fullscreen mode
  • Extend your ProtectedRoute component to include role checks
    import { useUser } from "@clerk/clerk-react";
    import { redirect } from "next/navigation";
    import { checkRole } from "@/utils/roles";

    export default function ProtectedRoute({
      children,
      requiredRole,
    }: {
      children: React.ReactNode;
      requiredRole?: Roles;
    }) {
      const { isSignedIn, isLoaded } = useUser();

      if (!isLoaded) {
        return <LoadingComponent />;
      }

      if (!isSignedIn) {
        redirect(`/sign-in`);
      }

      if (requiredRole && !checkRole(requiredRole)) {
        redirect(`/unauthorized`);
      }

      return <>{children}</>;
    }
Enter fullscreen mode Exit fullscreen mode

Now you can protect routes based on both authentication and roles

    import ProtectedRoute from "@/components/custom-routes/ProtectedRoute";

    export default function AdminDashboard() {
      return (
        <ProtectedRoute requiredRole="admin">
          {/* Your Admin Dashboard component */}
        </ProtectedRoute>
      );
    }
Enter fullscreen mode Exit fullscreen mode

This implementation allows you to create a flexible system for protecting routes based on both authentication status and user roles, enhancing the security of your application.

Customization Option

Clerk provides many customization options for your application. You can customize the SignIn and SignUp component to use your logo rather than Clerk’s logo.

  • Navigate to your Clerk dashboard, right-click on your app’s name, and click the settings icon. There, you’ll see different customization settings to make Clerk feel more like yours.

Besides the logo change on the SignIn and SignUp components, you can also customize the theme of all Clerk components to match your application’s theme at once or individually. Clerk provides an appearance prop in the ClerkProvider wrapper component, which you use in your layout.tsx file. You can also use the appearance prop in any Clerk component to customize it individually. The appearance prop accepts the following properties:

  • baseTheme: The base theme to be used in your component. See the themes documentation for available themes.
  • variables: An object to define the styles like color, font size, font weight, and much more.
  • layout: This property is particularly useful for customizations that are hard to implement with CSS. It gives you access to configuration options that affect the layout of Clerk components. See layout for more information.
  • elements: If you want to override the default style of a specific element, this property is for you. For more information, see elements.

All of these properties are optional so you can use 1 or 2 or all of them, independently. The code sample below illustrates a sample use of the appearance prop in the ClerkProvider wrapper component.

    import type { Metadata } from "next";
    import { ClerkProvider } from "@clerk/nextjs";
    import { dark } from "@clerk/themes";
    import localFont from "next/font/local";
    import "./globals.css";
    import { cn } from "@/lib/utils";

    const geistSans = localFont({
      src: "./fonts/GeistVF.woff",
      variable: "--font-geist-sans",
      weight: "100 900",
    });
    const geistMono = localFont({
      src: "./fonts/GeistMonoVF.woff",
      variable: "--font-geist-mono",
      weight: "100 900",
    });
    export const metadata: Metadata = {
      title: "Clerk Auth",
      description: "Code sample of using the appearance prop",
    };
    export default function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode;
    }>) {
      return (
        <ClerkProvider
          appearance={{
            baseTheme: dark,
            variables: {
              colorPrimary: "#3371FF",
              fontSize: "16px",
            },
          }}
        >
          <html lang="en" suppressHydrationWarning>
            <body
              className={cn(
                "antialiased min-h-screen",
                geistSans.variable,
                geistMono.variable
              )}
            >
             {children}
            </body>
          </html>
        </ClerkProvider>
      );
    }
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this article, you learned how to implement a basic but strong authentication system in your Next.js application using Clerk. You saw how Clerk can provide an all-in-one solution which is both robust and very easy to implement. You started by creating a Clerk account and linking it with the Next.js application. Then you integrated the primary authentication functionality: sign-in and sign-up. Later, you dove deeper into more advanced topics, like user session management, protecting routes, and even Role-Based Access Control. You learned how Clerk's customizability allowed you to tailor the authentication experience to your application. You have seen how easily you can implement different themes and layouts, making every adjustment necessary to fit your app's design.
With web application development, you will need a secure, customizable, full-featured authentication system—that's what Clerk offers with much simplicity in implementation hence highly recommended for your Next.js projects. You now know how to effectively use Clerk's authentication solution for your projects, whether small or large scalable applications. Whether you're building a small project or a large-scale application, you now have the knowledge to use Clerk's authentication solution effectively. You're equipped with the tools and flexibility to meet your authentication needs efficiently and securely in your Next.js applications.

Top comments (0)