NextJS Pages Router SDK v1

Link to this section

This SDK is for developers already using the NextJS Pages Router SDK. This document is out of date for new users, use this document instead: NextJS Pages Router v2.

NextJS 13 and App Router support

Link to this section

We highly recommend using our dedicated NextJS 13 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.

Both yarn and npm will work fine, we’ll be using npm for the purposes of this guide.

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:
  3. Select Save.

If you would like to use our Environments feature as part of your development process. You will need to create them first within your Kinde account. In this case you would use the Environment subdomain in the code block above.

Configuring your app

Link to this section

Environment variables

Link to this section

Put these variables in your .env file. You can find these variables on your Kinde Settings -> App keys page.

  • KINDE_SITE_URL - where your app is running
  • KINDE_ISSUER_URL - your kinde domain
  • 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.
  • KINDE_CLIENT_ID - you can find this on the Application details page
  • KINDE_CLIENT_SECRET - you can find this on the Application details page
KINDE_SITE_URL=http://localhost:3000
KINDE_ISSUER_URL=https://your_kinde_domain.kinde.com
KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
KINDE_POST_LOGIN_REDIRECT_URL=http://localhost:3000/dashboard
KINDE_CLIENT_ID=your_kinde_client_id
KINDE_CLIENT_SECRET=your_kinde_client_secret

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

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

This will handle Kinde Auth endpoints in your NextJS app.

  • /api/auth/me - this endpoint will get user information
  • /api/auth/login - will redirect you to login at the KindeAuth server
  • /api/auth/logout - will log you out of the app
  • /api/auth/register - will redirect you to register at the KindeAuth server.

Our SDK relies on this file existing in this location specified above. This includes NextJS 13 projects.

Integrate with your app

Link to this section

Kinde Provider

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.

NextJS 13: we suggest you include this in app/layout.tsx

// NextJS 13

"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>
    );
}

In the example above, there is a custom Auth component which handles routing depending on if the user is authenticated. Here is an example Auth component from our Starter Kit.

NextJS 12 and below: we suggest you include this in the root file of your application in _app.js

// NextJS 12 and below
import {KindeProvider} from "@kinde-oss/kinde-auth-nextjs";
function MyApp({Component, pageProps}) {
    return (
        <KindeProvider>
            <Component {...pageProps} />
        </KindeProvider>
    );
}
export default MyApp;

Sign up and sign in

Link to this section

The SDK ships with predefined API routes to generate the auth urls for sign up and sign in.

NextJS 13

Link prefetching causes issues with preflight options, so we need to use standard <a> tags for our links. So the build doesn’t break you’ll want to disable the linting that comes with Next as per the sample below:

// NextJS 13
"use client";

export default function MainNav() {
    return (
        <ul>
            <li>
                {/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
                <a href="/api/auth/login">Sign in</a>
            </li>
            <li>
                {/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
                <a href="/api/auth/register">Sign up</a>
            </li>
        </ul>
    );
}

NextJS 12 and below

You can use the <Link> component that ships with earlier versions of Next.

// NextJS 12 and below
import Link from "next/link";

export default function MainNav() {
    return (
        <ul>
            <li>
                <Link href="/api/auth/login">
                    <a>Sign in</a>
                </Link>
            </li>
            <li>
                <Link href="/api/auth/register">
                    <a>Sign up</a>
                </Link>
            </li>
        </ul>
    );
}

This is implemented in much the same way as signing up or signing in. An API route is provided for you

// NextJS 13
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a href="/api/auth/logout">
   Sign out
</a>

// NextJS 12 or below
<Link href="/api/auth/logout">
    <a>Sign out</a>
</Link>

Register your first user by signing up yourself. You’ll see your newly registered user on the Users page in Kinde.

View user profile

Link to this section

You can get an authorized user’s profile from any component using the Kinde NextJS hoo

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

const SayHello = () => {
    const {user} = useKindeAuth();
    return <p>Hi {user.given_name}!</p>;
};

To be on the safe side we have also provided isAuthenticated and isLoading state to prevent rendering errors.

"use client";
import { useKindeAuth } from "@kinde-oss/kinde-auth-nextjs";

const UserProfile = () => {
	const { user, isAuthenticated, isLoading } = useKindeAuth();

	if (isLoading) {
		return <p>Loading</p>;
	}

	return (
		{
			isAuthenticated ?
				<div>
					<h2>{user.given_name}</h2>
					<p>{user.email}</p>
				</div> :
				<p>Please sign in or register!</p>
		}
	);
};

The getToken method lets you to securely call your API and pass the bearer token to validate that your user is authenticated.

const {getToken} = useKindeAuth();

const fetchData = async () => {
    try {
        const accessToken = await getToken();
        const res = await fetch(`<your-api>`, {
            headers: {
                Authorization: `Bearer ${accessToken}`
            }
        });
        const {data} = await res.json();
        console.log({data});
    } catch (err) {
        console.log(err);
    }
};

We recommend using JWT verification middleware on your back end to verify the token and protect your endpoints.

An audience is the intended recipient of an access token - for example the API for your application. The audience argument can be set against KINDE_AUDIENCE in your environment variables.

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

// .env file

KINDE_AUDIENCE = your_audience;

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

User Permissions

Link to this section

Once a user has been verified as login in, your product/application will be returned the JWT token with an array of permissions for that user. You will need to configure your product/application to read permissions and unlock the respective functions.

You set Permissions in your Kinde account (see help article), the below is an example set of permissions.

"permissions":[
    "create:todos",
    "update:todos",
    "read:todos",
    "delete:todos",
    "create:tasks",
    "update:tasks",
    "read:tasks",
    "delete:tasks"
]

We provide helper functions to more easily access permissions:

const {getPermission, getPermissions} = useKindeAuth();

getPermission("create:todos");
// {orgCode: "org_1234", isGranted: true}

getPermissions();
// {orgCode: "org_1234", permissions: ["create:todos", "update:todos", "read:todos"]}

A practical example in code might look something like:

{
    getPermission("create:todos").isGranted ? <button>Create todo</button> : null;
}

When a user signs in the Access token your product/application receives contains a custom claim called feature_flags which is an object detailing the feature flags for that user.

You can set feature flags in your Kinde account. Here’s an example.

feature_flags: {
  theme: {
      "t": "s",
      "v": "pink"
 },
 is_dark_mode: {
      "t": "b",
      "v": true
  },
 competitions_limit: {
      "t": "i",
      "v": 5
  }
}

In order to minimize the payload in the token we have used single letter keys / values where possible. The single letters represent the following:

t = type

v = value

s = string

b = boolean

i = integer

We provide helper functions to more easily access feature flags:

/**
  * Get a flag from the feature_flags claim of the access_token.
  * @param {string} code - The name of the flag.
  * @param {obj} [defaultValue] - A fallback value if the flag isn't found.
  * @param {'s'|'b'|'i'|undefined} [flagType] - The data type of the flag (integer / boolean / string).
  * @return {object} Flag details.
*/
const { getFlag } = useKindeAuth();

/* Example usage */
getFlag('theme');
/*{
//   "code": "theme",
//   "type": "string",
//   "value": "pink",
//   "is_default": false // whether the fallback value had to be used
*/}

getFlag('create_competition', {defaultValue: false});
/*{
      "code": "create_competition",
      "value": false,
      "is_default": true // because fallback value had to be used
}*/

A practical example in code might look something like:

const {getFlag} = useKindeAuth();

{
    getFlag("create_competition").value ? <button>Create competition</button> : null;
}

We also require wrapper functions by type which should leverage getFlag above.

Booleans:

/**
 * Get a boolean flag from the feature_flags claim of the access_token.
 * @param {string} code - The name of the flag.
 * @param {bool} [defaultValue] - A fallback value if the flag isn't found.
 * @return {bool}
 */
const {getBooleanFlag} = useKindeAuth();

/* Example usage */
getBooleanFlag("is_dark_mode");
// true

getBooleanFlag("is_dark_mode", false);
// true

getBooleanFlag("new_feature", false);
// false (flag does not exist so falls back to default)

Strings and integers work in the same way as booleans above:

/**
 * Get a string flag from the feature_flags claim of the access_token.
 * @param {string} code - The name of the flag.
 * @param {string} [defaultValue] - A fallback value if the flag isn't found.
 * @return {string}
 */
const {getStringFlag} = useKindeAuth();

/**
 * Get an integer flag from the feature_flags claim of the access_token.
 * @param {string} code - The name of the flag.
 * @param {int} [defaultValue] - A fallback value if the flag isn't found.
 * @return {int}
 */
const {getIntegerFlag} = useKindeAuth();

A practical example in code might look something like:

const {getBooleanFlag, getStringFlag} = useKindeAuth();

{
    getBooleanFlag("create_competition") ? (
        <button className={`theme-${getStringFlag("theme")}`}>Create competition</button>
    ) : null;
}

Creating an organization

Link to this section

To have a new organization created within your application, you will need to run a similar function to below:

NextJS 13

// NextJS 13
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a href={`/api/auth/create_org?org_name=${<org_name>}`}>
	Create org
</a>

NextJS 12 and below

// NextJS 12 and below
<Link
    href={{
        pathname: "/api/auth/create_org",
        query: {
            org_name: "Organization name"
        }
    }}
>
    Create org
</Link>

Register and log in users to organizations

Link to this section

Every organization in Kinde has a unique code. To sign up a new user into a particular organization you will need to pass through this code in the register method.  (See where to find it).

NextJS 13

// NextJS 13
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a href={`/api/auth/register?org_code=${<org_code>}`}>
	Create org
</a>

NextJS 12 and below

// NextJS 12 and below
<Link
    href={{
        pathname: "/api/auth/register",
        query: {
            org_code: "org_af9078366f4"
        }
    }}
>
    Register org
</Link>

This code should also be passed along with the login method if you wish for a user to be logged into a specific organization.

NextJS 13

// NextJS 13
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a href={`/api/auth/login?org_code=${<org_code>}`}>
	Sign in
</a>

NextJS 12 and below

// NextJS 12 and below
<Link
    href={{
        pathname: "/api/auth/login",
        query: {
            org_code: "org_af9078366f4"
        }
    }}
>
    Sign into Org
</Link>

For general information about how organizations work in Kinde, see Kinde organizations for developers.

Customising the API path

Link to this section

If your Next.js application uses a custom base path for your API. The default path is /api/auth but to override this setting you can change this in your .env file as follows:

KINDE_AUTH_API_PATH="/my/custom/path"

Kinde Management API

Link to this section

The sections below relate to how to call the Kinde Management API using the Next.js SDK.

Getting access

Link to this section
  1. Enable the application access to the Kinde Management API. You can do this in Kinde by going to Settings > APIs > Kinde Management API and then toggling on your Next.js application under the Applications tab.

  2. Find the audience defined by the Kinde Management API under the Application details tab, then set the KINDE_AUDIENCE in your .env file to match the audience. It should look something like:

    // .env
    ...
    KINDE_AUDIENCE=https://your_kinde_domain.kinde.com/api
    ...
  3. Create a Kinde Management API Client in your app.

    We provide a helper function createKindeManagementAPIClient to do that. This can be done in Next.js API Routes and in Next.js getServerSideProps where you just have to pass through the Request and Response objects.

  4. Choose the API you want to use (e.g. Users, Permissions, Roles). You can see what is available in the Kinde Management API docs.

  5. Call one of the helper functions from the API.

// Next.js API Route - e.g. /api/kindeUser.ts

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 {
  ...
}
// Next.js getServerSideProps - e.g. pages/users/index.tsx

...
export async function getServerSideProps({ req, res }) {
  const client = await createKindeManagementAPIClient(req, res);
  const users = await client.usersApi.getUsers();
  return {
   props: {
     users
   }
  }
};
...

Troubleshooting

Link to this section

undefined cannot be serialized as JSON

This happens when the API returns an object with a property that is undefined and we try to return that object from getServerSideProps.

You can use this workaround to avoid this error:

...
const users = await client.usersApi.getUsers();

return {
	props: {
    users: JSON.parse(JSON.stringify(users))
  }
}
...

Persisting app state

Link to this section

If you want your project to remember which url your user was intending to visit before they were asked to authenticate, you can pass an additional parameter in the /login and /register links.

After the user has completed authentication at your defined callback url they will be redirected to the path you define here. This value does not need to be added to your allowed callback urls in Kinde.

NextJS 13

// NextJS 13
{
    /* eslint-disable-next-line @next/next/no-html-link-for-pages */
}
<a href="api/auth/login?post_login_redirect_url=/dashboard">Sign in</a>;

NextJS 12 and below

// NextJS 12 and below
<Link
    href={{
        pathname: "/api/auth/login",
        query: {
            post_login_redirect_url: "/dashboard"
        }
    }}
>
    Sign in
</Link>

Note: the value of post_login_redirect_url should either be a url on the same origin or a relative path.

If you need any assistance with getting Kinde connected reach out to us at support@kinde.com.


Talk to us

If you can’t find what you’re looking for in our help center — email our team

Contact support