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.
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.
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 points cover all the main ideas from the paragraphs. Here’s a refined version that includes every detail:
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:
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!
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
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
Also, install a few UI compoents fron Radix UI:
cd live-chat npm install -D prettier prettier-plugin-tailwindcss @tailwindcss/forms
Create a new file in the root of the project directory - .prettierrc and enter the following:
npm install @radix-ui/react-scroll-area
In the tailwind.config.ts file, enter the following:
{ "plugins": ["prettier-plugin-tailwindcss"] }
// ./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;
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;
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; } }
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
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`.
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: [], });
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;
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
Next, in the newly created project, we’ll set up a new 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.
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.
With that we should have our 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
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
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
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"] }
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;
With that, we should have something like this:
Now that we’ve set up authentication, let’s proceed to add websockets capabilities to our app with Ably.
To get started, sign-up to Ably then Create a new app with the following options:
In the next screen, copy your 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;
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; } }
Once jose and ably installs, create ./app/api/ably/route.ts:
npm install next-auth@beta
Let’s break down what’s happening here:
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.
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
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.
Create a new file - ./components/Message/List.tsx:
cd live-chat npm install -D prettier prettier-plugin-tailwindcss @tailwindcss/forms
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.
Create new file - ./components/Message/Input.tsx:
npm install @radix-ui/react-scroll-area
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.
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
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.
Create a new file - ./components/Chat/index.tsx:
cd live-chat npm install -D prettier prettier-plugin-tailwindcss @tailwindcss/forms
In this component, we’re doing a few things:
Next, we’ll put everything together in the chat page.
Create a new file - ./app/chat/[[...channel]]/page.tsx:
npm install @radix-ui/react-scroll-area
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 (
With that, we should have something like this, when we navigate to http://localhost:3000/chat/general:
Now that our cha
Create a new account at https://www.permit.io/:
Enter the name of your project, I’ll be using Live Chat for this example:
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:
Now we can edit the resource and add roles on our channel resource:
Here are the roles we’ve created. You can view it by going to the Roles tab on the Policy page.
Now we can update our policies to determine who has access to what on each resource:
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.
Here we can see the created resource instances:
Now that we’ve set up our Permit Dashboard, we can add Permit to our Next.js app.
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
We have to obtain our API key from our Permit.io dashboard:
Add the copied key to the .env file:
cd live-chat npm install -D prettier prettier-plugin-tailwindcss @tailwindcss/forms
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
Run the container & replace the PDP_API_KEY environment variable with your API key.
{ "plugins": ["prettier-plugin-tailwindcss"] }
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
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
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
To automatically add users to Permit during sign-in, use NextAuth’s signIn callback. During the callback:
Replace the ./auth.ts file with this updated code:
{ "plugins": ["prettier-plugin-tailwindcss"] }
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:
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;
Next, we’ll create a few API routes to obtain user data and permissions and promote and demote users.
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;
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; } }
Create a new file - ./app/api/permit/promoteUser/route.ts and enter the following:
npm install next-auth@beta
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.
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
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.
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
In our file ./app/api/ably/route.ts, replace it with the following updated code:
npm install @radix-ui/react-scroll-area
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.
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"] }
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;
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;
With that we should be able to promote and demote users in real time:
Here’s the demotion in action:
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.
With the foundational setup complete, you can explore simple and advanced features like:
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!