Accessing JWT and Session Data in NextAuth.js with Callbacks (Okta Example)
Tags: nextjs, authentica, okta, tutorial
Published: 2023-01-03 19:23:57

Do you have NextAuth.js working in your Next.js app but need to extend the functionality with callbacks? You are in the right place. In this article, I’ll show you how NextAuth.js provides callbacks to give you greater control of your authentication. You will see examples with JSON Web Tokens (JWT) and Session callbacks, and these translate well to the other callbacks available in NextAuth.js. I’ll be showing off with Okta as the auth provider, but if you are using a different provider you’ll benefit from this as well. 

If you are still setting up NextAuth.js, you can follow my previous post, [Adding Authentication to Nextjs with NextAuth.js and Okta](https://thetombomb.com/posts/nextjs-nextauth-okta). It uses Okta as well for the examples but is generic enough to help you set up for any provider. 

## Callbacks in NextAuth.js

[Callbacks are asynchronous functions](https://next-auth.js.org/configuration/callbacks) that NexAuth.js exposes so that you can execute your own logic when an action is performed. Each callback has specific criteria for when it will run covered below.

There are 4 callbacks that you have access to and we will be focusing on the top 2.

- **JWT callback**
- **Session callback**
- Sign in callback
- Redirect callback

You access these callbacks and write your logic in the `pages/api/auth/[...nextauth].ts` file. Below is a code example with empty JWT and session callbacks.

```jsx
// pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth';

export const authOptions = {
    // Configure one or more authentication providers
    providers: [
    ],
    session: {
    },
    secret: process.env.SECRET as string,
    callbacks: {
        async jwt({ token, account }: any) {

        },
        async session({ session, token }: any) {

        }
    }
};

export default NextAuth(authOptions)
```

## The JSON Web Token (JWT) Callback

This callback is called whenever a JWT is created or updated. The return value will be **encrypted** and stored in the client machine's cookie information. You are likely using the JWT to authenticate with another service so let’s take a look at how you can access and decrypt your JWT.

```jsx
// pages/api/auth/[...nextauth].ts
...
callbacks: {
    async jwt({ token, account }: any) {
        if (account) {
            token.accessToken = account.access_token;
            token.idToken = account.id_token;
            token.oktaId = account.providerAccountId;
        }

				// Decrypting JWT to check if expired
        var tokenParsed = JSON.parse(Buffer.from(token.idToken.split('.')[1], 'base64').toString());
        const dateNowInSeconds = new Date().getTime() / 1000
        if (dateNowInSeconds > tokenParsed.exp) {
             throw Error("expired token");
        }
			
				return token;
    }
...
```

First, you validate if the `account` exists. The account refers to the authentication provider, which in my case is Okta. It then takes the required account information and stores that in the token. You can debug or `console.log` to see what all information is available for your provider in the `account` variable.  

Second, the token is decrypted so you can check the expiration. If the token is expired an error is thrown. You may want a more delicate solution but this gives you the groundwork for decrypting and checking values in the JWT callback.

> 💡 The above code has Okta-specific provider variables. The information accessed from the `account` variable is going to be information about the user's Okta account. If you are using different providers it’s important to verify the naming conventions used by your provider. For example, `id_token` may be `idToken` instead. 

*[NextAuth.js JWT Callback Documentation](https://next-auth.js.org/configuration/callbacks#jwt-callback)*

## Session Callback Example

The session callback is called whenever a session is checked. So any calls to `getSession()`, `useSession()`, & `/api/auth/session`. 

Inside the session callback, you can extend what data is returned to you whenever the session is checked. In my use case, I needed the user's `oktaId` passed through so I did that in the session callback.

The example below shows taking information from the token and passing that along to the session. 

```tsx
// pages/api/auth/[...nextauth].ts
...
callbacks: {
        async session({ session, token }: any) {
            session.accessToken = token.accessToken;
            session.idToken = token.idToken;
            session.oktaId = token.oktaId;
            return session;
        }
    }
...
```

*[NextAuth.js Session Callback Documentation](https://next-auth.js.org/configuration/callbacks#session-callback)*

## Closing

When adding authentication to your Next.js app with NextAuth.js it’s likely you will need to use the callback feature if you want to go beyond the most basic implementation. This was definitely the most confusing and difficult part for me but I hope the code samples are useful in your auth implementation!

To learn more check the [Callback Docs](https://next-auth.js.org/configuration/callbacks) page provided by NextAuth.js or reach out to me and I’d love to learn more about your project and help out. If you need help getting your initial setup of NextAuth.js working read my previous blog: [Adding Authentication to Nextjs with NextAuth.js and Okta](https://thetombomb.com/posts/nextjs-nextauth-okta).