Learn how to create protected routes using React Context as well as how using Clerk makes this process easier.
In part one of this series, you learned about Next.js API routes and how to protect API routes with JWT authentication. In this part, you'll learn how to create protected routes using React Context as well as how using Clerk makes this process easier.
To get started, a starter app already has been made that you can clone from GitHub:
The app will start running at localhost:3000.
The posts page can be found at http://localhost:3000/posts, which shows a list of all the posts.
This page has a companion API route that returns all the posts in JSON:
The data for posts is stored in data.js.
The /api/auth/login route implements JWT authentication and returns a JWT when the correct username and password combination is supplied:
Finally, the /api/users/me route returns the currently authenticated user, provided a valid JWT is passed in the header:
The user profiles can also be found in data.js.
The goal of the article is to protect the /api/posts API route and the /posts page using the JWT authentication strategy. In part one, you saw how JWT authentication can be added to API routes using the jwt.verify method. However, it's resource intensive to manually verify and decode the JWT in every single API route. It's also tedious to manually include the authentication header in every request that you make from a page.
In this article, you'll learn how to "share" an authenticated session across all pages by using an AuthContext. You'll also refactor the JWT verification to a withAuth wrapper that will make it easy to protect API routes. Finally, you'll see how using Clerk makes this process smooth and seamless.
AuthContext is simply a React Context that will make it easy to pass required authentication parameters throughout the app. To implement this, first install the required libraries:
js-cookie will be used to store the JWT in the browser's cookie. axios makes it easy to preconfigure a default API service with headers. You'll use this to include the token in the headers once the user logs in.
Create a file named api.js in the project root:
Here, an instance of axios is created that will be used to make API calls later.
Create the file auth_context.js:
The most important bits in the above Context are the fetchUserFromCookie, login, and logout functions. The fetchUserFromCookie function fetches the token from the cookie and sets the authorization header in the default api instance.
It then makes a call to /api/users/me to retrieve and store the authenticated user. The login function logs in the user through the /api/auth/login route and stores the token in the cookie. The logout function deletes the user, the cookie, and the authorization header.
Finally, create the withAuth function that'll protect the API routes by wrapping the handlers:
You can now modify pages/api/posts.js to include withAuth:
Note that only the posts by the logged in user are returned. The logged in user is found through req.auth.user.
Let's now create the login page. First, add Formik and Yup. These are not strictly required but will help in creating the login form.
Create pages/login.js:
This page uses the login function described above to log in the user.
Now update pages/posts.js to make use of AuthContext:
Finally, update pages/_app.js to wrap everything in AuthProvider:
Start the server with yarn dev and visit http://localhost:3000/login. Log in with the credentials (you can find the username in data.js and the password is "password") and you'll be redirected to the posts page. You can verify that you're only seeing posts from the logged in user.
The final app for this section can be found in the manual branch of the GitHub repo.
In this section, you'll replace the manual authentication with Clerk. Before starting with Clerk, you should revert the changes you've made so far. You can simply run git stash && git clean -fdx to stash your changes.
Once the application is created, go to the API keys page and copy the frontend API key, the backend API key, and the JWT verification key. Paste these into .env:
Install the required library:
Wrap pages/_app.js with Clerkprovider:
The publicPages array decides which pages will not be protected under authentication. For a protected page, if the user is not signed in, they will be redirected to the login page.
Create the file middleware.js in the project route:
Modify pages/api/posts.js to include the getAuth function that authenticates the user with Clerk:
In the Clerk dashboard, go to the Users page and create a test user.
After the user is created, open the record and copy the ID as shown below.
Open data.js and replace the numeric id field of any one user and the userId field in the posts array:
Run the server again with yarn dev. Visit http://localhost:3000/posts and you should be redirected to Clerk's login page.
After logging in, you'll be redirected back to the /posts page. Verify that you can see only the posts corresponding to the logged in user.
Protecting Next.js routes with authentication is a vital part of developing any web app. However, manually creating authentication mechanisms can be tedious and time-consuming. A solution like Clerk’s Next.js authentication comes with all the bells and whistles so that you don't need to worry about auth and can focus on the core app instead.