From 7564937faafa1e81d8d1cd66de22f3817cad7075 Mon Sep 17 00:00:00 2001 From: Cameron Redmore Date: Sat, 26 Apr 2025 08:32:54 +0100 Subject: [PATCH] Authentication flow change - Remove requirement for user to enter username - Use Passkey discoverability instead. --- .vscode/settings.json | 5 +++- quasar.config.js | 5 +++- src-server/routes/auth.js | 49 +++++++++++++++++--------------- src/css/app.scss | 19 ++++++++++++- src/layouts/MainLayout.vue | 6 ++-- src/pages/LandingPage.vue | 22 ++++----------- src/pages/LoginPage.vue | 57 ++++++++++++++++++++++---------------- src/pages/RegisterPage.vue | 32 +++++++++++++++------ 8 files changed, 120 insertions(+), 75 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c0c478e..4eee679 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,5 +16,8 @@ "editor.formatOnSave": true, "files.eol": "\n", "files.trimTrailingWhitespace": true, - "editor.trimAutoWhitespace": true + "editor.trimAutoWhitespace": true, + "[scss]": { + "editor.defaultFormatter": "vscode.css-language-features" + } } \ No newline at end of file diff --git a/quasar.config.js b/quasar.config.js index 0c26a16..3003be1 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -50,7 +50,10 @@ export default defineConfig((/* ctx */) => // publicPath: '/', // analyze: true, - // env: {}, + env: { + API_URL: process.env.API_URL || '/api', + PRODUCT_NAME: process.env.PRODUCT_NAME || 'StylePoint', + }, // rawDefine: {} // ignorePublicFolder: true, // minify: false, diff --git a/src-server/routes/auth.js b/src-server/routes/auth.js index 5280430..20e018c 100644 --- a/src-server/routes/auth.js +++ b/src-server/routes/auth.js @@ -60,7 +60,7 @@ router.post('/generate-registration-options', async(req, res) => //Check if the registrationToken matches the setting const registrationTokenSetting = await getSetting('REGISTRATION_TOKEN'); - if (registrationTokenSetting !== registrationToken) + if (registrationTokenSetting !== registrationToken && !req.session.loggedInUserId) { return res.status(403).json({ error: 'Invalid registration token' }); } @@ -200,9 +200,6 @@ router.post('/verify-registration', async(req, res) => challengeStore.delete(userId); delete req.session.userId; - // Log the user in by setting the final session userId - req.session.loggedInUserId = user.id; - res.json({ verified: true }); } else @@ -243,27 +240,29 @@ router.post('/generate-authentication-options', async(req, res) => user = await getUserById(req.session.loggedInUserId); } - if (!user) - { - return res.status(404).json({ error: 'User not found' }); - } + // if (!user) + // { + // return res.status(404).json({ error: 'User not found' }); + // } - const userAuthenticators = await getUserAuthenticators(user.id); + // const userAuthenticators = await getUserAuthenticators(user.id); const options = await generateAuthenticationOptions({ rpID, // Require users to use a previously-registered authenticator - allowCredentials: userAuthenticators.map(auth => ({ - id: auth.credentialID, - type: 'public-key', - transports: auth.transports ? auth.transports.split(',') : undefined, - })), + // allowCredentials: userAuthenticators.map(auth => ({ + // id: auth.credentialID, + // type: 'public-key', + // transports: auth.transports ? auth.transports.split(',') : undefined, + // })), + allowCredentials: [], userVerification: 'preferred', }); - // Store the challenge associated with the user ID for verification - challengeStore.set(user.id, options.challenge); - req.session.challengeUserId = user.id; // Store user ID associated with this challenge + //Store challenge associated with random ID + const challengeId = crypto.randomUUID(); + challengeStore.set(challengeId, options.challenge); + req.session.challengeUserId = challengeId; // Store user ID associated with this challenge res.json(options); } @@ -294,11 +293,11 @@ router.post('/verify-authentication', async(req, res) => try { - const user = await getUserById(challengeUserId); - if (!user) - { - return res.status(404).json({ error: 'User associated with challenge not found' }); - } + // const user = await getUserById(challengeUserId); + // if (!user) + // { + // return res.status(404).json({ error: 'User associated with challenge not found' }); + // } const authenticator = await getAuthenticatorByCredentialID(authenticationResponse.id); @@ -307,6 +306,12 @@ router.post('/verify-authentication', async(req, res) => return res.status(404).json({ error: 'Authenticator not found' }); } + const user = await getUserById(authenticator.userId); + if (!user) + { + return res.status(404).json({ error: 'User not found' }); + } + // Ensure the authenticator belongs to the user attempting to log in if (authenticator.userId !== user.id) { diff --git a/src/css/app.scss b/src/css/app.scss index 824d96e..09428ab 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -2,6 +2,23 @@ @import url('https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100..900;1,100..900&display=swap'); -html, body { +html, +body { font-family: 'Exo 2', sans-serif; +} + +.bg-theme { + background: linear-gradient(to top left, + #fdc730, + #ea2963, + #bd288a 50%, + #6e43ac, + #4763bf, + #16a3e8) !important; +} + + +.bg-blurred { + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(10px); } \ No newline at end of file diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index f88e85a..d52d29b 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -18,7 +18,7 @@ - StylePoint + {{ productName }} @@ -195,7 +195,7 @@ @@ -265,6 +265,8 @@ import { usePreferencesStore } from 'stores/preferences'; // Import the preferen import ChatInterface from 'components/ChatInterface.vue'; // Adjust path as needed import routes from '../router/routes'; // Import routes +const productName = process.env.PRODUCT_NAME; + const $q = useQuasar(); const leftDrawerOpen = ref(false); const router = useRouter(); diff --git a/src/pages/LandingPage.vue b/src/pages/LandingPage.vue index df089f5..79ce8c1 100644 --- a/src/pages/LandingPage.vue +++ b/src/pages/LandingPage.vue @@ -1,14 +1,14 @@