This SDK is for NextJS version 13 and later and uses Server Side Components and Pages Router.
We highly recommend using our dedicated NextJS SDK with App Router instead of this one.
Whilst technically this SDK is compatible with NextJS 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.
- If you have not already installed the NextJS SDK, we recommend you use NextJS App Router v2.
- If you are already using the NextJS Pages Router, refer to the earlier version NextJS Pages Router v1.
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
.
The easiest way to get started is to use the NextJS starter kit. You can try out a live demo at
# npm
npm i @kinde-oss/kinde-auth-nextjs
# yarn
yarn add @kinde-oss/kinde-auth-nextjs
# pnpm
pnpm i @kinde-oss/kinde-auth-nextjs
- In Kinde, go to Settings > Applications > [Your app] > View details.
- 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
- Allowed callback URLs (also known as redirect URIs) - for example
- Select Save.
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 KindeKINDE_CLIENT_SECRET
- Your business’s secret key (do not share)KINDE_ISSUER_URL
- your kinde domainKINDE_SITE_URL
- where your app is runningKINDE_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.
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
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.
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();
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>
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>
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>;
}
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;
};
💡 Use
isLoading
to ensure the data is up to date.
You can return a Loading spinner or something similar while it isLoading
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.
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 thegetKindeServerSession
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");
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 accessloginPage
- define the path of the login page (where the users are redirected to when not authenticated)publicPaths
- define the public pathsisAuthorized
- 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"]
};
You have to navigate to Settings → APIs → Kinde Management API → Applications and enable your Next.js app.
getServerSideProps
example:
import {
createKindeManagementAPIClient,
} from "@kinde-oss/kinde-auth-nextjs/server";
export async function getServerSideProps({
req,
res,
}: {
req: Request;
res: Response;
}) {
const client = await createKindeManagementAPIClient(req, res);
const users = await client.usersApi.getUsers();
return {
props: {
// we do this hack to resolve any undefined values
users: JSON.parse(JSON.stringify(users)),
},
};
}
export default function Server({ users }: any) {
console.log(users)
...
}
API route example:
import {createKindeManagementAPIClient} from "@kinde-oss/kinde-auth-nextjs/server";
import type {NextApiRequest, NextApiResponse} from "next";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const client = await createKindeManagementAPIClient(req, res);
const users = await client.usersApi.getUsers();
if (users.code === "OK") {
return res.status(200).json({users});
} else {
return res.status(400);
}
}
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>;
You can have your users sign into 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.
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.
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.
Developer tools