summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-06-01 08:30:09 +0200
committerRory& <root@rory.gay>2025-06-01 08:30:09 +0200
commit9c90f22c5c68e2320054b99c7e69677f7e778f6b (patch)
tree525fd42a7e398bacddd6b878b3c1ca7435b35393
parentRegister works, part of login and auth middleware (diff)
downloadnodejs-final-assignment-9c90f22c5c68e2320054b99c7e69677f7e778f6b.tar.xz
Login, delete user
-rw-r--r--src/api/middlewares/authMiddleware.js11
-rw-r--r--src/api/routes.js24
-rw-r--r--src/api/routes/auth/accountRoutes.js1
-rw-r--r--src/api/routes/auth/deviceRoutes.js56
-rw-r--r--src/api/routes/auth/index.js1
-rw-r--r--src/db/dbAccess/user.js14
-rw-r--r--src/db/dbAccess/user.test.js19
-rw-r--r--src/util/jwtUtils.js79
8 files changed, 127 insertions, 78 deletions
diff --git a/src/api/middlewares/authMiddleware.js b/src/api/middlewares/authMiddleware.js

index 4cdbb51..a1ba498 100644 --- a/src/api/middlewares/authMiddleware.js +++ b/src/api/middlewares/authMiddleware.js
@@ -7,16 +7,19 @@ import { DbUser } from '#db/schemas/index.js'; */ export function validateAuth(options) { return async function (req, res, next) { - var auth = validateJwtToken(req.headers.authorization); + const auth = (req.auth = validateJwtToken(req.headers.authorization)); if (!auth) { res.status(401).send('Unauthorized'); return; } - req.user = await DbUser.findById(auth.id).exec(); + const user = (req.user = await DbUser.findById(auth.id).exec()); - req.auth = auth; - req = next(); + if (options.roles && !options.roles.includes(user.type)) { + return; + } + + next(); }; } diff --git a/src/api/routes.js b/src/api/routes.js
index 0da8be9..09606f1 100644 --- a/src/api/routes.js +++ b/src/api/routes.js
@@ -12,23 +12,37 @@ export function registerRoutes(app) { ); if (route.onGet) { - app.get(route.route, route.onGet); + if (route.onGetValidation) + app.get(route.route, route.onGetValidation, route.onGet); + else app.get(route.route, route.onGet); routeCount++; } if (route.onPost) { - app.post(route.route, route.onPost); + if (route.onPostValidation) + app.post(route.route, route.onPostValidation, route.onPost); + else app.post(route.route, route.onPost); routeCount++; } if (route.onPut) { - app.put(route.route, route.onPut); + if (route.onPutValidation) + app.put(route.route, route.onPutValidation, route.onPut); + else app.put(route.route, route.onPut); routeCount++; } if (route.onDelete) { - app.put(route.route, route.onDelete); + if (route.onDeleteValidation) + app.delete( + route.route, + route.onDeleteValidation, + route.onDelete + ); + else app.delete(route.route, route.onDelete); routeCount++; } if (route.onPatch) { - app.patch(route.route, route.onPatch); + if (route.onPatchValidation) + app.patch(route.route, route.onPatchValidation, route.onPatch); + else app.patch(route.route, route.onPatch); routeCount++; } }); diff --git a/src/api/routes/auth/accountRoutes.js b/src/api/routes/auth/accountRoutes.js
index 6655ecb..5c88c22 100644 --- a/src/api/routes/auth/accountRoutes.js +++ b/src/api/routes/auth/accountRoutes.js
@@ -20,7 +20,6 @@ export const loginRoute = { */ async onPost(req, res) { const data = await AuthDto.create(req.body); - console.log(req.headers['user-agent']); const loginResult = await loginUser(data, req.headers['user-agent']); res.send(loginResult); } diff --git a/src/api/routes/auth/deviceRoutes.js b/src/api/routes/auth/deviceRoutes.js
index 6655ecb..11cae8f 100644 --- a/src/api/routes/auth/deviceRoutes.js +++ b/src/api/routes/auth/deviceRoutes.js
@@ -1,36 +1,38 @@ import { deleteUser, loginUser, registerUser } from '#db/index.js'; import { AuthDto, RegisterDto } from '#dto/index.js'; +import { validateAuth } from '#api/middlewares/index.js'; -export const registerRoute = { - route: '/auth/register', - async onPost(req, res) { +export const getDevicesRoute = { + route: '/auth/devices', + onGetValidation: validateAuth({}), + async onGet(req, res) { const data = await RegisterDto.create(req.body); const registerResult = await registerUser(data); res.send(registerResult); } }; -export const loginRoute = { - route: '/auth/login', - /** - * - * @param req {Request} - * @param res - * @returns {Promise<WhoAmIDto>} - */ - async onPost(req, res) { - const data = await AuthDto.create(req.body); - console.log(req.headers['user-agent']); - const loginResult = await loginUser(data, req.headers['user-agent']); - res.send(loginResult); - } -}; - -export const deleteRoute = { - route: '/auth/delete', - async onDelete(req, res) { - const data = await AuthDto.create(req.body); - await deleteUser(data); - res.status(204).send(); - } -}; +// export const loginRoute = { +// route: '/auth/login', +// /** +// * +// * @param req {Request} +// * @param res +// * @returns {Promise<WhoAmIDto>} +// */ +// async onPost(req, res) { +// const data = await AuthDto.create(req.body); +// console.log(req.headers['user-agent']); +// const loginResult = await loginUser(data, req.headers['user-agent']); +// res.send(loginResult); +// } +// }; +// +// export const deleteRoute = { +// route: '/auth/delete', +// async onDelete(req, res) { +// const data = await AuthDto.create(req.body); +// await deleteUser(data); +// res.status(204).send(); +// } +// }; diff --git a/src/api/routes/auth/index.js b/src/api/routes/auth/index.js
index 29a07ad..e687911 100644 --- a/src/api/routes/auth/index.js +++ b/src/api/routes/auth/index.js
@@ -1 +1,2 @@ export * from './accountRoutes.js'; +export * from './deviceRoutes.js'; diff --git a/src/db/dbAccess/user.js b/src/db/dbAccess/user.js
index a461f3e..7357b59 100644 --- a/src/db/dbAccess/user.js +++ b/src/db/dbAccess/user.js
@@ -3,6 +3,7 @@ import { DbUser, deviceSchema } from '#db/schemas/index.js'; import { AuthDto, RegisterDto } from '#dto/index.js'; import { SafeNSoundError } from '#util/error.js'; import { WhoAmIDto } from '#dto/auth/WhoAmIDto.js'; +import { generateJwtToken } from '#util/jwtUtils.js'; async function whoAmI(token) {} @@ -63,7 +64,7 @@ export async function registerUser(data) { export async function deleteUser(data) { var user = await getUserByAuth(data); - await DbUser.findByIdAndDelete(data._id); + await DbUser.findByIdAndDelete(user._id); } /** @@ -80,9 +81,18 @@ export async function loginUser(data, deviceName) { user.devices.push(device); await user.save(); - return WhoAmIDto.create({ + const whoAmI = await WhoAmIDto.create({ userId: user._id, username: user.username, deviceId: device._id }); + + whoAmI.access_token = await generateJwtToken({ + type: user.type, + sub: user._id.toString(), + deviceId: device._id.toString(), + iat: Math.floor(Date.now() / 1000) + }); + + return whoAmI; } diff --git a/src/db/dbAccess/user.test.js b/src/db/dbAccess/user.test.js
index 7b72d29..bb3b125 100644 --- a/src/db/dbAccess/user.test.js +++ b/src/db/dbAccess/user.test.js
@@ -4,20 +4,21 @@ import { deleteUser, registerUser } from '#db/index.js'; import * as assert from 'node:assert'; import { initDb } from '#db/db.js'; import { disconnect } from 'mongoose'; +import { AuthDto, RegisterDto } from '#dto/auth/index.js'; dotenv.config(); await initDb(); async function createTestUser() { - const username = (Math.random() * 1000000).toString(); - const password = (Math.random() * 1000000).toString(); - const email = (Math.random() * 1000000).toString() + '@example.com'; + const authData = await AuthDto.create({ + username: (Math.random() * 1000000).toString(), + password: (Math.random() * 1000000).toString(), + email: (Math.random() * 1000000).toString() + '@example.com' + }); return { - username, - password, - email, - user: await registerUser(username, password, email) + authData, + user: await registerUser(await RegisterDto.create(authData)) }; } @@ -26,9 +27,9 @@ await it('Can create user', async () => { }); await it('Can delete user', async () => { - const { password, user } = await createTestUser(); + const { authData } = await createTestUser(); - const deletePromise = deleteUser(user._id, password); + const deletePromise = deleteUser(authData); await assert.doesNotReject(deletePromise); }); diff --git a/src/util/jwtUtils.js b/src/util/jwtUtils.js
index 9031631..ad97666 100644 --- a/src/util/jwtUtils.js +++ b/src/util/jwtUtils.js
@@ -1,14 +1,25 @@ -import {existsSync} from 'fs'; -import {readFile, writeFile} from "node:fs/promises"; -import {generateKeyPairSync, createHash, createPublicKey, createPrivateKey} from 'node:crypto'; -import jwt from "jsonwebtoken"; +import { existsSync } from 'fs'; +import { readFile, writeFile } from 'node:fs/promises'; +import { + generateKeyPairSync, + createHash, + createPublicKey, + createPrivateKey +} from 'node:crypto'; +import jwt from 'jsonwebtoken'; let privateKey, publicKey, fingerprint; +/** + * + * @returns {Promise<JwtData>} + */ export async function initJwt() { const secretPath = process.env.JWT_SECRET_PATH; if (!secretPath || !existsSync(secretPath)) { - throw new Error('JWT secret path is not defined in environment variables, or the directory does not exist.'); + throw new Error( + 'JWT secret path is not defined in environment variables, or the directory does not exist.' + ); } console.log(`[JWT] Initializing JWT with secret path: ${secretPath}`); @@ -17,9 +28,9 @@ export async function initJwt() { const publicKeyPath = `${secretPath}/jwt.key.pub`; if (!existsSync(privateKeyPath)) { - console.log("[JWT] Generating new keypair"); - const keyPair = generateKeyPairSync("ec", { - namedCurve: "secp521r1", + console.log('[JWT] Generating new keypair'); + const keyPair = generateKeyPairSync('ec', { + namedCurve: 'secp521r1' }); privateKey = keyPair.privateKey; @@ -28,16 +39,16 @@ export async function initJwt() { await Promise.all([ writeFile( privateKeyPath, - privateKey.export({format: "pem", type: "sec1"}), + privateKey.export({ format: 'pem', type: 'sec1' }) ), writeFile( publicKeyPath, - publicKey.export({format: "pem", type: "spki"}), - ), + publicKey.export({ format: 'pem', type: 'spki' }) + ) ]); - console.log("[JWT] Keypair generated successfully."); + console.log('[JWT] Keypair generated successfully.'); } else { - console.log("[JWT] Using existing keypair"); + console.log('[JWT] Using existing keypair'); const loadedPrivateKey = await readFile(privateKeyPath, 'utf8'); const loadedPublicKey = await readFile(publicKeyPath, 'utf8'); @@ -45,32 +56,32 @@ export async function initJwt() { publicKey = createPublicKey(loadedPublicKey); } - fingerprint = createHash("sha256") - .update(publicKey.export({format: "pem", type: "spki"})) - .digest("hex"); + fingerprint = createHash('sha256') + .update(publicKey.export({ format: 'pem', type: 'spki' })) + .digest('hex'); } /** * @type {import('jsonwebtoken').JwtOptions} */ const jwtOptions = { - algorithm: 'ES512', -} + algorithm: 'ES512' +}; -export async function generateJwtToken(user) { +/** + * + * @param data {JwtData} + * @returns {Promise<unknown>} + */ +export async function generateJwtToken(data) { if (!privateKey) { - throw new Error('JWT private key is not initialized. Please call initJwt() first.'); + throw new Error( + 'JWT private key is not initialized. Please call initJwt() first.' + ); } - const payload = { - sub: user._id.toString(), - username: user.username, - type: user.type, - iat: Math.floor(Date.now() / 1000) - }; - return new Promise((resolve, reject) => { - jwt.sign(payload, privateKey, jwtOptions, (err, token) => { + jwt.sign(data, privateKey, jwtOptions, (err, token) => { if (err) { console.error('[JWT] Error generating token:', err); return reject(err); @@ -82,7 +93,9 @@ export async function generateJwtToken(user) { export async function validateJwtToken(token) { if (!publicKey) { - throw new Error('JWT public key is not initialized. Please call initJwt() first.'); + throw new Error( + 'JWT public key is not initialized. Please call initJwt() first.' + ); } return new Promise((resolve, reject) => { @@ -94,4 +107,10 @@ export async function validateJwtToken(token) { resolve(decoded); }); }); -} \ No newline at end of file +} + +export class JwtData { + sub; + type; + iat = Math.floor(Date.now() / 1000); +}