From e9464b8a10eb57d9b021251ff97bcbd9c53a1e98 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 24 Oct 2024 13:37:32 +0300 Subject: `rememberme` middleware: authorize using Spring Security cookie --- config/test.json | 5 +++- jest.config.js | 2 +- package-lock.json | 21 +++++++++++++++ package.json | 1 + vnext/server/app.js | 4 +++ vnext/server/db/Users.js | 1 + vnext/server/middleware/rememberme.js | 50 +++++++++++++++++++++++++++++++++++ 7 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 vnext/server/middleware/rememberme.js diff --git a/config/test.json b/config/test.json index fb3fae9d..649179d6 100644 --- a/config/test.json +++ b/config/test.json @@ -3,6 +3,9 @@ "baseURL": "https://example.lan", "user": "juick", "password": "secret", - "sandboxIds": [] + "sandboxIds": [], + "auth": { + "key": "" + } } } diff --git a/jest.config.js b/jest.config.js index abaffdbd..69b061e1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,6 +8,6 @@ module.exports = { '^.+\\.js$': '@swc/jest' }, transformIgnorePatterns: ['!node_modules/'], - testEnvironment: 'jsdom', + testEnvironment: 'node', setupFilesAfterEnv: ['/setupTests.js'] } diff --git a/package-lock.json b/package-lock.json index 2b9d35ec..e8096cbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "body-parser": "^1.20.3", "classlist.js": "^1.1.20150312", "config": "^3.3.12", + "cookie-parser": "^1.4.7", "css-minimizer-webpack-plugin": "^7.0.0", "dayjs": "^1.11.13", "element-closest": "3.0.2", @@ -7389,6 +7390,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/package.json b/package.json index bfeefff3..33455d8d 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "body-parser": "^1.20.3", "classlist.js": "^1.1.20150312", "config": "^3.3.12", + "cookie-parser": "^1.4.7", "css-minimizer-webpack-plugin": "^7.0.0", "dayjs": "^1.11.13", "element-closest": "3.0.2", diff --git a/vnext/server/app.js b/vnext/server/app.js index 223868f1..4249b922 100644 --- a/vnext/server/app.js +++ b/vnext/server/app.js @@ -1,5 +1,6 @@ import express from 'express' import { raw } from 'body-parser' +import cookieParser from 'cookie-parser' import cors from 'cors' import config from 'config' @@ -14,11 +15,14 @@ import path from 'path' import { webhook, webhookPath } from './durov' import { webfinger } from './middleware/webfinger' import { jsonMeta, nodeinfo, xmlMeta } from './middleware/host-meta' +import { rememberMeParser } from './middleware/rememberme' // initialize the application and create the routes export const app = express() app.use(raw()) app.use(cors()) +app.use(cookieParser()) +app.use(rememberMeParser) const router = express.Router() router.post('/api/v2/sender', event) diff --git a/vnext/server/db/Users.js b/vnext/server/db/Users.js index 3f4422e2..445b0ff0 100644 --- a/vnext/server/db/Users.js +++ b/vnext/server/db/Users.js @@ -7,6 +7,7 @@ const User = db.define('user', { primaryKey: true }, nick: DataTypes.STRING, + passw: DataTypes.STRING, banned: DataTypes.INTEGER, last_seen: DataTypes.DATE }, { diff --git a/vnext/server/middleware/rememberme.js b/vnext/server/middleware/rememberme.js new file mode 100644 index 00000000..1c0a2ee4 --- /dev/null +++ b/vnext/server/middleware/rememberme.js @@ -0,0 +1,50 @@ +import config from 'config' +import { createHash } from 'node:crypto' +import debug from 'debug' +import { getUserByName } from '../db/Users' +const log = debug('auth') + +const auth_key = config.get('service.auth.key') +const JUICK_COOKIE_NAME = 'juick-remember-me' + +export const rememberMeParser = (req, _res, next) => { + if (req.cookies && !!req.cookies[JUICK_COOKIE_NAME]) { + validate_cookie(req.cookies[JUICK_COOKIE_NAME]).then(visitor => { + req.visitor = visitor + setImmediate(next) + }).catch(() => { + setImmediate(next) + }) + } else { + setImmediate(next) + } +} + +const validate_cookie = async (cookie) => { + const [ username, expiry_time, , signature ] = Buffer.from(cookie, 'base64').toString('ascii').split(':', 4) + if (is_token_expired(expiry_time)) { + log(`Token expired at: ${new Date(expiry_time)}`) + return '' + } + const user = await getUserByName(username) + if (!user) { + log(`User not found: ${username}`) + return '' + } + const expected_signature = make_token_signature(expiry_time, username, user['passw']) + if (expected_signature === signature) { + log(`Signature verified: ${username}`) + return username + } + log(`Invalid token signature: ${username}`) + return '' +} + +const make_token_signature = (expiry_time, username, password) => { + const data = `${username}:${expiry_time}:{noop}${password}:${auth_key}` + return createHash('sha256').update(data).digest('hex') +} + +const is_token_expired = (expiry_time) => { + return new Date(expiry_time) < new Date() +} -- cgit v1.2.3