Pipedrive Authentication Provider in NextAuth.js + Next.js

Estevan Jantsk
Pipedrive R&D Blog
Published in
7 min readMar 1, 2022

Recently I contributed to the NextAuth.js project and added Pipedrive as one of its OAuth providers. But what is NextAuth.js and why is it important?

Next.Js is a framework built on top of React. It simplifies full-stack development while empowering users to easily build frontend and backend apps. NextAuth.js is a complete open-source authentication solution for Next.js projects. This means you can add any sort of authentication mechanism, including OAuth, in a jiffy!

The idea of this post is to show you how easy it is to integrate both libraries and add Pipedrive as an OAuth provider, so you can consume Pipedrive’s API with less effort. By doing that, you could focus more on the business logic and worry less about technical parts.

Pre-requisites

If you get lost at some point, the source code is always available at https://github.com/estevanjantsk/pipedrive-oauth

Let’s start from the boilerplate

Next.Js offers great scaffolding via the npx commands. We can get started without worrying about the toolchain using the following command.

 npx create-next-app@latest --typescript pipedrive-oauth

Let’s also add NextAuth.js to our project now. Make sure you cd into app directory

cd pipedrive-oauth
npm i next-auth

If everything went as expected, you should see the setup like in the GIF above. You can run the following command to see the boilerplate app in action.

npm run dev

Adding Pipedrive as an OAuth Provider

Now that we have a boilerplate app that we can preview easily, we can add an OAuth provider to our app. Authentication providers in NextAuth.js are services that can be used to sign in a user. In our case, we can use the Pipedrive OAuth provider in 3 easy steps:

import NextAuth from "next-auth"
import PipedriveProvider from "next-auth/providers/pipedrive"
export default NextAuth({
providers: [
PipedriveProvider({
clientId: process.env.PIPEDRIVE_CLIENT_ID || '',
clientSecret: process.env.PIPEDRIVE_CLIENT_SECRET || '',
})
]
})
  • Add PIPEDRIVE_CLIENT_ID and PIPEDRIVE_CLIENT_SECRET as environment variables to your .env.local file (More info). The .env.local is located at the root of your project and provides a secure way to pass the secrets.
🚨 Don’t forget to re-run your project again after you add your keys.

Configuring a shared session state & creating a simple sign-in page

The useSession() React Hook in the NextAuth.js client allows you to check if someone is signed in. However, to be able to use it, we need to expose the session context. We can create our shared session state by having the following code in pages/_app.tsx

import { SessionProvider } from "next-auth/react";
import "../styles/globals.css";
import type { AppProps } from "next/app";
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
);
}
export default MyApp;

Since we have the SessionProvider, we can implement the sign-in page. Make sure you have the following code in the index.tsx file:

import type { NextPage } from "next";
import { useSession, signIn, signOut } from "next-auth/react";
const Home: NextPage = () => {
const { data: session } = useSession();
if (session) {
return (
<>
Signed in as {session.user?.email} <br />
<button onClick={() => signOut()}>Sign out</button>
</>
);
}
return (
<>
Not signed in <br />
<button onClick={() => signIn()}>Sign in</button>
</>
);
};
export default Home;

Test driving the sign-in page

NextAuth.js will automate plenty of actions for us, but one important thing that we should do is adding the Callback URL to our Pipedrive app via the Marketplace Manager. The URL that NextAuth generates for us follows the following structure: /api/auth/callback/:provider

In our case it will be something like this:

http://localhost:3000/api/auth/callback/pipedrive

Make sure you add the callback URL (under the OAuth & Access scopes section)

When you run the app & access http://localhost:3000, you should see something like this:

NextAuth.js handles the session for us and adds the cookies automatically.

Protecting Backend API routes

A simple sign-in page is a great starting point. But what if we want to do more? The beauty of NextAuth.js is that it facilitates working with REST APIs as it handles the authentication and authorization parts. We can store the access_token safely as part of the session information and use it across different routes.

Our boilerplate app already has an API route that is accessible at http://localhost:3000/api/hello/ with a corresponding TS file at pages/api/hello.ts.

By default, it would just return a static response. Also is unprotected.

But you can easily protect this backend API route with the following check:

const session = await getSession({ req })

If the user is signed in, a valid session object is returned. The following code snippet can be used to protect the backend route. Replace the code in hello.ts file with the following:

import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "next-auth/react";
type Data = {
content?: string,
error?: string,
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
const session = await getSession({ req });
if (session) {
res.send({
content:
"✅ This is protected content. You can access this content because you are signed in.",
});
} else {
res.send({
error: "✋ You must sign in to view the protected content on this page.",
});
}
}

Let’s see that in action at http://localhost:3000/api/hello

Securely accessing the access token in our backend API routes

NextAuth.js allows you to specify what happens during different stages of the authentication flow. This is possible via built-in callbacks. In our case, the jwt and session callbacks can be used to persist access_token and user-specific information.

All you need to do is add the following callbacks to the […nextauth].ts file. It should look like this:

...
export default NextAuth({
providers: [
PipedriveProvider({
clientId: process.env.PIPEDRIVE_CLIENT_ID || '',
clientSecret: process.env.PIPEDRIVE_CLIENT_SECRET || '',
})
],
callbacks: {
async jwt({
token,
account
}) {
// Persist the OAuth access_token to the token right after signin
if (account) {
token.accessToken = account.access_token
token.apiDomain = account.api_domain
}
return token
},
async session({
session,
token,
user
}) {
// Send properties to the client, like an access_token from a provider.
session.accessToken = token.accessToken
session.apiDomain = token.apiDomain
return session
}
}

})

Note that as part of successful authentication, you also receive the api_domain value that describes the base path of the API. Let’s pass it on to the session object. If you are curious to know what token(blue) and account (yellow) parameters hold, refer to the image below. The Pipedrive Next.js provider automatically populates the basic information after a successful authentication is triggered.

To fetch more data, we can make use of the access_token in our previously created backend API route. In the hello.ts file, make sure you retrieve the access token from the session and perform a Pipedrive REST API call with it. The following code snippet lets you do the same:

...
if (session) {
const { accessToken, apiDomain } = session;
const data = await fetch(`${apiDomain}/api/v1/users/me`, {
method: "GET",
headers: {
Authorization: "Bearer " + accessToken,
},
}).then((res) => res.json());
res.send(data);
} else {
...

If everything is successful, you would be able to authenticate with the simple sign-in page and also be able to invoke Pipedrive REST APIs like in the below GIF.

That’s pretty much it, now you have your Next.js + NextAuth.js + Pipedrive webapp up and running successfully. 🚀

You can also build your web app with the same stack and publish it to our Pipedrive Marketplace ! ❤️ If you run into any challenges, make sure you reach out to the Pipedrive Developer Community.

If you are a bit adventurous, go ahead and contribute to https://github.com/nextauthjs/next-auth. Either way, let me know your thoughts in the comments section or reach me via Linkedin or Twitter.

Interested in working in Pipedrive?

We’re currently hiring for several different positions in several different countries/cities.

Take a look and see if something suits you

Positions include:

  • Software Engineer in DevOps Tooling
  • Junior Developer
  • Backend, Full-Stack, iOS Engineers
  • Infrastructure Engineer
  • And several more

--

--

Estevan Jantsk
Pipedrive R&D Blog

Hi there! I am frontend dev looking to solve real world problems. I have a passion for learning and sharing my knowledge with others as publicly as possible.