Home > Web Front-end > JS Tutorial > Real-Time Authorization in a Chat Application with Permit.io and WebSockets

Real-Time Authorization in a Chat Application with Permit.io and WebSockets

Patricia Arquette
Release: 2025-01-23 18:41:09
Original
652 people have browsed it

As modern chat applications evolve, real-time communication requires increasingly granular access controls. Managing dynamic permissions in real-time for various chat rooms and participants, especially in complex or multi-user environments, can quickly become challenging. What if you could easily implement fine-grained authorization for your chat app without compromising on performance?

Permit.io makes integrating robust, real-time access control into your chat application simple. By pairing Permit.io’s advanced authorization models with WebSockets, you can ensure that the right users have access at the right time, all while maintaining the responsiveness needed in chat applications.

In this tutorial, you’ll learn how to implement real-time authorization in a WebSocket-based chat application using Permit.io. By the end, you’ll understand how to enforce role-based and attribute-based access controls dynamically, securing different chat rooms, message visibility, and interactions in real time.

Introduction to Real-Time Authorization in Chat Applications

We all use chat applications in one form or another to stay connected with friends and family, discuss important matters with colleagues, and even conduct business. With the increasing demand for seamless, real-time communication, it’s easy to take for granted the sophisticated security measures that protect these interactions. However, as chat applications become more complex, so do the challenges of securing user data and conversations. Fine-grained access control helps ensure that only authorized users have access to sensitive information and actions.

Why real-time chat apps require fine-grained access control

Fine-grained access control is essential in real-time chat applications to ensure security, user customization, and regulatory compliance.
By setting robust authentication methods and role-based permissions, chat applications prevent unauthorized users from accessing sensitive conversations and allow admins to effectively manage user interactions. This approach also enhances user experience by enabling participation in various chat types—public, private, or group—based on individual roles or preferences, creating more engaging interactions.
Furthermore, fine-grained access control helps organizations meet strict data privacy regulations, like the GDPR, safeguarding confidential data and minimizing legal risks.

The challenges of implementing dynamic authorization in a chat context

The points cover all the main ideas from the paragraphs. Here’s a refined version that includes every detail:

  1. Real-time chat apps require instant permission checks and updates, making dynamic authorization challenging without risking performance impacts, especially when handling large volumes of messages and users.
  2. Chat applications often involve multiple access layers, with permissions that vary based on roles, group memberships, or specific attributes, requiring consistent and efficient enforcement.
  3. Dynamic changes in roles (e.g., admin promotions, group removals, or temporary access) must be recognized and applied immediately across all active sessions without disrupting ongoing conversations.
  4. Achieving this level of flexibility while maintaining a seamless user experience demands an advanced authorization model that integrates closely with real-time protocols like WebSockets.

Overview of how Permit.io’s authorization solutions can streamline this process with WebSockets

Permit.io’s authorization solutions can significantly streamline the implementation of real-time authorization in chat applications, particularly when integrated with WebSockets. Here’s an overview of how this combination enhances dynamic access control:

  1. Seamless Integration: Permit.io offers a robust framework for managing fine-grained access controls that can be easily integrated into chat applications utilizing WebSockets. This integration allows for real-time permission checks and updates, ensuring that users have immediate access to the appropriate chat rooms and functionalities based on their roles and attributes.
  2. Dynamic Permission Management: With Permit.io, developers can implement dynamic authorization models that adapt to changes in user roles or group memberships. For instance, if a user is promoted to an admin role or temporarily granted special access, these changes can be reflected instantly across all active sessions without interrupting ongoing conversations. This capability addresses one of the primary challenges in dynamic authorization by ensuring that permissions are consistently enforced in real-time.
  3. Enhanced Performance: By leveraging WebSockets for communication, Permit.io ensures that real-time authorization processes do not compromise application performance. The architecture supports high volumes of messages and users, allowing for efficient handling of concurrent access requests while maintaining responsiveness—a critical requirement for chat applications.
  4. Role-Based and Attribute-Based Access Control: Permit.io facilitates the enforcement of both role-based and attribute-based access controls within chat environments. This flexibility allows administrators to define specific permissions for different user types, such as moderators or regular users, enhancing security while providing a customizable user experience. Users can participate in various chat types—public, private, or group based on their assigned roles.
  5. Regulatory Compliance: Implementing Permit.io’s solutions helps organizations meet stringent data privacy regulations by ensuring only authorized users can access sensitive information and functionalities within the chat application. This compliance is crucial for safeguarding user data and minimizing legal risks associated with unauthorized access.

Setting Up a WebSocket-Based Chat Application

For our web socket-based application, we’ll be using Next.js and Ably, a service that allows us to easily integrate and manage real-time capabilities in our apps powered by web socket.

In addition to Ably and Next Auth, we can use something like Firebase to handle both authentication and realtime features. There’s an entire tutorial about that on the Permit.io blog.

Without further ado, let’s proceed!

Setting up Next.js

Run the following command and follow the prompts:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Navigate to the newly created project folder and install a few more packages we’ll use to build our app:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Also, install a few UI compoents fron Radix UI:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Create a new file in the root of the project directory - .prettierrc and enter the following:

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

In the tailwind.config.ts file, enter the following:

{
  "plugins": ["prettier-plugin-tailwindcss"]
}
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
// ./tailwind.config.ts
import type { Config } from "tailwindcss";
import tailwindForms from "@tailwindcss/forms";
export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [tailwindForms],
} satisfies Config;
Copy after login
Copy after login
Copy after login
Copy after login

Set up global styles

In the ./app/globals.css file, enter the following:

In the `./next.config.ts`, enter the following:

// ./next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  /* config options here */
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "lh3.googleusercontent.com",
      },
      {
        protocol: "https",
        hostname: "www.tapback.co",
      },
    ],
  },
};
export default nextConfig;
Copy after login
Copy after login
Copy after login
Copy after login

Setting up Authentication

We’ll be using Auth.js, an authentication library originally built for Next.js.
Run the following command to install the package:

/* ./app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
  body {
    @apply bg-white text-gray-800 dark:bg-gray-900 dark:text-gray-300;
  }
}
@layer components {
  .btn {
    @apply inline-flex items-center justify-center gap-2 rounded-full bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-500 hover:brightness-95 focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-300 dark:brightness-105 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:focus:ring-offset-gray-900;
  }
  .btn:has(> .icon:first-child) {
    @apply pl-2;
  }
  .btn:has(> .icon:last-child) {
    @apply pr-2;
  }
  .icon {
    @apply h-5 w-5 text-current;
  }
  .form-input {
    @apply flex grow rounded-full border border-none bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-500 outline-none hover:brightness-95 focus:border-none focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-300 dark:brightness-105 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:focus:ring-offset-gray-900;
  }
  .site-section {
    @apply py-16 md:py-24;
  }
  .site-section > .wrapper {
    @apply mx-auto max-w-5xl px-4 sm:px-6 lg:px-8;
  }
  .noscroll {
    @apply overflow-auto;
    scrollbar-width: none;
  }
}
Copy after login
Copy after login
Copy after login

We have to create an AUTH_SECRET environment variable. The library uses this random value to encrypt tokens and email verification hashes. (See Deployment to learn more). You can generate one via the official Auth.js CLI running:

npm install next-auth@beta
Copy after login
Copy after login
Copy after login

Next, create the Auth.js config file and object - ./auth.js:

npx auth secret

? Created /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat/.env.local with `AUTH_SECRET`.
Copy after login

Add a Route Handler under ./app/api/auth/[...nextauth]/route.ts:

// ./auth.ts
import NextAuth from "next-auth";
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [],
});
Copy after login

Add optional Middleware to keep the session alive; this will update the session expiry every time it is called - ./middleware.ts:

// ./app/api/auth/[...nextauth]/route.ts

import { handlers } from "@/auth"; // Referring to the auth.ts we just created
export const { GET, POST } = handlers;
Copy after login

Setting up Google OAuth

NextAuth supports multiple OAuth providers for authentication. For this tutorial, we’ll be using Google.

To obtain our Google client ID and secret, we have to set up a new project in Google Cloud Console - https://console.cloud.google.com/projectcreate

Google Cloud Console

Next, in the newly created project, we’ll set up a new consent screen:

Consent Screen

Once our consent screen has been created, we can set up credentials. Navigate to Credentials from the sidebar.
Click on the Create Credentials button and select OAuth client ID from the dropdown.

Oauth Client ID

In the following screen, select Web Application, enter the Authorized JavaScript origins, and redirect URIs: http://localhost:3000 and http://localhost:3000/api/auth/callback/google respectively.

Create Oauth Client ID

With that we should have our client ID and secret:

Client ID and Secret

Copy the values and enter them into the .env file:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Next, enable Google as a sign-in option in our Auth.js configuration. We’ll have to import the Google provider from the package and pass it to the providers array we set earlier in the Auth.js config file:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Create a Site Header component

Let’s create a header for our app containing the sign-in and sign-out buttons. Create a new file - ./components/Site/Header.tsx:

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Here, a SiteHeader component serves as the main navigation bar.

If a user session exists (session?. user), display the user’s avatar, name, and a “Sign out” button.
Otherwise, display a “Sign in” button, and since we’re using Next.js server actions, we put it inside a form.

The signIn and signOut functions are wrapped in server action markers ("use server") for server-side execution in Next.js.

In our ./app/layout.tsx file import the SiteHeader component:

{
  "plugins": ["prettier-plugin-tailwindcss"]
}
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

In the homepage at ./app/page.tsx, enter the following:

// ./tailwind.config.ts
import type { Config } from "tailwindcss";
import tailwindForms from "@tailwindcss/forms";
export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [tailwindForms],
} satisfies Config;
Copy after login
Copy after login
Copy after login
Copy after login

With that, we should have something like this:

Auth with Google

Now that we’ve set up authentication, let’s proceed to add websockets capabilities to our app with Ably.

Set up WebSockets with Ably

To get started, sign-up to Ably then Create a new app with the following options:

  • App name: Call your app something meaningful
  • Select your preferred language(s): JavaScript
  • What type of app are you building? Live Chat

Ably App Set up

In the next screen, copy your API Key:

Ably API Key

Save it in the .env file:

In the `./next.config.ts`, enter the following:

// ./next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  /* config options here */
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "lh3.googleusercontent.com",
      },
      {
        protocol: "https",
        hostname: "www.tapback.co",
      },
    ],
  },
};
export default nextConfig;
Copy after login
Copy after login
Copy after login
Copy after login

In our app, install the Ably React SDK and jose library for JWT:

/* ./app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
  body {
    @apply bg-white text-gray-800 dark:bg-gray-900 dark:text-gray-300;
  }
}
@layer components {
  .btn {
    @apply inline-flex items-center justify-center gap-2 rounded-full bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-500 hover:brightness-95 focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-300 dark:brightness-105 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:focus:ring-offset-gray-900;
  }
  .btn:has(> .icon:first-child) {
    @apply pl-2;
  }
  .btn:has(> .icon:last-child) {
    @apply pr-2;
  }
  .icon {
    @apply h-5 w-5 text-current;
  }
  .form-input {
    @apply flex grow rounded-full border border-none bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-500 outline-none hover:brightness-95 focus:border-none focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-300 dark:brightness-105 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:focus:ring-offset-gray-900;
  }
  .site-section {
    @apply py-16 md:py-24;
  }
  .site-section > .wrapper {
    @apply mx-auto max-w-5xl px-4 sm:px-6 lg:px-8;
  }
  .noscroll {
    @apply overflow-auto;
    scrollbar-width: none;
  }
}
Copy after login
Copy after login
Copy after login

Once jose and ably installs, create ./app/api/ably/route.ts:

npm install next-auth@beta
Copy after login
Copy after login
Copy after login

Let’s break down what’s happening here:

  1. JWT Token Creation: The createToken function generates a JSON Web Token compatible with Ably, including user capabilities and a client identifier. Here, we encode the claim in the token, meaning that the userClaim will be included in any events published by this client in topics with a name matching *.
  2. Dynamic Capabilities: The generateCapability function assigns permissions to users based on their role (moderator or regular user) for specific channels.
  3. User Authentication: The auth function is used to retrieve the user’s session, ensuring only authenticated users can request tokens.
  4. Environment Variables: The ABLY_SECRET_KEY environment variable securely stores the Ably API secret for token signing.
  5. API Response: The handler processes the request, generates the token, and returns it as a JSON response or an empty string if the user is unauthenticated.

Next, lt’s build out all the components we need for that chat page. We’ll start with the Message Item component that displays a single message sent by a user.

Message Item component

Create a new file - ./components/Message/Item.tsx:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

This MessageItem component dynamically adjusts its layout based on the fromUser prop, using flex-row-reverse for alignment.
It displays the sender’s avatar sourced from message.data.avatarUrl. The message text (message.data.text) and its timestamp (message.timestamp) are shown, formatted as a localized time string.
For messages sent by the current user, a delete button, rendered with an SVG icon, is conditionally displayed and triggers the onDelete callback with message.id.

Message List component

Create a new file - ./components/Message/List.tsx:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

The MessageList component renders a scrollable list of messages using a messages.map() loop, where each message is displayed through the MessageItem component.
It identifies if a message is sent by the logged-in user by comparing the session email (session.data.user.email) with the message’s clientId, styling user messages with self-end for right alignment.
Each MessageItem receives its message and the onDelete callback.

Message Input component

Create new file - ./components/Message/Input.tsx:

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

The MessageInput component manages user input with useState and submits messages via onSubmit, resetting the field after submission. The input field () shows contextual placeholders and is disabled when the disabled prop is true.

Chat Channel List component

Create a new file - ./components/Chat/ChannelList.tsx:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

The ChatChannelList component renders a list of channels as links. The active channel is highlighted using the font-bold class based on the current pathname.

Chat component

Create a new file - ./components/Chat/index.tsx:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

In this component, we’re doing a few things:

  1. Message Handling: The component listens for messages (ADD, DELETE, PROMOTE) via useChannel and updates the message list accordingly.
  2. Publishing Messages: Messages are published using the publishMessage function, sending the message text and user avatar.
  3. ScrollArea: Radix UI’s ScrollArea is used for smooth scrolling in both vertical and horizontal directions for the message history.
  4. Message History Fetching: On mount, we fetche the last 100 messages from the channel’s history.

Next, we’ll put everything together in the chat page.

Create Chat Page

Create a new file - ./app/chat/[[...channel]]/page.tsx:

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

The Page component wraps the chat app in providers for state and context management. The SessionProvider ensures global access to the user’s session, while the AblyProvider and ChannelProvider enable seamless integration with Ably by sharing a Realtime client (authUrl: "/api/ably") and the current channelName (e.g., chat:general).

The layout uses a grid with three sections: a left sidebar ( for channel navigation), a center area ( for messages), and a placeholder right sidebar (TODO: Users list) for future features like online users. The autoConnect option ensures Ably connects only in the browser, avoiding SSR issues.

With that, we should have something like this, when we navigate to http://localhost:3000/chat/general:

Chat Interface

Now that our cha

Set up Permit.io

Create a new account at https://www.permit.io/:

Create Permit Account

Create a new project

Enter the name of your project, I’ll be using Live Chat for this example:

Create Permit Project

Create new resource

To create channels for our chat app, Permit allows us to create resources which are entities that represent what users can have access to, let’s set up our channel as a resource to proceed:

Create new resource

Edit resuorce

Now we can edit the resource and add roles on our channel resource:

Edit Resource

View roles

Here are the roles we’ve created. You can view it by going to the Roles tab on the Policy page.

View Roles

Udpate policies

Now we can update our policies to determine who has access to what on each resource:

Update Policies

Create resource instance

Create resource instances for each channel we want in our chat, here we are creating an instance for the general channel, we can do same for random and mod.

Create Resource Instance

View Instances

Here we can see the created resource instances:

View Instance

Now that we’ve set up our Permit Dashboard, we can add Permit to our Next.js app.

Adding Permit.io to your Next.js application

Let’s dive in and start integrating Permit.io into our application.

First, we have to install the permitio package:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

We have to obtain our API key from our Permit.io dashboard:

Obtain API Key

Add the copied key to the .env file:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Set up local PDP

Next, we’ll have to set up our Policy Decision Point which is a network node responsible for answering authorization queries using policies and contextual data.

Pull the PDP container from Docker Hub (Click here to install Docker):

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Run the container & replace the PDP_API_KEY environment variable with your API key.

{
  "plugins": ["prettier-plugin-tailwindcss"]
}
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Now that we have our PDP set up, let’s dive in to adding autoriaztion to our app, you can learn more about adding Permit.io to a Next.js app from this step-by-step tutorial on the Permit.io blog.

For this example, we’ll need to set up a few reusable functions and routes, lets start with creating a Permit library function in ./lib/permit.ts:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

We’ll also create ./utils/permit.ts file for all our permit-related utility functions:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

User Hook

We’ll have to create a useUser hook which will allow us to easily retrieve the permit user on the frontend. Create a new file ./hooks/useUser.ts:

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Create Users in Permit During Authentication

To automatically add users to Permit during sign-in, use NextAuth’s signIn callback. During the callback:

  1. Fetch Resource Instances: Retrieve resource instances from Permit (e.g., a default workspace or project).
  2. Sync User Data: Use a utility function (like handleSyncUser) to create or update the user’s information in Permit. This includes their ID, email, name, role (e.g., “participant”), and the relevant resource instance.
  3. Complete Sign-In: The sign-in process will continue by returning true.

Replace the ./auth.ts file with this updated code:

{
  "plugins": ["prettier-plugin-tailwindcss"]
}
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

This ensures users are authenticated and seamlessly integrated into Permit for access control.

As we can see below, when the user signs in, their account is being added to our Permit dashboard:

Real-Time Authorization in a Chat Application with Permit.io and WebSockets

We’ll have to set up a few more things before we can proceed, let’s starts with types to appease TypeScript as we develop our app. Create a new ./types/user.ts file and enter the following:

// ./tailwind.config.ts
import type { Config } from "tailwindcss";
import tailwindForms from "@tailwindcss/forms";
export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [tailwindForms],
} satisfies Config;
Copy after login
Copy after login
Copy after login
Copy after login

Next, we’ll create a few API routes to obtain user data and permissions and promote and demote users.

Get Users Data

Create a new file - ./app/api/permit/getUsers/route.ts:

In the `./next.config.ts`, enter the following:

// ./next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  /* config options here */
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "lh3.googleusercontent.com",
      },
      {
        protocol: "https",
        hostname: "www.tapback.co",
      },
    ],
  },
};
export default nextConfig;
Copy after login
Copy after login
Copy after login
Copy after login

Get User Data

Create a new file *./app/api/permit/getUser/route.ts*:

/* ./app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
  body {
    @apply bg-white text-gray-800 dark:bg-gray-900 dark:text-gray-300;
  }
}
@layer components {
  .btn {
    @apply inline-flex items-center justify-center gap-2 rounded-full bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-500 hover:brightness-95 focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-300 dark:brightness-105 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:focus:ring-offset-gray-900;
  }
  .btn:has(> .icon:first-child) {
    @apply pl-2;
  }
  .btn:has(> .icon:last-child) {
    @apply pr-2;
  }
  .icon {
    @apply h-5 w-5 text-current;
  }
  .form-input {
    @apply flex grow rounded-full border border-none bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-500 outline-none hover:brightness-95 focus:border-none focus:outline-none focus:ring-2 focus:ring-gray-200 focus:ring-offset-2 dark:bg-gray-800 dark:text-gray-300 dark:brightness-105 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:focus:ring-offset-gray-900;
  }
  .site-section {
    @apply py-16 md:py-24;
  }
  .site-section > .wrapper {
    @apply mx-auto max-w-5xl px-4 sm:px-6 lg:px-8;
  }
  .noscroll {
    @apply overflow-auto;
    scrollbar-width: none;
  }
}
Copy after login
Copy after login
Copy after login

Promote User

Create a new file - ./app/api/permit/promoteUser/route.ts and enter the following:

npm install next-auth@beta
Copy after login
Copy after login
Copy after login

Here we have a Next.js API route that promotes a user by assigning them specific roles on different resource instances using Permit.io.
The promoteUser function checks if the current user has permission to promote others and assigns roles such as "participant," "moderator," and "admin" to the target user on specified channels.
The GET function handles incoming requests, extracts query parameters, validates them, fetches resource instances, and executes the promotion process. It returns the results as a JSON response or an error message if any issues occur.

Real-Time Authorization in a Chat Application with Permit.io and WebSocketste User

Create a new file - ./app/api/permit/demoteUser/route.ts and enter the following:

npx create-next-app@latest live-chat
Need to install the following packages:
create-next-app@15.1.0
Ok to proceed? (y) y

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
Creating a new Next.js app in /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc


added 371 packages, and audited 372 packages in 1m

141 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created live-chat at /Users/miracleio/Documents/writing/permit/real-time-authorization-in-a-chat-application-with-permitio-and-websockets/live-chat 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Similarly, we have a Next.js API route that demotes a user by unassigning their roles on specific resource instances using Permit.io. The demoteUser function removes roles such as "participant", "moderator", and "admin" from the user on specified channels. The GET function handles incoming requests, extracts query parameters, validates them, fetches resource instances, retrieves the user's assigned roles, and executes the demotion process.

Get Resources

Create a new file - ./app/api/permit/listResourceInstances/route.ts and enter the following:

cd live-chat
npm install -D prettier prettier-plugin-tailwindcss  @tailwindcss/forms 
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Update Ably permissions route

In our file ./app/api/ably/route.ts, replace it with the following updated code:

npm install @radix-ui/react-scroll-area
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Here we incorporate Permit.io for managing roles and permissions, replacing static configurations with dynamic role-based permissions.
The generatePermissions function uses data from Permit.io to map roles to specific channel capabilities, ensuring permissions are aligned with user roles in real-time. This approach improves flexibility and ensures the system adapts as roles or permissions change, integrating seamlessly with Ably’s JWT-based authentication.

Update Channel List

Now that we’ve added the resources (channels) to our permit dashboard, we can fetch them from there instead of hardcoding them.
In the ./components/Chat/ChannelList.tsx file, make the following changes:

{
  "plugins": ["prettier-plugin-tailwindcss"]
}
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Create User List component

Let’s create a user list component that fetches all the permit users and display promote or demote buttons next to the user name.
Create a new file - ./components/Chat/UserList.tsx and enter the following:

// ./tailwind.config.ts
import type { Config } from "tailwindcss";
import tailwindForms from "@tailwindcss/forms";
export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [tailwindForms],
} satisfies Config;
Copy after login
Copy after login
Copy after login
Copy after login

Here, we use a custom hook useUser to fetch the current user's information and the useChannel hook from Ably for real-time channel communication. The getUserList function fetches the list of users from the server using the Permit.io API. The component subscribes to the Ably channel for real-time updates and fetches the updated user list when a promote/demote event occurs. The user list is stored in the component's state, excluding the current user.

Finally, we can add it to our page, in ./app/chat/[[...channel]]/page.tsx :

In the `./next.config.ts`, enter the following:

// ./next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  /* config options here */
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "lh3.googleusercontent.com",
      },
      {
        protocol: "https",
        hostname: "www.tapback.co",
      },
    ],
  },
};
export default nextConfig;
Copy after login
Copy after login
Copy after login
Copy after login

With that we should be able to promote and demote users in real time:

Real Time Real-Time Authorization in a Chat Application with Permit.io and WebSockets

Here’s the demotion in action:

Real-Time Authorization in a Chat Application with Permit.io and WebSocketstion Action

Closing and Conclusion

Building a chat application with real-time authorization is a challenging but rewarding process. By integrating powerful tools like Permit.io and WebSockets, you can create a seamless experience that ensures secure and fine-grained access control. In this article, we explored the importance of dynamic authorization in chat applications, set up a WebSocket-based architecture with Ably, and integrated Permit.io for authorization management.

This workflow demonstrates how modern tools simplify what was once a complex implementation, enabling developers to focus more on user experience and scalability rather than the underlying infrastructure. With the right approach, you can ensure your chat application is both dynamic and secure.

Further Reading and Resources

  • GitHub Code - https://github.com/miracleonyenma/live-chat
  • Permit.io Documentation – A comprehensive guide to Permit.io’s capabilities and APIs.
  • Ably WebSockets Documentation – Learn more about building real-time apps with Ably.
  • Next.js Documentation – Explore advanced features for building React applications with Next.js.
  • Auth.js Documentation – Set up secure and scalable authentication in your Next.js apps.
  • WebSockets for Real-Time Web Applications – An in-depth overview of WebSockets and their use cases.

Next Steps

With the foundational setup complete, you can explore simple and advanced features like:

  • Allowing moderators to delete participants messages
  • Adding AI-powered moderation tools to detect and prevent abusive content in chat. You can learn more about Building AI Applications with Permit
  • Implementing analytics dashboards to track user activity and message trends.

The above is the detailed content of Real-Time Authorization in a Chat Application with Permit.io and WebSockets. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template