Skip to main content

Integrate authentication into Next.js

This guide shows how to create a simple Next.js application and secure it with authentication powered by Ory. The guide provides the setup for using Ory Network, but the code can be used with both Ory Network and self-hosted Ory software.

This guide is perfect for you if:

  1. You have Next.js installed.
  2. You want to build an app using Next.js.
  3. You want to give access to your application to signed-in users only.

Before you start, watch this video to see the user flow you're going to implement:

info

You can find the code of the sample application here.

Create Next.js app

First we create a new Next.js 13 project:

npx create-next-app@latest --typescript
cd <your-project-directory>

create-next-app Settings used in this tutorial:

✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use `src/` directory with this project? … Yes
✔ Would you like to use experimental `app/` directory with this project? … No
✔ What import alias would you like configured? … @

Install Ory

Ory provides integration tools that quickly combine Ory with Next.js. Let's install Ory's SDK, used to make API calls to Ory, and Ory's Integration Tools for JavaScript frameworks:

npm i --save @ory/integrations @ory/client

and create a special route in [...paths].js at pages/api/.ory/.

// @ory/integrations offers a package for integrating with Next.js in development. It is not needed in production.
// @ory/integrations works in a similar way as ory tunnel, read more about it what it does:
// https://www.ory.sh/docs/guides/cli/proxy-and-tunnel
import { config, createApiHandler } from "@ory/integrations/next-edge"

// We need to export the config.
export { config }

// And create the Ory Network API "bridge".
export default createApiHandler({
fallbackToPlayground: true,
dontUseTldForCookieDomain: true,
})

This route connects your Next.js app with Ory's APIs and ensures that all cookies and credentials are set up. Without it, your Next.js application won't be able to talk to Ory's APIs.

tip

This route from ory/integrations works in a similar way as ory tunnel, read more about it here. It is only required when developing locally. It can be removed when going to production with a custom domain. More details in the "Go to production" section below.

Require login to access the home page

Next we add a session check to the default home page of the Next.js example application.The highlighted code is what we added to check whether the user is signed in, and redirect them to the login page if not:

import Head from "next/head"
import Image from "next/image"
import { Inter } from "next/font/google"
import styles from "@/styles/Home.module.css"
import { useEffect, useState } from "react"
import { useRouter } from "next/router"

import { Configuration, FrontendApi, Session, Identity } from "@ory/client"
import { edgeConfig } from "@ory/integrations/next"

const ory = new FrontendApi(new Configuration(edgeConfig))

// Returns either the email or the username depending on the user's Identity Schema
const getUserName = (identity: Identity) =>
identity.traits.email || identity.traits.username

const inter = Inter({ subsets: ["latin"] })

export default function Home() {
const router = useRouter()
const [session, setSession] = useState<Session | undefined>()
const [logoutUrl, setLogoutUrl] = useState<string | undefined>()

useEffect(() => {
ory
.toSession()
.then(({ data }) => {
// User has a session!
setSession(data)
// Create a logout url
ory.createBrowserLogoutFlow().then(({ data }) => {
setLogoutUrl(data.logout_url)
})
})
.catch(() => {
// Redirect to login page
return router.push(edgeConfig.basePath + "/ui/login")
})
}, [router])

if (!session) {
// Still loading
return null
}

return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.description}>
<p>Hello, {getUserName(session?.identity)}</p>
<div>
<p className={styles.description}>
<a href={logoutUrl}>Log out</a>
</p>
</div>
<div>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{" "}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className={styles.vercelLogo}
width={100}
height={24}
priority
/>
</a>
</div>
</div>

<div className={styles.center}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
<div className={styles.thirteen}>
<Image
src="/thirteen.svg"
alt="13"
width={40}
height={31}
priority
/>
</div>
</div>

<div className={styles.grid}>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2 className={inter.className}>
Docs <span>-&gt;</span>
</h2>
<p className={inter.className}>
Find in-depth information about Next.js features and&nbsp;API.
</p>
</a>

<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2 className={inter.className}>
Learn <span>-&gt;</span>
</h2>
<p className={inter.className}>
Learn about Next.js in an interactive course with&nbsp;quizzes!
</p>
</a>

<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2 className={inter.className}>
Templates <span>-&gt;</span>
</h2>
<p className={inter.className}>
Discover and deploy boilerplate example Next.js&nbsp;projects.
</p>
</a>

<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2 className={inter.className}>
Deploy <span>-&gt;</span>
</h2>
<p className={inter.className}>
Instantly deploy your Next.js site to a shareable URL
with&nbsp;Vercel.
</p>
</a>
</div>
</main>
</>
)
}

Run your Next.js app

Great, that's it. Let's run your application. Set up your environment variables to connect with Ory's APIs

export NEXT_PUBLIC_ORY_SDK_URL=https://$PROJECT_SLUG.projects.oryapis.com

and start the Next.js development server:

npm run dev

Then open http://localhost:3000 in your browser. You are presented with Ory's Sign In page. Let's click on sign up and create your first user.

Go to production

When upgrading to production, you need to set up a custom domain and configure your Next.js app to use it. Once you have configured a custom domain and deployed your application the integrations package is no longer needed:

- import { edgeConfig } from "@ory/integrations/next"
- const ory = new FrontendApi(new Configuration(edgeConfig))
+ const basePath = process.env.NEXT_PUBLIC_ORY_SDK_URL;

+ const ory = new FrontendApi(
+ new Configuration({
+ basePath: basePath,
+ baseOptions: {
+ withCredentials: true,
+ },
+ })
+ );

...

- return router.push(edgeConfig.basePath + "/ui/login")
+ return router.push(basePath + "/ui/login");

...

You can now also remove the pages/api/.ory/ route.

Release your app bundled with Ory to production using:

# This is a public Ory Network Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up to Ory Network at
#
# https://console.ory.sh/registration
#
# and create a free Ory Network Project to see your own configuration embedded in code samples.
npx vercel deploy --prod -e ORY_SDK_URL=https://$PROJECT_SLUG.projects.oryapis.com