---
meta:
  title: "Set up ID token permissions with Next.js"
  parentTitle: "Authentication"
  description: "Learn how to setup ID token permissions with Next.js."
---

Follow the following steps to start configure your authentication endpoint and
start building your own security logic in Next.js’ `/app` directory.

## Quickstart

<Steps>
  <Step>
    <StepTitle>Install the `liveblocks/node` package</StepTitle>
    <StepContent>

      ```bash
      npm install @liveblocks/node
      ```

    </StepContent>

  </Step>
  <Step>
    <StepTitle>Add your project’s secret key</StepTitle>
    <StepContent>

      Create a new `.env.local` file and add your Liveblocks secret key from the [dashboard](/dashboard/apikeys).

      ```env file=".env.local"
      LIVEBLOCKS_SECRET_KEY="{{SECRET_KEY}}"
      ```

    </StepContent>

  </Step>
  <Step>
    <StepTitle>Set up authentication endpoint</StepTitle>
    <StepContent>
      Users can only interact with rooms they have access to. You can
      configure permission access in an `api/liveblocks-auth` endpoint by
      creating the `app/api/liveblocks-auth/route.ts` file with the
      following code. This is where you will implement your security and
      define if the current user has access to a specific room.

      ```ts file="app/api/liveblocks-auth/route.ts"
      import { Liveblocks } from "@liveblocks/node";

      const liveblocks = new Liveblocks({
        secret: process.env.LIVEBLOCKS_SECRET_KEY!,
      });

      export async function POST(request: Request) {
        // Get the current user from your database
        const user = __getUserFromDB__(request);

        // Identify the user and return the result
        const { status, body } = await liveblocks.identifyUser(
          {
            userId: user.id,
            groupIds, // Optional
          },
          { userInfo: user.metadata },
        );

        return new Response(body, { status });
      }
      ```

      Here’s an example using the older API routes format in `/pages`.

      ```ts file="pages/api/liveblocks-auth.ts" isCollapsed isCollapsable
      import { Liveblocks } from "@liveblocks/node";
      import type { NextApiRequest, NextApiResponse } from "next";

      const liveblocks = new Liveblocks({
        secret: process.env.LIVEBLOCKS_SECRET_KEY!,
      });

      export default async function handler(request: NextApiRequest, response: NextApiResponse) {
        // Get the current user from your database
        const user = __getUserFromDB__(request);

        // Identify the user and return the result
        const { status, body } = await liveblocks.identifyUser(
          {
            userId: user.id,
            groupIds, // Optional
          },
          { userInfo: user.metadata },
        );

        // Authorize the user and return the result
        const { status, body } = await session.authorize();
        response.status(status).send(body);
      }
      ```

    </StepContent>

  </Step>
  <Step>
    <StepTitle>Set up the client</StepTitle>
    <StepContent>
      On the front end, you can now replace the `publicApiKey`
      prop on [`LiveblocksProvider`](/docs/api-reference/liveblocks-react#LiveblocksProvider)
      with `authEndpoint` pointing to the endpoint you just created.

      ```tsx
      <LiveblocksProvider authEndpoint="/api/liveblocks-auth">
      ```

      If you need to pass custom headers or data to your endpoint, you can
      use
      [authEndpoint as a callback](/docs/api-reference/liveblocks-react#LiveblocksProviderCallback)
      instead.

      ```tsx title="Pass custom headers" isCollapsed isCollapsable
      <LiveblocksProvider
        authEndpoint={async (room) => {
          // Passing custom headers and body to your endpoint
          const headers = {
            // Custom headers
            // ...

            "Content-Type": "application/json",
          };

          const body = JSON.stringify({
            // Custom body
            // ...

            room,
          });

          const response = await fetch("/api/liveblocks-auth", {
            method: "POST",
            headers,
            body,
          });

          return await response.json();
        }}
      />
      ```

    </StepContent>

  </Step>
  <Step>
    <StepTitle>Set permission accesses to a room</StepTitle>
    <StepContent>
      A room can have `defaultAccesses`, `usersAccesses`, and `groupsAccesses` defined.
      Permissions are then checked when users try to connect to a room. For security purposes,
      [room permissions](/docs/api-reference/authentication#id-token-room-permissions) can only be set on the back-end through `@liveblocks/node` or our REST API.
      For instance, you can use [`liveblocks.createRoom`](/docs/api-reference/liveblocks-node#post-rooms)
      to create a new room with read-only public access levels while giving write access to specific groups and users.

      ```ts highlight="7-15"
      import { Liveblocks } from "@liveblocks/node";

      const liveblocks = new Liveblocks({
        secret: process.env.LIVEBLOCKS_SECRET_KEY!,
      });

      const room = await liveblocks.createRoom("my-room-id", {
        defaultAccesses: ["*:read"],
        groupsAccesses: {
          "my-group-id": ["*:write"],
        },
        usersAccesses: {
          "my-user-id": ["*:write"],
        },
      });
      ```

      For more information, make sure to read the section on [room permissions](/docs/api-reference/authentication#id-token-room-permissions).

    </StepContent>

  </Step>
  <Step lastStep>
    <StepTitle>Attach metadata to users</StepTitle>
      <StepContent>
        Optionally, you can attach static metadata to each user, which will
        be accessible in your app. First you need to define the types in
        your config file, under `UserMeta["info"]`.

        ```ts file="liveblocks.config.ts" highlight="7-11"
        declare global
          interface Liveblocks {
            UserMeta: {
              id: string;

              // Example, use any JSON-compatible data in your metadata
              info: {
                name: string;
                avatar: string;
                colors: string[];
              }
            }

            // Other type definitions
            // ...
          }
        }
        ```

        When authenticating, you can then pass the user’s metadata to
        `prepareSession` in the endpoint we’ve just created.

        ```ts file="app/api/liveblocks-auth/route.ts" highlight="11-15"
        // Get the current user from your database
        const user = __getUserFromDB__(request);

        // Identify the user and return the result
        const { status, body } = await liveblocks.identifyUser(
          {
            userId: user.id,
            groupIds, // Optional
          },
          {
            userInfo: {
              name: user.name,
              avatar: user.avatarUrl,
              colors: user.colorArray,
            }
          },
        );
        ```

        User metadata has now been set! You can access this information in your app through
        [`useSelf`](/docs/api-reference/liveblocks-react#useSelf).

        ```tsx highlight="4"
        export { useSelf } from "@liveblocks/react/suspense";

        function Component() {
          const { name, avatar, colors } = useSelf((me) => me.info);
        }
        ```

        Bear in mind that if you’re using the [default Comments components](/docs/api-reference/liveblocks-react-ui#Components),
        you must specify a `name` and `avatar` in `userInfo`.
      </StepContent>

    </Step>

</Steps>

## More information

Both `userId` and `userInfo` can then be used in your React application as such:

```ts
const self = useSelf();
console.log(self.id);
console.log(self.info);
```

<Figure>
  <Image
    src="/assets/id-token-auth-diagram.png"
    alt="Auth diagram"
    width={768}
    height={576}
  />
</Figure>

---

For an overview of all available documentation, see [/llms.txt](/llms.txt).
