aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2024-10-24 13:37:32 +0300
committerGravatar Vitaly Takmazov2024-10-24 23:40:41 +0300
commite9464b8a10eb57d9b021251ff97bcbd9c53a1e98 (patch)
tree6d9d37c1d9b39a667d869fe4f2784e1add2e9959
parent4bb9c0dd855e1f79308908a97106bf173088cfb8 (diff)
`rememberme` middleware: authorize using Spring Security cookie
-rw-r--r--config/test.json5
-rw-r--r--jest.config.js2
-rw-r--r--package-lock.json21
-rw-r--r--package.json1
-rw-r--r--vnext/server/app.js4
-rw-r--r--vnext/server/db/Users.js1
-rw-r--r--vnext/server/middleware/rememberme.js50
7 files changed, 82 insertions, 2 deletions
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: ['<rootDir>/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()
+}