Next.js (App Router)¶
Next.js is a highly popular JavaScript framework for Server-Side Rendering. The Fief JavaScript client provides tools dedicated to Next.js. Let's see how to use them!
Install the client¶
The big picture¶
To make integration seamless, we use a middleware to handle the authentication process. When the user is successfully authenticated, this middleware will set headers that can be read by layouts and pages to retrieve user information.
graph LR
subgraph Next.js
M[Middleware]
P[Page]
end
F[Fief]
RQ((Request))
RQ --> M
M -- not authenticated --> F
F -- callback --> M
M -- authenticated --> P
Middleware¶
In most recent versions, Next.js introduced Middleware. It's a useful mechanism allowing us to define logic before every request. Next.js team designed it to be highly performant: it's executed by their Edge Runtime technology, a special runtime different from Node.js.
In Fief implementation, we'll use the Middleware to check if the user is authenticated and if they have the right scope and permissions.
Besides, it'll also automatically handle special routes dedicated to authentication:
/login
will redirect to Fief authentication page./auth-callback
will complete the OAuth2 callback and set the session cookie;/logout
will clear the session cookie and redirect to Fief logout.
Pages and layout¶
The actual pages and layouts of our app will then only have to read the information set by the middleware on the server side to get information about the user and render the components, passing the data through props as needed.
Configure your project¶
This is for you if...
- You use Next.js with App Router.
Prerequisites
- Bootstrap a Next.js project as described in Automatic Setup section of the Next.js documentation.
- Allow the following Redirect URI on your Fief Client:
http://localhost:3000/auth-callback
1. Create a fief
module¶
Let's create a fief.ts
module at the root of the project. It'll contain the basic instantiation of Fief helpers.
import { Fief, FiefUserInfo } from '@fief/fief';
import { FiefAuth, IUserInfoCache } from '@fief/fief/nextjs';
export const SESSION_COOKIE_NAME = "user_session"; // (1)!
export const fiefClient = new Fief({ // (2)!
baseURL: 'http://localhost:8000',
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
requestInit: { next: { revalidate: 3600 } },
});
export const fiefAuth = new FiefAuth({ // (3)!
client: fiefClient, // (4)!
sessionCookieName: SESSION_COOKIE_NAME, // (5)!
redirectURI: 'http://localhost:3000/auth-callback', // (6)!
logoutRedirectURI: 'http://localhost:3000', // (7)!
});
-
Define a session cookie name constant
We'll use a cookie to maintain the user session.
For convenience, we set its name in a constant.
-
Fief client instantiation
As we showed in the standard JavaScript section, we instantiate here a Fief client here with the base tenant URL and client credentials.
-
Fief helper for Next.js
This is the helper doing the tedious work for you with Next.js.
We'll review here the required parameters. You can have a complete list of parameters in the reference documentation.
-
The Fief client
The first mandatory parameter is the Fief client we just created above.
-
The session cookie name
We pass the constant we defined above.
-
Absolute redirect URI
After the user has succesfully authenticated on Fief, our user will need to be redirected to our application so we can get the access token and set our session.
This constant is an absolute URL to the
/auth-callback
route the Middleware will handle. -
Absolute redirect URI after logout
When logging out, the user is redirected to Fief so the session stored on Fief's side can also be cleared.
After that, Fief will redirect the user to your application. This parameter allows you to set the page where they should be redirected.
Typically, this can be the home page of your application.
2. Add the Middleware¶
If not already, create a middleware.ts
module at the root of the project.
import type { NextRequest } from 'next/server';
import { fiefAuth } from './fief'; // (1)!
const authMiddleware = fiefAuth.middleware([ // (2)!
{
matcher: '/private', // (3)!
parameters: {},
},
{
matcher: '/castles/:path*', // (4)!
parameters: {
permissions: ['castles:read'], // (5)!
},
},
{
matcher: '/:path*', // (6)!
parameters: {
optional: true,
},
},
]);
export async function middleware(request: NextRequest) { // (7)!
return authMiddleware(request); // (8)!
};
-
Import the
fiefAuth
instance fromfief
moduleThis is the instance of FiefAuth we created in the previous section.
-
Create an instance of the middleware
Using the
fiefAuth.middleware
method, we create an instance of a Next.js Middleware that'll check the authentication on our routes.All it needs is an array of routes, consisting of the elements described below.
-
Match the
/private
pathBy doing this, we tell the middleware to ensure a user is authenticated before accessing the
/private
page.If they're not, they will be automatically redirected to the Fief login page.
-
Match all the routes starting with
/castles
The
matcher
property follows the same syntax as the one supported by Next.js.It means that you can match a single path, a group of paths or a specific pattern.
Here, we match all the routes starting with
/castles
. We'll be sure that the user is authenticated before accessing those routes. -
Require a permission to access
/castles
routesYou can add
AuthenticateRequestParameters
to a path matcher.Here, we require the user to have
castles:read
permission to access those routes.If they don't have the required permission, the middleware will redirect them to the
/forbidden
page. -
Catch all the other routes
To be able to get the user information even for routes that don't require authentication (think, for example, a home page where you render a header with a user menu), we need a last matcher that catches all the route.
It ensures the middleware does its job and set the necessary headers for the pages and layouts. Notice how we set the
optional: true
option: it means we won't redirect the user to the login page if they're not authenticated. -
Define the Middleware function
This is the standard way to define a Next.js Middleware.
-
Call the
authMiddleware
functionYou can now call the
authMiddleware
function with therequest
object.This function returns a
NextResponse
, so you can very well add your own middleware logic if needed to further customize it.
Don't mix it with default config
matcher
The Next.js Middleware documentation shows you how to filter the execution of your Middleware using path matchers.
When using Fief middleware, it's better to avoid it to make sure the authentication logic is called on the routes you defined.
Indeed, let's imagine you have this configuration:
const authMiddleware = fiefAuth.middleware([
{
matcher: '/private',
parameters: {},
},
]);
// ❌ Don't do this
export const config = {
matcher: ['/another-route'],
}
The authentication middleware will never be called in this configuration, because Next.js will only run the Middleware for /another-route
, not /private
.
4. Create a forbidden
page¶
When the user tries to access a page they are not authorized to see, e.g. if they lack a scope or a permission; the Middleware will automatically render the pages/forbidden.tsx
page.
You should implement it to show a useful error message to the user. It can be as simple as the following:
export default async function Page() {
return (
<h2>Sorry, you are not authorized to access this page.</h2>
)
}
5. Build a root layout¶
The root layout is useful to render common components in all parts of your application. Typically, you may want to render a header showing the authenticated user or a login button.
It can look like this:
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import { fiefAuth } from '@/fief' // (1)!
import Header from '@/components/Header/Header'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default async function RootLayout({
children,
}: {
children: React.ReactNode
}) {
const userinfo = await fiefAuth.getUserInfo() // (2)!
return (
<html lang="en">
<body className={inter.className}>
<Header userinfo={userinfo} /> {/* (3)! */}
{children}
</body>
</html>
)
}
-
Import the
fiefAuth
instance fromfief
moduleThis is the instance of FiefAuth we created at the beginning.
-
Get user information
Use the
getUserInfo
method to retrieve information about the authenticated user, ornull
if not authenticated. -
A simple demo
Header
componentThis is a simple demo component. Notice how we pass down user information as props.
Good to go!¶
Our project is now ready! You can run it with:
If you go to http://localhost:3000, you'll see the index public page.
If you click on the Private page link in the header, you'll be automatically redirected on Fief authentication pages. After a successful login, you'll be taken back to the /private
page.
As you can see, we can show the email address of the current user. This is done quite simply using again getUserInfo
.
import { fiefAuth } from '@/fief'
export default async function Page() {
const userInfo = await fiefAuth.getUserInfo()
return (
<h2>You are authenticated. Your user email is {userInfo?.email}</h2>
)
}
Now, let's try to go to the Castles / Index page. If you've not added the correct permission to the user, you'll probably see the Forbidden page:
If you assign the correct permission to the user and authenticate again, you'll see the actual page.
As you can see, we are able to show the list of permissions granted to the user. It can be useful to hide or show parts of the UI depending on those permissions.
You can easily access them using the getAccessTokenInfo
method.
import { fiefAuth } from '@/fief'
export default async function Page() {
const accessTokenInfo = await fiefAuth.getAccessTokenInfo()
return (
<>
<h2>You have the following permissions:</h2>
<ul>
{accessTokenInfo?.permissions.map((permission) => <li key={permission}>{permission}</li>)}
</ul>
</>
)
}
If you click on Logout, your session will be cleared and you will be redirected to the index page.
Summary¶
Your Next.js project is now ready and can easily control the authentication and permissions of your users! Here are the most important things to remember while developing your app:
- If you want to protect a page, add it to the
authMiddleware
paths inmiddleware.ts
. - If you want to access user information or permissions, use
getUserInfo
andgetAccessTokenInfo
methods.
If you want to go further and customize more aspects, like the path to login or logout routes, be sure to check the library reference.