From f777631ab875fcf0b2072bf697499503aed38454 Mon Sep 17 00:00:00 2001 From: Lakhan Date: Fri, 25 Oct 2024 17:06:44 +0530 Subject: [PATCH 1/4] chore: updated live server auth cookies handling --- live/src/core/extensions/index.ts | 6 ++++-- live/src/core/hocuspocus-server.ts | 18 ++++++++++++++---- .../src/core/hooks/use-collaborative-editor.ts | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/live/src/core/extensions/index.ts b/live/src/core/extensions/index.ts index b3ac6843fa5..2fada768479 100644 --- a/live/src/core/extensions/index.ts +++ b/live/src/core/extensions/index.ts @@ -34,12 +34,13 @@ export const getExtensions: () => Promise = async () => { }), new Database({ fetch: async ({ + context, documentName: pageId, requestHeaders, requestParameters, }) => { // request headers - const cookie = requestHeaders.cookie?.toString(); + const cookie = context.cookie ?? requestHeaders.cookie?.toString(); // query params const params = requestParameters; const documentType = params.get("documentType")?.toString() as @@ -71,13 +72,14 @@ export const getExtensions: () => Promise = async () => { }); }, store: async ({ + context, state, documentName: pageId, requestHeaders, requestParameters, }) => { // request headers - const cookie = requestHeaders.cookie?.toString(); + const cookie = context.cookie ?? requestHeaders.cookie?.toString(); // query params const params = requestParameters; const documentType = params.get("documentType")?.toString() as diff --git a/live/src/core/hocuspocus-server.ts b/live/src/core/hocuspocus-server.ts index 0aa411b9334..d9ee1b9c9d3 100644 --- a/live/src/core/hocuspocus-server.ts +++ b/live/src/core/hocuspocus-server.ts @@ -12,20 +12,30 @@ export const getHocusPocusServer = async () => { name: serverName, onAuthenticate: async ({ requestHeaders, + context, // user id used as token for authentication token, }) => { - // request headers - const cookie = requestHeaders.cookie?.toString(); + let cookie, userId; + try { + const parsedToken = JSON.parse(token); + cookie = parsedToken?.cookie ?? requestHeaders.cookie?.toString(); + userId = parsedToken.id; + } catch { + throw Error("Credentials not provided"); + } - if (!cookie) { + if (!cookie || !userId) { throw Error("Credentials not provided"); } + // set cookie in context, so it can be used in the server handler. + context.cookie = cookie; + try { await handleAuthentication({ cookie, - token, + token: userId, }); } catch (error) { throw Error("Authentication unsuccessful!"); diff --git a/packages/editor/src/core/hooks/use-collaborative-editor.ts b/packages/editor/src/core/hooks/use-collaborative-editor.ts index 5a004bff284..ea3c4877ef8 100644 --- a/packages/editor/src/core/hooks/use-collaborative-editor.ts +++ b/packages/editor/src/core/hooks/use-collaborative-editor.ts @@ -39,7 +39,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => { name: id, parameters: realtimeConfig.queryParams, // using user id as a token to verify the user on the server - token: user.id, + token: JSON.stringify(user), url: realtimeConfig.url, onAuthenticationFailed: () => { serverHandler?.onServerError?.(); From f5b0fa03fe2600a6b820d8859cd31d2e7611a2c7 Mon Sep 17 00:00:00 2001 From: Lakhan Date: Fri, 25 Oct 2024 17:32:43 +0530 Subject: [PATCH 2/4] chore: update token parsing logic --- live/src/core/hocuspocus-server.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/live/src/core/hocuspocus-server.ts b/live/src/core/hocuspocus-server.ts index d9ee1b9c9d3..c22335aba2a 100644 --- a/live/src/core/hocuspocus-server.ts +++ b/live/src/core/hocuspocus-server.ts @@ -16,14 +16,8 @@ export const getHocusPocusServer = async () => { // user id used as token for authentication token, }) => { - let cookie, userId; - try { - const parsedToken = JSON.parse(token); - cookie = parsedToken?.cookie ?? requestHeaders.cookie?.toString(); - userId = parsedToken.id; - } catch { - throw Error("Credentials not provided"); - } + const { cookie = requestHeaders.cookie?.toString(), id: userId } = + JSON.parse(token); if (!cookie || !userId) { throw Error("Credentials not provided"); From dcafd06ba63fbb3d92623a3592777013016afe89 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Wed, 30 Oct 2024 15:43:23 +0530 Subject: [PATCH 3/4] fix: types and better logical seperation between the existing two tokens --- live/src/core/extensions/index.ts | 40 +++++++++--------------- live/src/core/hocuspocus-server.ts | 29 +++++++++++++---- live/src/core/lib/authentication.ts | 6 ++-- live/src/core/types/common.d.ts | 4 +++ packages/editor/src/core/types/editor.ts | 1 + 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/live/src/core/extensions/index.ts b/live/src/core/extensions/index.ts index 2fada768479..4867cad3d7b 100644 --- a/live/src/core/extensions/index.ts +++ b/live/src/core/extensions/index.ts @@ -1,28 +1,26 @@ // Third-party libraries import { Redis } from "ioredis"; - // Hocuspocus extensions and core import { Database } from "@hocuspocus/extension-database"; import { Extension } from "@hocuspocus/server"; import { Logger } from "@hocuspocus/extension-logger"; import { Redis as HocusPocusRedis } from "@hocuspocus/extension-redis"; - -// Core helpers and utilities +// core helpers and utilities import { manualLogger } from "@/core/helpers/logger.js"; import { getRedisUrl } from "@/core/lib/utils/redis-url.js"; - -// Core libraries +// core libraries import { fetchPageDescriptionBinary, updatePageDescription, } from "@/core/lib/page.js"; - -// Core types -import { TDocumentTypes } from "@/core/types/common.js"; - -// Plane live libraries +// plane live libraries import { fetchDocument } from "@/plane-live/lib/fetch-document.js"; import { updateDocument } from "@/plane-live/lib/update-document.js"; +// types +import { + type HocusPocusServerContext, + type TDocumentTypes, +} from "@/core/types/common.js"; export const getExtensions: () => Promise = async () => { const extensions: Extension[] = [ @@ -33,14 +31,8 @@ export const getExtensions: () => Promise = async () => { }, }), new Database({ - fetch: async ({ - context, - documentName: pageId, - requestHeaders, - requestParameters, - }) => { - // request headers - const cookie = context.cookie ?? requestHeaders.cookie?.toString(); + fetch: async ({ context, documentName: pageId, requestParameters }) => { + const cookie = (context as HocusPocusServerContext).cookie; // query params const params = requestParameters; const documentType = params.get("documentType")?.toString() as @@ -55,7 +47,7 @@ export const getExtensions: () => Promise = async () => { fetchedData = await fetchPageDescriptionBinary( params, pageId, - cookie + cookie, ); } else { fetchedData = await fetchDocument({ @@ -75,11 +67,9 @@ export const getExtensions: () => Promise = async () => { context, state, documentName: pageId, - requestHeaders, requestParameters, }) => { - // request headers - const cookie = context.cookie ?? requestHeaders.cookie?.toString(); + const cookie = (context as HocusPocusServerContext).cookie; // query params const params = requestParameters; const documentType = params.get("documentType")?.toString() as @@ -126,7 +116,7 @@ export const getExtensions: () => Promise = async () => { } manualLogger.warn( `Redis Client wasn't able to connect, continuing without Redis (you won't be able to sync data between multiple plane live servers)`, - error + error, ); reject(error); }); @@ -140,12 +130,12 @@ export const getExtensions: () => Promise = async () => { } catch (error) { manualLogger.warn( `Redis Client wasn't able to connect, continuing without Redis (you won't be able to sync data between multiple plane live servers)`, - error + error, ); } } else { manualLogger.warn( - "Redis URL is not set, continuing without Redis (you won't be able to sync data between multiple plane live servers)" + "Redis URL is not set, continuing without Redis (you won't be able to sync data between multiple plane live servers)", ); } diff --git a/live/src/core/hocuspocus-server.ts b/live/src/core/hocuspocus-server.ts index c22335aba2a..f13e837eafb 100644 --- a/live/src/core/hocuspocus-server.ts +++ b/live/src/core/hocuspocus-server.ts @@ -4,6 +4,9 @@ import { v4 as uuidv4 } from "uuid"; import { handleAuthentication } from "@/core/lib/authentication.js"; // extensions import { getExtensions } from "@/core/extensions/index.js"; +import { TUserDetails } from "@plane/editor"; +// types +import { type HocusPocusServerContext } from "@/core/types/common.js"; export const getHocusPocusServer = async () => { const extensions = await getExtensions(); @@ -16,20 +19,34 @@ export const getHocusPocusServer = async () => { // user id used as token for authentication token, }) => { - const { cookie = requestHeaders.cookie?.toString(), id: userId } = - JSON.parse(token); + let cookie: string | undefined; + let userId: string | undefined; + + // Extract cookie (fallback to request headers) and userId from token (for scenarios where + // the cookies are not passed in the request headers) + try { + const parsedToken = JSON.parse(token) as TUserDetails; + userId = parsedToken.id; + cookie = parsedToken.cookie; + if (!cookie) { + cookie = requestHeaders.cookie?.toString(); + } + } catch (error) { + // If token parsing fails, fallback to request headers + console.error("Token parsing failed, using request headers:", error); + } if (!cookie || !userId) { - throw Error("Credentials not provided"); + throw new Error("Credentials not provided"); } - // set cookie in context, so it can be used in the server handler. - context.cookie = cookie; + // set cookie in context, so it can be used throughout the ws connection + (context as HocusPocusServerContext).cookie = cookie; try { await handleAuthentication({ cookie, - token: userId, + userId, }); } catch (error) { throw Error("Authentication unsuccessful!"); diff --git a/live/src/core/lib/authentication.ts b/live/src/core/lib/authentication.ts index ee01b020908..0f679337c77 100644 --- a/live/src/core/lib/authentication.ts +++ b/live/src/core/lib/authentication.ts @@ -7,11 +7,11 @@ const userService = new UserService(); type Props = { cookie: string; - token: string; + userId: string; }; export const handleAuthentication = async (props: Props) => { - const { cookie, token } = props; + const { cookie, userId } = props; // fetch current user info let response; try { @@ -20,7 +20,7 @@ export const handleAuthentication = async (props: Props) => { manualLogger.error("Failed to fetch current user:", error); throw error; } - if (response.id !== token) { + if (response.id !== userId) { throw Error("Authentication failed: Token doesn't match the current user."); } diff --git a/live/src/core/types/common.d.ts b/live/src/core/types/common.d.ts index 3b9dff8e140..3156060efb3 100644 --- a/live/src/core/types/common.d.ts +++ b/live/src/core/types/common.d.ts @@ -2,3 +2,7 @@ import { TAdditionalDocumentTypes } from "@/plane-live/types/common.js"; export type TDocumentTypes = "project_page" | TAdditionalDocumentTypes; + +export type HocusPocusServerContext = { + cookie: string; +}; diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index 1aa85822451..bdaf70d68cf 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -138,6 +138,7 @@ export type TUserDetails = { color: string; id: string; name: string; + cookie?: string; }; export type TRealtimeConfig = { From 29cea3983695665ebefb662d2a99934dc2d716c2 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Wed, 30 Oct 2024 15:53:01 +0530 Subject: [PATCH 4/4] fix: better fallback to use request headers for cookies --- live/src/core/hocuspocus-server.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/live/src/core/hocuspocus-server.ts b/live/src/core/hocuspocus-server.ts index f13e837eafb..b34a8fbb221 100644 --- a/live/src/core/hocuspocus-server.ts +++ b/live/src/core/hocuspocus-server.ts @@ -4,6 +4,7 @@ import { v4 as uuidv4 } from "uuid"; import { handleAuthentication } from "@/core/lib/authentication.js"; // extensions import { getExtensions } from "@/core/extensions/index.js"; +// editor types import { TUserDetails } from "@plane/editor"; // types import { type HocusPocusServerContext } from "@/core/types/common.js"; @@ -19,8 +20,8 @@ export const getHocusPocusServer = async () => { // user id used as token for authentication token, }) => { - let cookie: string | undefined; - let userId: string | undefined; + let cookie: string | undefined = undefined; + let userId: string | undefined = undefined; // Extract cookie (fallback to request headers) and userId from token (for scenarios where // the cookies are not passed in the request headers) @@ -28,12 +29,14 @@ export const getHocusPocusServer = async () => { const parsedToken = JSON.parse(token) as TUserDetails; userId = parsedToken.id; cookie = parsedToken.cookie; - if (!cookie) { - cookie = requestHeaders.cookie?.toString(); - } } catch (error) { // If token parsing fails, fallback to request headers console.error("Token parsing failed, using request headers:", error); + } finally { + // If cookie is still not found, fallback to request headers + if (!cookie) { + cookie = requestHeaders.cookie?.toString(); + } } if (!cookie || !userId) {