WebSocket Session Authentication (#21837)

* session auth experiments

* simplified cookie parsing

* forgot the package json

* Create brave-walls-scream.md

* prettier
This commit is contained in:
Brainslug
2024-03-19 16:37:23 +01:00
committed by GitHub
parent b83e7bb8a5
commit 90476ccf17
4 changed files with 38 additions and 4 deletions

View File

@@ -0,0 +1,5 @@
---
"@directus/api": patch
---
Added WebSocket Session Authentication

View File

@@ -92,6 +92,7 @@
"@rollup/plugin-alias": "5.1.0",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-virtual": "3.0.2",
"@types/cookie": "0.6.0",
"argon2": "0.40.1",
"async": "3.2.5",
"axios": "1.6.7",
@@ -102,6 +103,7 @@
"chokidar": "3.6.0",
"commander": "12.0.0",
"content-disposition": "0.5.4",
"cookie": "0.6.0",
"cookie-parser": "1.4.6",
"cors": "2.8.5",
"cron-parser": "4.9.0",

View File

@@ -4,7 +4,6 @@ import type { Accountability } from '@directus/types';
import { parseJSON, toBoolean } from '@directus/utils';
import type { IncomingMessage, Server as httpServer } from 'http';
import { randomUUID } from 'node:crypto';
import type { ParsedUrlQuery } from 'querystring';
import type { RateLimiterAbstract } from 'rate-limiter-flexible';
import type internal from 'stream';
import { parse } from 'url';
@@ -22,6 +21,7 @@ import { getExpiresAtForToken } from '../utils/get-expires-at-for-token.js';
import { getMessageType } from '../utils/message.js';
import { waitForAnyMessage, waitForMessageType } from '../utils/wait-for-message.js';
import { registerWebSocketEvents } from './hooks.js';
import cookie from 'cookie';
const TOKEN_CHECK_INTERVAL = 15 * 60 * 1000; // 15 minutes
@@ -132,10 +132,20 @@ export default abstract class SocketController {
return;
}
const env = useEnv();
const cookies = request.headers.cookie ? cookie.parse(request.headers.cookie) : {};
const context: UpgradeContext = { request, socket, head };
const sessionCookieName = env['SESSION_COOKIE_NAME'] as string;
if (cookies[sessionCookieName]) {
const token = cookies[sessionCookieName] as string;
await this.handleTokenUpgrade(context, token);
return;
}
if (this.authentication.mode === 'strict') {
await this.handleStrictUpgrade(context, query);
const token = query['access_token'] as string;
await this.handleTokenUpgrade(context, token);
return;
}
@@ -151,11 +161,10 @@ export default abstract class SocketController {
});
}
protected async handleStrictUpgrade({ request, socket, head }: UpgradeContext, query: ParsedUrlQuery) {
protected async handleTokenUpgrade({ request, socket, head }: UpgradeContext, token: string) {
let accountability: Accountability | null, expires_at: number | null;
try {
const token = query['access_token'] as string;
accountability = await getAccountabilityForToken(token);
expires_at = getExpiresAtForToken(token);
} catch {

18
pnpm-lock.yaml generated
View File

@@ -119,6 +119,9 @@ importers:
'@rollup/plugin-virtual':
specifier: 3.0.2
version: 3.0.2(rollup@4.12.0)
'@types/cookie':
specifier: 0.6.0
version: 0.6.0
argon2:
specifier: 0.40.1
version: 0.40.1
@@ -149,6 +152,9 @@ importers:
content-disposition:
specifier: 0.5.4
version: 0.5.4
cookie:
specifier: 0.6.0
version: 0.6.0
cookie-parser:
specifier: 1.4.6
version: 1.4.6
@@ -7962,6 +7968,10 @@ packages:
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
dev: true
/@types/cookie@0.6.0:
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
dev: false
/@types/cookiejar@2.1.5:
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
dev: true
@@ -10622,6 +10632,11 @@ packages:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
/cookie@0.6.0:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
dev: false
/cookiejar@2.1.4:
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
dev: true
@@ -18575,6 +18590,9 @@ packages:
/sqlite3@5.1.7:
resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==}
requiresBuild: true
peerDependenciesMeta:
node-gyp:
optional: true
dependencies:
bindings: 1.5.0
node-addon-api: 7.0.0