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);
+}
|