Skip to content
  • SDKs and APIs
  • Back end SDKs

Next.js Pages Router SDK

This SDK is for Next.js version 13 and later and uses Server Side Components and Pages Router.

Next.js 13 and later App Router support

Link to this section

We highly recommend using our dedicated Next.js SDK with App Router instead of this one.

Whilst technically this SDK is compatible with Next.js 13, it isn’t optimal. It leverages the use client; escape hatch, which we don’t love. It also requires a single API file to be stored in the legacy pages directory.

Other document versions

Link to this section

Register for Kinde

Link to this section

If you haven’t already got a Kinde account, register for free here (no credit card required). This will give you a Kinde domain, which you need to get started, e.g. yourapp.kinde.com.

Get started with a new project

Link to this section

The easiest way to get started is to use the Next.js starter kit. You can try out a live demo at

Install for existing project

Link to this section
Terminal window
npm i @kinde-oss/kinde-auth-nextjs

Set callback URLs

Link to this section
  1. In Kinde, go to Settings > Applications > [Your app] > View details.
  2. Add your callback URLs in the relevant fields. For example:
    • Allowed callback URLs (also known as redirect URIs) - for example http://localhost:3000/api/auth/kinde_callback
    • Allowed logout redirect URLs - for example http://localhost:3000
  3. Select Save.

Configure environment variables

Link to this section

Put these variables in a .env.local file in the root of your Next.js app. You can find these variables on your Kinde Settings > Applications > [Your app] > View details page.

  • KINDE_CLIENT_ID - Your business’s unique ID on Kinde
  • KINDE_CLIENT_SECRET - Your business’s secret key (do not share)
  • KINDE_ISSUER_URL - your kinde domain
  • KINDE_SITE_URL - where your app is running
  • KINDE_POST_LOGOUT_REDIRECT_URL - where you want users to be redirected to after logging out. Make sure this URL is under your allowed logout redirect URLs.
  • KINDE_POST_LOGIN_REDIRECT_URL - where you want users to be redirected to after authenticating.

Replace the information in the example below with your own information. You might also set different URLs depending where your project is running. They need to be the same as the callback URLs you entered in Kinde, above.

Terminal window
KINDE_CLIENT_ID=<your_kinde_client_id>
KINDE_CLIENT_SECRET=<your_kinde_client_secret>
KINDE_ISSUER_URL=https://<your_kinde_subdomain>.kinde.com
KINDE_SITE_URL=http://localhost:3000
KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
KINDE_POST_LOGIN_REDIRECT_URL=http://localhost:3000/dashboard

Set up KindeProvider

Link to this section

Kinde uses a React Context Provider to maintain its internal state in your application.

Import the KindeProvider component and wrap your application in it.

app/layout.tsx
"use client";
import { KindeProvider } from "@kinde-oss/kinde-auth-nextjs";
import Auth from "./auth";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<KindeProvider>
<html lang="en">
<body>
<Auth>{children}</Auth>
</body>
</html>
</KindeProvider>
);
}

Set up Kinde Auth Route Handlers

Create the following file /pages/api/auth/[...kindeAuth].js inside your Next.js project. Inside the file [...kindeAuth].js put this code:

import {handleAuth} from "@kinde-oss/kinde-auth-nextjs/server";
export default handleAuth();

This will handle Kinde Auth endpoints in your Next.js app.

Important: Our SDK relies on this file existing in this location specified above.

Migration guide

Link to this section

Updates since last version.

handleAuth - is now imported from “@kinde-oss/kinde-auth-nextjs/server”

import { handleAuth } from "@kinde-oss/kinde-auth-nextjs/

getKindeServerSession - functions returned from getKindeServerSession now return promises

const {getUser} = getKindeServerSession();
const user = await getUser();

Authentication

Link to this section

Sign up and sign in

Link to this section

The SDK ships with <LoginLink> and <RegisterLink> components which can be used to start the auth flow.

import {RegisterLink, LoginLink} from "@kinde-oss/kinde-auth-nextjs/components";
...
<LoginLink>Sign in</LoginLink>
<RegisterLink>Sign up</RegisterLink>

Redirecting after authentication

Link to this section

Static redirect

If you want to redirect users to a certain page after signing in, you can set the KINDE_POST_LOGIN_REDIRECT_URL environment variable in your .env.local file.

Dynamic redirect

You can also set a postLoginRedirectURL parameter to tell us where to redirect after authenticating.

import {RegisterLink, LoginLink} from "@kinde-oss/kinde-auth-nextjs/components";
...
<LoginLink postLoginRedirectURL="/dashboard">Sign in</LoginLink>
<RegisterLink postLoginRedirectURL="/welcome">Sign up</RegisterLink>

This appends post_login_redirect_url to the search params when redirecting to Kinde Auth. That means you can achieve the same result as above, like this:

import { redirect } from "next/navigation";
...
redirect('/api/auth/login?post_login_redirect_url=/dashboard')
...

This is implemented in much the same way as signing up or signing in. A component is provided for you.

import {LogoutLink} from "@kinde-oss/kinde-auth-nextjs/components";
...
<LogoutLink>Log out</LogoutLink>

Kinde Auth data

Link to this section

getServerSideProps - getKindeServerSession

Link to this section

You can get an authorized user’s Kinde Auth data from getServerSideProps using the getKindeServerSession helper.

Example:

import {
getKindeServerSession,
} from "@kinde-oss/kinde-auth-nextjs/server";
export async function getServerSideProps({
req,
res,
}: {
req: Request;
res: Response;
}) {
const {
getUser,
getPermissions,
getOrganization,
} = getKindeServerSession(req, res);
const organization = await getOrganization();
const permissions = await getPermissions();
const user = await getUser();
return {
props: {
user,
permissions,
organization,
},
};
}
export default function Server({
user,
permissions,
organization,
}: any) {
console.log("user", user);
console.log("permissions", permissions);
console.log("organization", organization);
...
}

Reference:

{
getAccessToken: () => Promise<string>;
getBooleanFlag: (code: string, defaultValue: boolean) => Promise<boolean>;
getFlag: (code: string, defaultValue: any, flagType: any) =>
Promise<
| import("@kinde-oss/kinde-typescript-sdk").GetFlagType
| {
value: any;
}
>;
getIntegerFlag: (code: string, defaultValue: number) => Promise<number>;
getOrganization: () =>
Promise<{
orgCode: string;
}>;
getPermission: (name: any) =>
Promise<{
orgCode: string;
isGranted: boolean;
}>;
getPermissions: () =>
Promise<{
permissions: string[];
orgCode: string;
}>;
getStringFlag: (code: string, defaultValue: string) => Promise<string>;
getUser: () => Promise<any>;
getUserOrganizations: () =>
Promise<{
orgCodes: string[];
}>;
isAuthenticated: () => Promise<boolean>;
}

Components - useKindeAuth

Link to this section

You can get an authorized user’s Kinde Auth data from any component using the useKindeAuth helper.

Example:

import {useKindeAuth} from "@kinde-oss/kinde-auth-nextjs";
export default function ClientPage() {
const {
isLoading,
user,
permissions,
organization,
userOrganizations,
accessToken,
getBooleanFlag,
getClaim,
getFlag,
getIntegerFlag,
getPermission,
getStringFlag,
isAuthenticated,
error
} = useKindeAuth();
if (isLoading) return <div>Loading...</div>;
return (
<div className="pt-20">
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">User</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(user, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Permissions</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(permissions, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Organization</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(organization, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">User organizations</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(userOrganizations, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Access Token</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(accessToken, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Is Authenticated</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(isAuthenticated, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">error</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(error, null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Get boolean flag</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(getBooleanFlag("bodsa", true), null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Get claim</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(getClaim("bodsa"), null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Get integer flag</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(getIntegerFlag("bodsa", 1), null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Get string flag</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(getStringFlag("bodsa", "dsad"), null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Get permission</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(getPermission("bodsa"), null, 2)}
</pre>
</div>
<div className="mb-8">
<h4 className="text-2xl font-bold dark:text-white mb-2">Get flag</h4>
<pre className="p-4 rounded bg-slate-950 text-green-300">
{JSON.stringify(getFlag("bodsa", "dsad", "s"), null, 2)}
</pre>
</div>
</div>
);
}

Reference:

export type State = {
/**
* - Kinde access token
*/
accessToken: string | null;
error?: string | null;
isAuthenticated: boolean | null;
isLoading: boolean | null;
/**
* - The organization that the current user is logged in to
*/
organization: string | null;
/**
* - The current user's permissions
*/
permissions: string[] | null;
/**
* - Kinde user
*/
user: KindeUser | null;
/**
* - Organizations that the current user belongs to
*/
userOrganizations: string[] | null;
getBooleanFlag: getBooleanFlag;
getClaim: getClaim;
getFlag: getFlag;
getIntegerFlag: getIntegerFlag;
getPermission: getPermission;
getStringFlag: getStringFlag;
};

Protecting routes

Link to this section

It’s likely that your application will have both pages that are publicly available and private ones which should only be available to logged in users. There are multiple ways you can protect pages with Kinde Auth.

Protect routes using Kinde Auth data

Link to this section

On the page you want to protect, you can check if the user is authenticated and then handle it right then and there by grabbing the Kinde Auth data.

Get Kinde Auth data:

  • server side in getServerSideProps with the getKindeServerSession help
  • client side using the useKindeAuth helper
// pages/protected.tsx - using getKindeServerSession
import { LoginLink } from "@kinde-oss/kinde-auth-nextjs/dist/components";
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
export async function getServerSideProps({
req,
res,
}: {
req: Request;
res: Response;
}) {
const {
isAuthenticated
} = getKindeServerSession(req, res);
const isAuthed = await isAuthenticated();
return {
props: {
isAuthed
},
};
}
export default async function Protected({isAuthed}) {
return (isAuthed ? (
<div>
This page is protected - but you can view it because you are authenticated
</div>
) : (
<div>
This page is protected, please <LoginLink>Login</LoginLink> to view it
</div>
);
}
// pages/protected/page.tsx - using useKindeAuth
import { useKindeAuth } from "@kinde-oss/kinde-auth-nextjs";
import { LoginLink } from "@kinde-oss/kinde-auth-nextjs/dist/components";
export default function Admin() {
const { isAuthenticated, isLoading } = useKindeAuth();
if (isLoading) return <div>Loading...</div>;
return isAuthenticated ? (
<div>Admin content</div>
) : (
<div>
You have to <LoginLink>Login</LoginLink> to see this page
</div>
);
}

In the example above we show different content based on whether or not the user is authenticated. If you want to automatically send the user to the sign in screen, you can do something like the following:

pages/protected.tsx
import {useKindeAuth} from "@kinde-oss/kinde-auth-nextjs";
import {useRouter} from "next/router";
import {useEffect} from "react";
export default async function Protected() {
const router = useRouter();
const {isAuthenticated} = useKindeAuth();
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push("/api/auth/login");
}
}, [isLoading, isAuthenticated, router]);
return <div>Protected content</div>;
}

If you want the user to be redirected back to that route after login, you can set post_login_redirect_url in the search params of the redirect.

router.push("/api/auth/login?post_login_redirect_url=/protected");

Protect routes using middleware

Link to this section

You can also protect routes with Next.js middleware.

Default page protection

We provide a withAuth helper that will protect routes covered by the matcher. If the user is not authenticated then they are redirected to login and once they have logged in they will be redirected back to the protected page which they should now have access to.

import {withAuth} from "@kinde-oss/kinde-auth-nextjs/middleware";
export default function middleware(req) {
return withAuth(req, {
isReturnToCurrentPage: true
});
}
export const config = {
matcher: ["/admin"]
};

Page protection with callback function after authorization

You can use the withAuth helper as shown below with a middleware callback function which has access to the req.kindeAuth object that exposes the token and user data.

import {withAuth} from "@kinde-oss/kinde-auth-nextjs/middleware";
export default withAuth(async function middleware(req) {
console.log("look at me", req.kindeAuth);
});
export const config = {
matcher: ["/admin"]
};

Middleware options

There are options that can be passed into the middleware function to configure its functionality.

  • isReturnToCurrentPage - redirect the user back to the page they were trying to access
  • loginPage - define the path of the login page (where the users are redirected to when not authenticated)
  • publicPaths - define the public paths
  • isAuthorized - define the criteria for authorization
import {withAuth} from "@kinde-oss/kinde-auth-nextjs/middleware";
export default withAuth(
async function middleware(req) {
console.log("look at me", req.kindeAuth);
},
{
isReturnToCurrentPage: true,
loginPage: "/login",
isAuthorized: ({token}) => {
// The user will be considered authorized if they have the permission 'eat:chips'
return token.permissions.includes("eat:chips");
}
}
);
export const config = {
matcher: ["/admin"]
};

Kinde Management API

Link to this section

To use our management API please see @kinde/management-api-js

getServerSideProps example:

import { Users } from "@kinde/management-api-js";
export async function getServerSideProps({
req,
res,
}: {
req: Request;
res: Response;
}) {
const { users } = await Users.getUsers();
return {
props: {
users,
},
};
}
export default function Server({ users }: any) {
console.log(users)
...
}

API route example:

import {Users} from "@kinde/management-api-js";
import type {NextApiRequest, NextApiResponse} from "next";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const users = await await Users.getUsers();
if (users.code === "OK") {
return res.status(200).json({users});
} else {
return res.status(400);
}
}

Create organizations

Link to this section

To create an organization from your app, you can use the CreateOrgLink component.

import {CreateOrgLink} from "@kinde-oss/kinde-auth-nextjs/components";
<CreateOrgLink orgName="Hurlstone">Create org</CreateOrgLink>;

Signing into organizations

Link to this section

You can have your users sign in to a specific organization by setting the orgCode param in the LoginLink and RegisterLink components.

import {LoginLink, RegisterLink} from "@kinde-oss/kinde-auth-nextjs/components";
<LoginLink orgCode="org_7392cf35a1e">Login</LoginLink>
<RegisterLink orgCode="org_7392cf35a1e">Register</RegisterLink>

If the orgCode is not specified and the user belongs to multiple organizations, they will be prompted to choose which organization to log into during the login or register flow.

Internationalization

Link to this section

You can set the language you wish your users to see when they hit the login flow by including the lang attribute as a part of the authUrlParams when using the LoginLink and RegisterLink components.

import {LoginLink} from "@kinde-oss/kinde-auth-nextjs/components";
<LoginLink
authUrlParams={{
lang: "en-AU"
}}
>
Login
</LoginLink>;

An audience is the intended recipient of an access token - for example the API for your application. The audience argument can be passed to the Kinde client to request an audience be added to the provided token.

The audience of a token is the intended recipient of the token.

.env
...
KINDE_AUDIENCE=<your-api>

For details on how to connect, see Register an API.

Working with subdomains

Link to this section

In the case you have a custom domain and you would like to start the authentication flow from a URL like auth.mysite.com and you want to redirect to a URL like app.mysite.com , all you have to do is set the KINDE_COOKIE_DOMAIN to match the domain.

.env
...
KINDE_COOKIE_DOMAIN=.mysite.com

If the URL you want to start the authentication flow from and the URL you want to redirect to don’t share the same domain, then this will not work.

In debug mode you will see more logs in your console that may help with debugging.

.env
KINDE_DEBUG_MODE = true;