Integrate authentication into Next.js
This guide shows how to create a simple Next.js application and secure it with authentication powered by Ory. You can use this guide with both The Ory Network and self-hosted Ory software.
This guide is perfect for you if:
- You have Next.js installed.
- You want to build an app using Next.js.
- 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:
You can find the code of the sample application here.
Create Next.js app
- Next.js 13
- Next.js 12
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? … @
First we create a new Next.js project:
npx create-next-app@12.1 --typescript
cd <your-project-directory>
Install Ory
- Next.js 13
- Next.js 12
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.
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.
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.
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 above.
Require login to access the home page
- Next.js 13
- Next.js 12
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>-></span>
</h2>
<p className={inter.className}>
Find in-depth information about Next.js features and 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>-></span>
</h2>
<p className={inter.className}>
Learn about Next.js in an interactive course with 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>-></span>
</h2>
<p className={inter.className}>
Discover and deploy boilerplate example Next.js 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>-></span>
</h2>
<p className={inter.className}>
Instantly deploy your Next.js site to a shareable URL
with Vercel.
</p>
</a>
</div>
</main>
</>
)
}
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 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 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 (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to{" "}
<a href="https://nextjs.org">
Next.js,{" "}
{
getUserName(session?.identity)
}
!
</a>
</h1>
<p className={styles.description}>
<a href={logoutUrl}>Log out</a>
</p>
<p className={styles.description}>
Get started by editing{" "}
<code className={styles.code}>pages/index.tsx</code>
</p>
<div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}>
<h2>Documentation →</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a href="https://nextjs.org/learn" className={styles.card}>
<h2>Learn →</h2>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a
href="https://github.com/vercel/next.js/tree/canary/examples"
className={styles.card}
>
<h2>Examples →</h2>
<p>Discover and deploy boilerplate example Next.js 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}
>
<h2>Deploy →</h2>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{" "}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
)
}
export default Home
Run your Next.js app
- Next.js 13
- Next.js 12
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. are presented with Ory's Sign In page. Let's click on sign up and create your first user.
Great, that's it. Let's run your application. Set up your environment variables to connect with Ory's APIs
- macOS
- Linux
- Windows CMD
- Windows Powershell
- Self-Hosted Ory Kratos
# 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.
export ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
# 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.
export ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
# 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.
set ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
# 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.
$Env:ORY_SDK_URL = "https://{project.slug}.projects.oryapis.com"
Clone and run Ory Kratos locally
git clone --depth 1 --branch master https://github.com/ory/kratos.git
cd kratos
git checkout master
git pull -ff
docker-compose -f quickstart.yml -f contrib/quickstart/kratos/cloud/quickstart.yml up --build --force-recreate -d
and set the environment variable to the exposed port:
export ORY_SDK_URL=http://localhost:4433
and start the Next.js development server:
npm run dev
Then open http://localhost:3000 in your browser. 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