diff --git a/backend/routes/msal.js b/backend/routes/msal.js
deleted file mode 100644
index cdd6920..0000000
--- a/backend/routes/msal.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License.
- */
-
-const fs = require('fs');
-const crypto = require('crypto');
-const express = require('express');
-
-const msal = require('@azure/msal-node');
-
-/**
- * If you have encrypted your private key with a *pass phrase* as recommended,
- * you'll need to decrypt it before passing it to msal-node for initialization.
- */
-// Secrets should never be hardcoded. The dotenv npm package can be used to store secrets or certificates
-// in a .env file (located in project's root directory) that should be included in .gitignore to prevent
-// accidental uploads of the secrets.
-
-// Certificates can also be read-in from files via NodeJS's fs module. However, they should never be
-// stored in the project's directory. Production apps should fetch certificates from
-// Azure KeyVault (https://azure.microsoft.com/products/key-vault), or other secure key vaults.
-
-// Please see "Certificates and Secrets" (https://learn.microsoft.com/azure/active-directory/develop/security-best-practices-for-app-registration#certificates-and-secrets)
-// for more information.
-const privateKeySource = fs.readFileSync('../certs/example.key');
-
-const privateKeyObject = crypto.createPrivateKey({
- key: privateKeySource,
- passphrase: "2255", // enter your certificate passphrase here
- format: 'pem'
-});
-
-const privateKey = privateKeyObject.export({
- format: 'pem',
- type: 'pkcs8'
-});
-
-// Before running the sample, you will need to replace the values in the config
-const config = {
- auth: {
- clientId: "3cdfac60-e7fb-4648-89d3-67966c497d35", //Client ID
- authority: "https://login.microsoftonline.com/538b9b1c-23fa-4102-b36e-a4d83fc9c4c1", //Tenant ID
- clientCertificate: {
- thumbprint: 'DD79B973F2D634840948970C712907DF4423C982', // can be obtained when uploading certificate to Azure AD
- privateKey: privateKey,
- }
- },
- system: {
- loggerOptions: {
- loggerCallback(loglevel, message, containsPii) {
- console.log(message);
- },
- piiLoggingEnabled: false,
- logLevel: msal.LogLevel.Verbose,
- }
- }
-};
-
-// Create msal application object
-const cca = new msal.ConfidentialClientApplication(config);
-
-// Create Express app
-const app = express();
-
-app.use(express.urlencoded({ extended: false }));
-
-app.get('/', (req, res) => {
- const authCodeUrlParameters = {
- scopes: ["user.read"],
- redirectUri: "http://localhost:3000/redirect",
- responseMode: 'form_post',
- };
-
- // get url to sign user in and consent to scopes needed for application
- cca.getAuthCodeUrl(authCodeUrlParameters).then((response) => {
- console.log(response);
- res.redirect(response);
- }).catch((error) => console.log(JSON.stringify(error)));
-});
-
-app.post('/redirect', (req, res) => {
- const tokenRequest = {
- code: req.body.code,
- scopes: ["user.read"],
- redirectUri: "http://localhost:3000/redirect",
- };
-
- cca.acquireTokenByCode(tokenRequest).then((response) => {
- console.log("\nResponse: \n:", response);
- res.status(200).send('Congratulations! You have signed in successfully');
- }).catch((error) => {
- console.log(error);
- res.status(500).send(error);
- });
-});
-
-const SERVER_PORT = process.env.PORT || 3000;
-
-app.listen(SERVER_PORT, () => {
- console.log(`Msal Node Auth Code Sample app listening on port ${SERVER_PORT}!`)
-});
\ No newline at end of file
diff --git a/backend/routes/passport.js b/backend/routes/passport.js
deleted file mode 100644
index 576647c..0000000
--- a/backend/routes/passport.js
+++ /dev/null
@@ -1,64 +0,0 @@
-const express = require('express');
-const session = require('express-session');
-const passport = require('passport');
-const { OIDCStrategy } = require('passport-azure-ad');
-
-const app = express();
-
-// Session setup
-app.use(
- session({
- secret: 'your-secret',
- resave: false,
- saveUninitialized: true,
- })
-);
-
-// Azure AD OIDC Strategy
-passport.use(
- new OIDCStrategy(
- {
- identityMetadata: `https://login.microsoftonline.com/538b9b1c-23fa-4102-b36e-a4d83fc9c4c1/v2.0/.well-known/openid-configuration`,
- clientID: '3cdfac60-e7fb-4648-89d3-67966c497d35',
- responseType: 'code',
- responseMode: 'query',
- redirectUrl: 'http://localhost:3000/auth/callback',
- clientSecret: '5Gi8Q~_pmDtvN3.Jwqt85kiI.uiyAAC7Z.4iFayY',
- allowHttpForRedirectUrl: true,
- },
- (issuer, sub, profile, accessToken, refreshToken, done) => {
- // Save the user profile and tokens
- return done(null, { profile, accessToken, refreshToken });
- }
- )
-);
-
-// Passport serialization
-passport.serializeUser((user, done) => done(null, user));
-passport.deserializeUser((user, done) => done(null, user));
-
-// Initialize Passport
-app.use(passport.initialize());
-app.use(passport.session());
-
-// Authentication routes
-app.get('/auth', passport.authenticate('azuread-openidconnect'));
-
-app.get(
- '/auth/callback',
- passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
- (req, res) => {
- res.send("Success");
- }
-);
-
-// Logout route
-app.get('/logout', (req, res) => {
- req.logout(() => {
- res.redirect('/');
- });
-});
-
-// Start server
-const port = process.env.PORT || 3000;
-app.listen(port, () => console.log(`Server running on http://localhost:${port}`));
diff --git a/backend/src/controllers/authController.js b/backend/src/controllers/authController.js
index 4fc9004..7cbc8ff 100644
--- a/backend/src/controllers/authController.js
+++ b/backend/src/controllers/authController.js
@@ -35,7 +35,31 @@ exports.login = async (req, res) => {
}
// Set the token as an HttpOnly cookie
- res.cookie('authToken', token, {
+ res.cookie('token', token, {
+ httpOnly: true, // Cannot be accessed by JavaScript
+ secure: process.env.NODE_ENV === 'production', // Use Secure flag only in production (requires HTTPS)
+ sameSite: 'Strict', // Prevents CSRF attacks
+ maxAge: 3600000, // 1 hour
+ });
+
+ // Set the token as an HttpOnly cookie
+ res.cookie('name', user.name, {
+ httpOnly: true, // Cannot be accessed by JavaScript
+ secure: process.env.NODE_ENV === 'production', // Use Secure flag only in production (requires HTTPS)
+ sameSite: 'Strict', // Prevents CSRF attacks
+ maxAge: 3600000, // 1 hour
+ });
+
+ // Set the token as an HttpOnly cookie
+ res.cookie('email', user.email, {
+ httpOnly: true, // Cannot be accessed by JavaScript
+ secure: process.env.NODE_ENV === 'production', // Use Secure flag only in production (requires HTTPS)
+ sameSite: 'Strict', // Prevents CSRF attacks
+ maxAge: 3600000, // 1 hour
+ });
+
+ // Set the token as an HttpOnly cookie
+ res.cookie('role', user.role, {
httpOnly: true, // Cannot be accessed by JavaScript
secure: process.env.NODE_ENV === 'production', // Use Secure flag only in production (requires HTTPS)
sameSite: 'Strict', // Prevents CSRF attacks
diff --git a/backend/src/controllers/documentController.js b/backend/src/controllers/documentController.js
index 0e0ef9d..0c99bda 100644
--- a/backend/src/controllers/documentController.js
+++ b/backend/src/controllers/documentController.js
@@ -30,6 +30,47 @@ exports.getDocumentById = async (req, res) => {
}
};
+exports.searchDocuments = async (req, res) => {
+ try {
+ const { query } = req.query; // Extract the search query from the query string
+
+ if (!query) {
+ return res.status(400).json({ message: "Search query is required." });
+ }
+
+ // Perform the search
+ const documents = await Document.find({
+ $or: [
+ { title: { $regex: query, $options: "i" } }, // Case-insensitive match in title
+ { body: { $regex: query, $options: "i" } }, // Case-insensitive match in body
+ { tags: { $regex: query, $options: "i" } }, // Case-insensitive match in tags array
+ ],
+ });
+
+ // Highlight the query in the fields
+ const highlightedDocuments = documents.map((doc) => {
+ const highlightMatch = (text) =>
+ text.replace(
+ new RegExp(`(${query})`, "gi"), // Match the query case-insensitively
+ '$1'
+ );
+
+ return {
+ ...doc._doc, // Convert Mongoose document to plain object
+ title: highlightMatch(doc.title || ""),
+ body: highlightMatch(doc.body || ""),
+ tags: doc.tags.map((tag) => highlightMatch(tag)), // Highlight each tag
+ };
+ });
+
+ res.json(highlightedDocuments); // Return the matching and highlighted documents
+ } catch (error) {
+ res.status(500).json({ message: error.message });
+ }
+};
+
+
+
exports.updateDocumentById = async (req, res) => {
try {
const documentId = req.params.id;
diff --git a/backend/src/models/Document.js b/backend/src/models/Document.js
index e58541e..e8ebbca 100644
--- a/backend/src/models/Document.js
+++ b/backend/src/models/Document.js
@@ -13,7 +13,7 @@ const DocumentSchema = new mongoose.Schema(
visibility: {
type: String,
required: true,
- default: "Public",
+ default: "public",
},
tags: {
type: [String], //Array of strings
diff --git a/backend/src/models/User.js b/backend/src/models/User.js
index fd74cb0..c6f69c9 100644
--- a/backend/src/models/User.js
+++ b/backend/src/models/User.js
@@ -21,7 +21,7 @@ const UserSchema = new mongoose.Schema(
role: {
type: String,
required: false, // Optional for OAuth users
- default: "User", //User = Read, Write Permissions | Admin = Read, Write, Delete Permissions
+ default: "user", //User = Read, Write Permissions | Admin = Read, Write, Delete Permissions
},
oauthProviders: [
{
diff --git a/backend/src/routes/documentRoutes.js b/backend/src/routes/documentRoutes.js
index 4609175..a3de99a 100644
--- a/backend/src/routes/documentRoutes.js
+++ b/backend/src/routes/documentRoutes.js
@@ -4,6 +4,7 @@ const router = express.Router();
const documentController = require('../controllers/documentController');
router.post('/', documentController.createDocument); //Create
+router.get('/search', documentController.searchDocuments); //Get a document by id
router.get('/', documentController.getDocument); //Get all documents
router.get('/:id', documentController.getDocumentById); //Get a document by id
router.put('/:id', authenticateToken, documentController.updateDocumentById); //Update a document by id
diff --git a/frontend/env.locals b/frontend/env.locals
index 3f8e4f4..ea69bd1 100644
--- a/frontend/env.locals
+++ b/frontend/env.locals
@@ -1 +1 @@
-BACKEND_URI='http://localhost:3000'
\ No newline at end of file
+PUBLIC_BASE_URL='http://localhost:3000'
\ No newline at end of file
diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts
new file mode 100644
index 0000000..ff6461b
--- /dev/null
+++ b/frontend/src/hooks.server.ts
@@ -0,0 +1,20 @@
+export async function handle({ event, resolve }) {
+ // this cookie would be set inside a login route
+ const token = event.cookies.get('token');
+ const name = event.cookies.get('name');
+ const email = event.cookies.get('email');
+ const role = event.cookies.get('role');
+
+ // you can get the user data from a database
+ // const user = await getUser(session)
+
+ // this is passed to `event` inside server `load` functions
+ // and passed to handlers inside `+page.ts`
+ event.locals.user = {
+ token: token,
+ name: name,
+ email: email,
+ role: role
+ }
+ return resolve(event)
+}
\ No newline at end of file
diff --git a/frontend/src/routes/+layout.server.ts b/frontend/src/routes/+layout.server.ts
index 4c85c3f..a1e537d 100644
--- a/frontend/src/routes/+layout.server.ts
+++ b/frontend/src/routes/+layout.server.ts
@@ -1,42 +1,3 @@
-import type { LayoutServerLoad } from './$types';
-import { PUBLIC_BASE_URL } from '$env/static/public';
-
-export const load: LayoutServerLoad = async ({ cookies, fetch }) => {
- let isAuthenticated = false;
- let user = null;
- let errorMessage = '';
-
- // Retrieve the auth token from cookies
- const token = cookies.get('authToken');
-
- if (!token) {
- errorMessage = 'No auth token found. Please log in.';
- return { isAuthenticated, user, errorMessage };
- }
-
- try {
- // Make a server-side fetch request to validate the token
- const response = await fetch(`${PUBLIC_BASE_URL}/auth/status`, {
- method: 'GET',
- headers: {
- Authorization: `Bearer ${token}`,
- },
- credentials: 'include',
- });
-
- if (response.ok) {
- const data = await response.json();
- isAuthenticated = data.authenticated;
- user = data.user;
- } else {
- errorMessage = 'Authentication failed. Please log in again.';
- }
- } catch (error) {
- console.error('Error checking authentication:', error);
- errorMessage = 'An error occurred while checking authentication.';
- }
-
- // Return data to the layout
- return { isAuthenticated, user, errorMessage };
-};
-
+export async function load({ locals }) {
+ return { user: locals.user }
+}
diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte
index d812d5b..1fb907e 100644
--- a/frontend/src/routes/+layout.svelte
+++ b/frontend/src/routes/+layout.svelte
@@ -1,32 +1,17 @@
@@ -35,17 +20,17 @@
-
@@ -53,17 +38,16 @@
{#if dropDown}
- {#if data.isAuthenticated}
+ {#if $page.data.user.name}
- Signed in
+ Signed in as
+ {$page.data.user.name}
-
+
+
{:else}
- You are not signed in {dropDown = false;}}>