Next.js App Router SDK v1
This SDK is for Next.js version 13 and later and uses Server Side Components and Pages Router.
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.
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 Next.js starter kit. You can try out a live demo at
npm i @kinde-oss/kinde-auth-nextjs
yarn add @kinde-oss/kinde-auth-nextjs
pnpm add @kinde-oss/kinde-auth-nextjs
http://localhost:3000/api/auth/kinde_callback
http://localhost:3000
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.comKINDE_SITE_URL=http://localhost:3000KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000KINDE_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.
"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>
getKindeServerSession
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>;}
useKindeAuth
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:
getServerSideProps
with the getKindeServerSession
helpuseKindeAuth
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:
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 authorizationimport {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"]};
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); }}
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 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.
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.
...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.
...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.
KINDE_DEBUG_MODE = true;