From a837412639b07a43a23c93d235c85183b219900a Mon Sep 17 00:00:00 2001 From: Sandros94 Date: Thu, 23 Jan 2025 03:18:45 +0100 Subject: [PATCH 1/5] docs: add auth and context --- docs/1.guide/2.hooks.md | 61 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/docs/1.guide/2.hooks.md b/docs/1.guide/2.hooks.md index 697ec2e..875c685 100644 --- a/docs/1.guide/2.hooks.md +++ b/docs/1.guide/2.hooks.md @@ -15,8 +15,8 @@ Crossws provides a cross-platform API to define WebSocket servers. An implementa import { defineHooks } from "crossws"; const hooks = defineHooks({ - upgrade(req) { - console.log(`[ws] upgrading ${req.url}...`) + upgrade(request) { + console.log(`[ws] upgrading ${request.url}...`) return { headers: {} } @@ -42,3 +42,60 @@ const hooks = defineHooks({ }, }); ``` + +## Authentication + +During the `upgrade` hook it is possible to authenticate the user before upgrading the connection. If the user is not authenticated, you can throw an error to prevent the connection from being upgraded. + +```ts +import { defineHooks } from "crossws"; + +const authToken = "Bearer myToken123"; + +const hooks = defineHooks({ + upgrade(request) { + const userAuth = request.headers.get("Authorization"); + + if (!userAuth || userAuth !== authToken) { + throw new Response("Unauthorized", { + status: 401, + }); + } + + return { + headers: {} // Optionaly return custom headers + } + }, +}); +``` + +## Context + +You can store data in the `context` property of the `request` object during the `upgrade` hook or the `peer` object during the other hooks. This data will be shared in all hooks for the lifetime of the connection. + +> [!NOTE] +> context can be volatile in some environments like `cloudflare-durable` + +```ts +import { defineHooks } from "crossws"; + +const hooks = defineHooks({ + upgrade(request) { + request.context = { + data: 'myData', + }; + }, + + open(peer) { + console.log(peer.context.data); // myData + }, + + message(peer, message) { + console.log(peer.context.data); // myData + }, + + close(peer, details) { + console.log(peer.context.data); // myData + }, +}); +``` From 9237f626d3d7c937646615df25e5caa544f9fb58 Mon Sep 17 00:00:00 2001 From: Sandros94 Date: Sat, 25 Jan 2025 19:11:56 +0100 Subject: [PATCH 2/5] docs: improve auth example --- docs/1.guide/2.hooks.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/1.guide/2.hooks.md b/docs/1.guide/2.hooks.md index 875c685..303c327 100644 --- a/docs/1.guide/2.hooks.md +++ b/docs/1.guide/2.hooks.md @@ -54,18 +54,33 @@ const authToken = "Bearer myToken123"; const hooks = defineHooks({ upgrade(request) { - const userAuth = request.headers.get("Authorization"); + const authHeader = request.headers.get("Authorization"); - if (!userAuth || userAuth !== authToken) { - throw new Response("Unauthorized", { + if (!authHeader || !authHeader.startsWith("Basic ")) { + return new Response("Unauthorized", { status: 401, + headers: { + "WWW-Authenticate": 'Basic realm="Websocket Authentication", charset="UTF-8"' + } }); } - return { - headers: {} // Optionaly return custom headers + const base64Credentials = authHeader.split(" ")[1]; + const [username, password] = atob(base64Credentials).split(":"); + + if (username !== "myUsername" || password !== "myPassword") { + return new Response("Unauthorized", { + status: 401, + headers: { + "WWW-Authenticate": 'Basic realm="Websocket Authentication", charset="UTF-8"' + } + }); } - }, + + return { + headers: {} // Optionally return custom headers + }; + }, }); ``` From 23a12708663c41cc0793b7f01481d03c20a3b8db Mon Sep 17 00:00:00 2001 From: Sandros94 Date: Wed, 29 Jan 2025 17:20:29 +0100 Subject: [PATCH 3/5] docs: fix context example Co-authored-by: Travis Reynolds --- docs/1.guide/2.hooks.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/1.guide/2.hooks.md b/docs/1.guide/2.hooks.md index 303c327..b33e9b0 100644 --- a/docs/1.guide/2.hooks.md +++ b/docs/1.guide/2.hooks.md @@ -96,9 +96,7 @@ import { defineHooks } from "crossws"; const hooks = defineHooks({ upgrade(request) { - request.context = { - data: 'myData', - }; + request.context.data = "myData"; }, open(peer) { From ea1e65ec6e779be372cec825127bef188a14cfd6 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 10 Feb 2025 20:03:14 +0100 Subject: [PATCH 4/5] lint --- docs/1.guide/2.hooks.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/1.guide/2.hooks.md b/docs/1.guide/2.hooks.md index b33e9b0..116a3b9 100644 --- a/docs/1.guide/2.hooks.md +++ b/docs/1.guide/2.hooks.md @@ -16,10 +16,10 @@ import { defineHooks } from "crossws"; const hooks = defineHooks({ upgrade(request) { - console.log(`[ws] upgrading ${request.url}...`) + console.log(`[ws] upgrading ${request.url}...`); return { - headers: {} - } + headers: {}, + }; }, open(peer) { @@ -60,8 +60,9 @@ const hooks = defineHooks({ return new Response("Unauthorized", { status: 401, headers: { - "WWW-Authenticate": 'Basic realm="Websocket Authentication", charset="UTF-8"' - } + "WWW-Authenticate": + 'Basic realm="Websocket Authentication", charset="UTF-8"', + }, }); } @@ -72,15 +73,16 @@ const hooks = defineHooks({ return new Response("Unauthorized", { status: 401, headers: { - "WWW-Authenticate": 'Basic realm="Websocket Authentication", charset="UTF-8"' - } + "WWW-Authenticate": + 'Basic realm="Websocket Authentication", charset="UTF-8"', + }, }); } return { - headers: {} // Optionally return custom headers + headers: {}, // Optionally return custom headers }; - }, + }, }); ``` From 21185e5b98323ee13d2f413f612de79c2f5d0638 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 10 Feb 2025 20:05:23 +0100 Subject: [PATCH 5/5] reorder so we can reference to context --- docs/1.guide/2.hooks.md | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/1.guide/2.hooks.md b/docs/1.guide/2.hooks.md index 116a3b9..006843c 100644 --- a/docs/1.guide/2.hooks.md +++ b/docs/1.guide/2.hooks.md @@ -43,9 +43,38 @@ const hooks = defineHooks({ }); ``` +## Context + +You can store data in the `context` property of the `request` object during the `upgrade` hook or the `peer` object during the other hooks. This data will be shared in all hooks for the lifetime of the connection. + +> [!NOTE] +> context can be volatile in some environments like `cloudflare-durable` + +```ts +import { defineHooks } from "crossws"; + +const hooks = defineHooks({ + upgrade(request) { + request.context.data = "myData"; + }, + + open(peer) { + console.log(peer.context.data); // myData + }, + + message(peer, message) { + console.log(peer.context.data); // myData + }, + + close(peer, details) { + console.log(peer.context.data); // myData + }, +}); +``` + ## Authentication -During the `upgrade` hook it is possible to authenticate the user before upgrading the connection. If the user is not authenticated, you can throw an error to prevent the connection from being upgraded. +During the `upgrade` hook it is possible to authenticate the user before upgrading the connection. If the user is not authenticated, you can throw a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) **as error** to prevent the connection from being upgraded. ```ts import { defineHooks } from "crossws"; @@ -85,32 +114,3 @@ const hooks = defineHooks({ }, }); ``` - -## Context - -You can store data in the `context` property of the `request` object during the `upgrade` hook or the `peer` object during the other hooks. This data will be shared in all hooks for the lifetime of the connection. - -> [!NOTE] -> context can be volatile in some environments like `cloudflare-durable` - -```ts -import { defineHooks } from "crossws"; - -const hooks = defineHooks({ - upgrade(request) { - request.context.data = "myData"; - }, - - open(peer) { - console.log(peer.context.data); // myData - }, - - message(peer, message) { - console.log(peer.context.data); // myData - }, - - close(peer, details) { - console.log(peer.context.data); // myData - }, -}); -```