Feature: Authentication model for web and tauri simulator#657
Feature: Authentication model for web and tauri simulator#657tachyons merged 18 commits intoCircuitVerse:mainfrom
Conversation
WalkthroughAdds Tauri HTTP plugin and capabilities, updates NPM and Rust manifests, initializes the plugin in the Tauri builder, and adds HTTP allow rules for two auth endpoints. Reworks the UserMenu across root, v0, and v1 variants into a right-side drawer with an in-app auth modal, snackbar feedback, and dark SCSS styles. Extends auth stores with setToken and signOut and adds TypeScript global types. Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Deploy Preview for circuitverse ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (24)
tsconfig.json (1)
24-26: Confirm declaration maps and new global typings are intended
declarationMap: trueships source paths; fine for dev, avoid in production if you don’t publish d.ts.@tauri-apps/api/tauriintypesis correct for Tauri globals.If not needed, disable maps:
- "declarationMap": true, + "declarationMap": false,v0/src/components/Navbar/User/User.scss (1)
1-54: Reduce selector leakage and reliance on !importantScope the free‑floating selectors under
.userMenuand prefer theme variables over blanket!importantto avoid surprising overrides and improve a11y/theming.Apply minimal scoping:
-.avatar-menu { +.userMenu .avatar-menu { background: transparent !important; } -.avatar-btn { +.userMenu .avatar-btn { text-transform: none; letter-spacing: normal; } -.list-item-avatar .v-list-item-title { +.userMenu .list-item-avatar .v-list-item-title { font-weight: 500; font-size: 1.1rem; color: white !important; }Consider replacing hardcoded white with Vuetify tokens (e.g.,
color: rgb(var(--v-theme-on-surface));) in a follow‑up.src-tauri/Cargo.toml (1)
25-25: Gate HTTP plugin and pin tauri-plugin-http
- Verified: src-tauri/capabilities/default.json contains "http:default" and an "http:allow-fetch" entry that only allows:
- Action: Pin the plugin in src-tauri/Cargo.toml (currently tauri-plugin-http = "2") to a concrete 2.x patch to avoid accidental breakage. Example:
-tauri-plugin-http = "2" +tauri-plugin-http = "2.5.2"v1/src/store/authStore.ts (1)
89-90: Fix TODO wording.Minor spelling/clarity.
-// TODO: extract store verify and check better ways to impliment +// TODO: extract store verification and explore better implementation approachessrc/components/Navbar/User/User.scss (1)
1-54: Scope and de-duplicate styles; reduce reliance on !important.These overrides are duplicated in v1/.../User.scss and heavily use
!important, which risks unintended bleed. Prefer a shared SCSS (imported in both variants) and scope under a component root class to drop most!importants.v1/src/components/Navbar/User/User.scss (1)
1-54: Same SCSS duplicated under src/... and v1/.... Consolidate.Keep a single
User.scssand import it from both locations to avoid drift.src/store/authStore.ts (1)
89-90: Fix TODO spelling/wording (same as v1).-// TODO: extract store verify and check better ways to impliment +// TODO: extract store verification and explore better implementation approachessrc/components/Navbar/User/UserMenu.vue (3)
268-273: Standardize snackbar colors.Mixing hex and semantic colors yields inconsistent theming. Default to a semantic color.
const snackbar = ref({ visible: false, message: '', - color: '#43b984' + color: 'success' })
316-323: Defensive check for server response shape before usingdata.token.Prevents silent failures when the API returns a different payload.
- const data = await response.json() - authStore.setToken(data.token) + const data = await response.json() + if (!data || !data.token) { + showSnackbar('Malformed server response', 'error') + return + } + authStore.setToken(data.token)
302-309: Consider using Tauri HTTP plugin in desktop to bypass CORS and honor capabilities.In Tauri, prefer
@tauri-apps/plugin-httpfor the auth calls; fall back towindow.fetchon web.// example (outside of diff scope) // import { fetch as tFetch } from '@tauri-apps/plugin-http' const doFetch = (window as any).__TAURI__ ? (window as any).__TAURI_HTTP_FETCH__ ?? fetch : fetch // then: const response = await doFetch(url, { method: 'POST', headers: {...}, body: ... })v1/src/components/Navbar/User/UserMenu.vue (8)
316-323: Validate token presence before storing.Avoid setting an undefined token; surface a clear error.
- const data = await response.json() - authStore.setToken(data.token) + const data = await response.json() + if (!data?.token) { + throw new Error('No token received from server') + } + authStore.setToken(data.token)
26-33: Prevent broken avatar when using a 'default' sentinel.Guard prepend-avatar to avoid passing a non-URL string.
- :prepend-avatar="authStore.getUserAvatar" + :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined" :prepend-icon=" authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined "
231-236: Fix snackbar close button icon and drop unused import.Use a standard icon name; avoid mixing @mdi/js path usage with Vuetify defaults.
-import { mdiClose } from '@mdi/js' import './User.scss'- <v-btn - variant="text" - @click="snackbar.visible = false" - :icon="mdiClose" - color="white" - ></v-btn> + <v-btn variant="text" @click="snackbar.visible = false" color="white"> + <v-icon>mdi-close</v-icon> + </v-btn>Also applies to: 247-249
258-266: Type the VForm ref for TS safety.Prevents “any” leakage on validate().
-import { ref } from 'vue' +import { ref } from 'vue' +import type { VForm } from 'vuetify/components'-const authForm = ref() +const authForm = ref<VForm | null>(null)Also applies to: 242-246
164-173: Add autocomplete attributes for better UX/security.Improve autofill and password manager behavior.
- <v-text-field + <v-text-field v-if="!isLoginMode" v-model="name" label="Name" type="text" :rules="[requiredRule]" variant="outlined" class="mb-0" bg-color="#f0eee6" + autocomplete="name" ></v-text-field>- <v-text-field + <v-text-field v-model="email" label="Email" type="email" :rules="[requiredRule, emailRule]" variant="outlined" class="mb-0" bg-color="#f0eee6" + autocomplete="email" ></v-text-field>- <v-text-field + <v-text-field v-model="password" label="Password" type="password" :rules="[requiredRule, passwordRule]" variant="outlined" class="mb-0" bg-color="#f0eee6" + :autocomplete="isLoginMode ? 'current-password' : 'new-password'" ></v-text-field>Also applies to: 174-183, 184-193
370-373: Close the drawer on logout.Minor UX polish.
function signout() { authStore.signOut() showSnackbar('You have been logged out', 'info') + drawer.value = false }
358-368: Prefer router navigation over full page reloads.Use useRouter().push for SPA behavior.
-import { ref } from 'vue' +import { ref } from 'vue' +import { useRouter } from 'vue-router' ... -function dashboard() { - window.location.href = `/users/${authStore.getUserId}` -} +const router = useRouter() +function dashboard() { + router.push(`/users/${authStore.getUserId}`) +} ... - window.location.href = `/users/${authStore.getUserId}/groups` + router.push(`/users/${authStore.getUserId}/groups`) ... - window.location.href = `/users/${authStore.getUserId}/notifications` + router.push(`/users/${authStore.getUserId}/notifications`)
2-205: Externalize UI strings to i18n ($t)
Replace all hard-coded labels in v1/src/components/Navbar/User/UserMenu.vue (lines 2–205; also 221–236, 248–356) with $t keys to enable localization. Generate a keys map + migration diff?v0/src/components/Navbar/User/UserMenu.vue (6)
24-34: Prevent broken avatar when using a 'default' sentinel.- <v-list-item + <v-list-item class="list-item-avatar" - :prepend-avatar="authStore.getUserAvatar" + :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined" :prepend-icon=" authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined " :title="authStore.getUsername" lines="two" color="white" ></v-list-item>
227-233: Fix snackbar close icon and remove unused mdiClose import.-import { mdiClose } from '@mdi/js' -import { fetch } from '@tauri-apps/plugin-http' +import { fetch } from '@tauri-apps/plugin-http'- <v-btn - variant="text" - @click="snackbar.visible = false" - :icon="mdiClose" - color="white" - ></v-btn> + <v-btn variant="text" @click="snackbar.visible = false" color="white"> + <v-icon>mdi-close</v-icon> + </v-btn>Also applies to: 243-246
256-257: Type the VForm ref for TS safety.-import { ref } from 'vue' +import { ref } from 'vue' +import type { VForm } from 'vuetify/components'-const authForm = ref() +const authForm = ref<VForm | null>(null)Also applies to: 238-246
159-168: Add autocomplete attributes to form fields.- <v-text-field + <v-text-field v-if="!isLoginMode" v-model="name" label="Name" type="text" :rules="[requiredRule]" variant="outlined" class="mb-0" bg-color="#f0eee6" + autocomplete="name" ></v-text-field>- <v-text-field + <v-text-field v-model="email" label="Email" type="email" :rules="[requiredRule, emailRule]" variant="outlined" class="mb-0" bg-color="#f0eee6" + autocomplete="email" ></v-text-field>- <v-text-field + <v-text-field v-model="password" label="Password" type="password" :rules="[requiredRule, passwordRule]" variant="outlined" class="mb-0" bg-color="#f0eee6" + :autocomplete="isLoginMode ? 'current-password' : 'new-password'" ></v-text-field>Also applies to: 170-179, 180-189
380-383: Close the drawer on logout.function signout() { authStore.signOut() showSnackbar('You have been logged out', 'info') + drawer.value = false }
368-378: Prefer router navigation over full reloads.- window.location.href = `/users/${authStore.getUserId}` + router.push(`/users/${authStore.getUserId}`) ... - window.location.href = `/users/${authStore.getUserId}/groups` + router.push(`/users/${authStore.getUserId}/groups`) ... - window.location.href = `/users/${authStore.getUserId}/notifications` + router.push(`/users/${authStore.getUserId}/notifications`)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (15)
package.json(1 hunks)src-tauri/Cargo.toml(1 hunks)src-tauri/capabilities/default.json(1 hunks)src-tauri/src/lib.rs(1 hunks)src-tauri/tauri.conf.json(2 hunks)src/components/Navbar/User/User.scss(1 hunks)src/components/Navbar/User/UserMenu.vue(1 hunks)src/store/authStore.ts(2 hunks)tsconfig.json(1 hunks)v0/src/components/Navbar/User/User.scss(1 hunks)v0/src/components/Navbar/User/UserMenu.vue(1 hunks)v0/src/store/authStore.ts(2 hunks)v1/src/components/Navbar/User/User.scss(1 hunks)v1/src/components/Navbar/User/UserMenu.vue(1 hunks)v1/src/store/authStore.ts(2 hunks)
🔇 Additional comments (5)
package.json (2)
19-19: MDI JS addition looks fineNo concerns adding
@mdi/jsfor icons.
22-22: Keep JS/Rust HTTP plugin versions in syncpackage.json: @tauri-apps/plugin-http = ^2.5.2; src-tauri/Cargo.toml: tauri-plugin-http = "2" — both are major v2 and compatible. No immediate change required; pin matching minor versions only if you depend on features introduced in >=2.5.2.
src-tauri/tauri.conf.json (1)
2-2: Schema path update LGTMLooks correct for Tauri 2 CLI schema. No action needed.
src-tauri/src/lib.rs (1)
7-7: Initialize HTTP plugin: verify capability covers only required endpointsThe init is correct. Double‑check the
src-tauri/capabilities/default.jsonincludeshttp:defaultwith a tight allow‑list (login/signup) so requests are actually permitted but limited. (v2.tauri.app)v1/src/store/authStore.ts (1)
58-65: Clear persisted auth token on sign-out; don't force locale reset.signOut() in v1/src/store/authStore.ts currently sets locale = 'en' and does not remove any persisted auth token — if the JWT is persisted, remove it here (e.g. localStorage.removeItem('') or call the project's token-removal utility) and stop overriding locale unless you intentionally want to revert to a guest default.
Location: v1/src/store/authStore.ts — signOut().
| const data = await response.json() | ||
| console.log('Auth successful:', data) | ||
|
|
||
| if (!data.token) { | ||
| throw new Error('No token received from server') | ||
| } | ||
|
|
There was a problem hiding this comment.
Remove sensitive console logs and harden error handling.
Never log auth payloads/tokens; normalize error message extraction.
- const data = await response.json()
- console.log('Auth successful:', data)
+ const data = await response.json()
...
- } catch (error) {
- console.error('Authentication error:', error)
- showSnackbar(`Authentication failed: ${error.message}`, 'error')
+ } catch (error: unknown) {
+ console.error('Authentication error')
+ const message = error instanceof Error ? error.message : String(error)
+ showSnackbar(`Authentication failed: ${message}`, 'error')Also applies to: 334-335
|
@aryanndwi123 @tachyons I have added the video showing the working of the auth model |
| this.signOut() | ||
| return | ||
| } | ||
| }, |
There was a problem hiding this comment.
You can use jwt package to verify this
|
there are improvements we can make here, but I'm merging this for now to enable community contributions |
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 Fix all issues with AI Agents
In @src/components/Navbar/User/UserMenu.vue:
- Around line 291-306: The API base URL is hard-coded to http://localhost:4000;
update the construction of url inside the login/signup flow (the url const that
depends on isLoginMode.value) to use a configurable environment variable (e.g.
import.meta.env.VITE_API_BASE or process.env.API_BASE) with a sensible default,
then append the path '/api/v1/auth/login' or '/api/v1/auth/signup' to that base;
ensure the fetch call that currently uses url, headers and body remains
unchanged but now targets the environment-driven base so production/HTTPS
environments are supported.
- Around line 24-33: The v-list-item is passing the literal 'default' string
into :prepend-avatar which makes Vuetify try to load it as an image; change the
binding to only provide the avatar URL when authStore.getUserAvatar !==
'default' (e.g. :prepend-avatar="authStore.getUserAvatar !== 'default' ?
authStore.getUserAvatar : undefined") and keep the prepend-icon conditional
(authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' :
undefined) so the broken image is avoided and the fallback icon is shown.
- Around line 320-321: Remove the sensitive console.log call that prints the
full auth response; do not log the result of response.json() (the data variable)
as it may contain tokens. Instead, either remove the console.log entirely or
replace it with a non-sensitive success indicator (e.g., log a static "Auth
successful" message or only safe fields such as user id/username after explicit
filtering). Update the code around the response.json() usage (the data variable
and the console.log('Auth successful:', data) line) to avoid exposing auth
payloads.
- Around line 333-335: The catch block in UserMenu.vue accesses error.message
directly even though `error` is `unknown`; update the catch to type-guard the
error before using `.message` (e.g., check `error instanceof Error` or use a
utility like `isError`) and pass a safe string to showSnackbar and
console.error; specifically modify the catch around the authentication code that
currently calls console.error('Authentication error:', error) and
showSnackbar(`Authentication failed: ${error.message}`, 'error') to log the raw
value and derive a message via the type guard (fallback to String(error) or a
default message) before calling showSnackbar.
In @v0/src/components/Navbar/User/UserMenu.vue:
- Around line 291-306: The fetch uses a hard-coded 'http://localhost:4000' which
will break production; change the login/signup URL construction in the
UserMenu.vue block that uses isLoginMode to build the request so it reads the
API base from an environment variable (e.g., import.meta.env.VITE_API_BASE_URL
or process.env.VUE_APP_API_URL) and concatenate the path '/api/v1/auth/login' or
'/api/v1/auth/signup' accordingly; ensure you handle missing trailing slash and
provide a sensible fallback or throw a clear error so both the URL variable and
the fetch using isLoginMode, body, and response remain functional in all
environments.
- Around line 320-321: Remove the sensitive console.log call that prints the
full auth response: delete or replace "console.log('Auth successful:', data)" in
the auth handler so you do not output tokens or credentials; if you need debug
info, log only non-sensitive fields from the parsed response (e.g., user id or
status) or use a secure logger that redacts tokens before logging within the
function that calls response.json().
In @v0/src/store/authStore.ts:
- Around line 70-77: The signOut method currently resets in-memory fields but
doesn't clear the persisted token; update the signOut function to also remove
the stored token by calling localStorage.removeItem('cv_token') (matching the
key used in setToken) so no stale token remains after logout. Ensure this change
is applied inside the same signOut method in authStore.ts alongside clearing
isLoggedIn, userId, username, userAvatar, locale, and isAdmin.
In @v1/src/components/Navbar/User/UserMenu.vue:
- Around line 320-321: Remove the sensitive console.log call that prints the
full auth response; locate the console.log('Auth successful:', data) after the
await response.json() and delete it or replace it with a safe log that only
outputs non-sensitive status (e.g., data.user?.id or a boolean success flag). If
you must log parts of data, explicitly redact tokens/credentials (e.g., remove
or mask data.token / data.access_token) before logging to ensure no auth tokens
are emitted to the console.
- Around line 24-33: The v-list-item currently passes authStore.getUserAvatar
directly to :prepend-avatar which causes a broken image when the value is the
literal string 'default'; change the binding so :prepend-avatar only receives a
real avatar URL (e.g., set it to undefined when authStore.getUserAvatar ===
'default') and keep the existing :prepend-icon fallback logic that checks
authStore.getUserAvatar === 'default'; update the template bindings around the
v-list-item (:prepend-avatar, :prepend-icon, authStore.getUserAvatar) to
implement this conditional so the default case uses the icon instead of
rendering a broken image.
- Around line 333-335: The catch block in UserMenu.vue accesses error.message
directly which can throw if error is not an Error; update the catch to
type-guard the caught value (e.g., use "error instanceof Error" or check typeof
error === 'object' && 'message' in error) and pass a safe string to showSnackbar
(fallback to String(error) or a generic message) and log accordingly; apply the
same pattern to all similar catch handlers that call showSnackbar with
error.message.
In @v1/src/store/authStore.ts:
- Around line 70-77: Update the signOut method in authStore.ts to also remove
the persisted auth token from localStorage: call localStorage.removeItem(...)
using the same storage key the app uses for the token (or reference the existing
TOKEN_STORAGE_KEY / get/set token utility if one exists) before resetting
isLoggedIn/userId/username/etc., so signing out clears the stored token.
🧹 Nitpick comments (2)
v0/src/store/authStore.ts (1)
33-58: JWT handling implementation looks correct.The base64url decoding and expiration validation are properly implemented. Minor nit: Line 49 has inconsistent indentation (extra leading space before
this.userId).🔎 Fix indentation
this.isLoggedIn = true - this.userId = payload.user_id ?? payload.sub ?? '' + this.userId = payload.user_id ?? payload.sub ?? '' this.username = payload.username ?? 'Guest'v1/src/store/authStore.ts (1)
33-58: JWT handling implementation looks correct.Same as v0 variant - base64url decoding and expiration validation are properly implemented. Same minor indentation issue on Line 49.
🔎 Fix indentation
this.isLoggedIn = true - this.userId = payload.user_id ?? payload.sub ?? '' + this.userId = payload.user_id ?? payload.sub ?? '' this.username = payload.username ?? 'Guest'
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonsrc-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (6)
src/components/Navbar/User/UserMenu.vuesrc/store/authStore.tsv0/src/components/Navbar/User/UserMenu.vuev0/src/store/authStore.tsv1/src/components/Navbar/User/UserMenu.vuev1/src/store/authStore.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/store/authStore.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-30T10:11:52.399Z
Learnt from: senutpal
Repo: CircuitVerse/cv-frontend-vue PR: 706
File: .github/workflows/tauri-release.yml:51-51
Timestamp: 2025-12-30T10:11:52.399Z
Learning: In cv-frontend-vue repository, the tauri-action's `projectPath` should be set to `.` (root) because the `beforeBuildCommand` needs to run from the root directory where `package.json` and `dist/` are located. The Tauri CLI automatically locates `src-tauri/tauri.conf.json` from the project root.
Applied to files:
src/components/Navbar/User/UserMenu.vue
| <v-list-item | ||
| class="list-item-avatar" | ||
| :prepend-avatar="authStore.getUserAvatar" | ||
| :prepend-icon=" | ||
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | ||
| " | ||
| :title="authStore.getUsername" | ||
| lines="two" | ||
| color="white" | ||
| ></v-list-item> |
There was a problem hiding this comment.
Passing 'default' to :prepend-avatar renders a broken image.
When getUserAvatar is 'default', the prepend-avatar prop receives the literal string 'default' which Vuetify will try to load as an image URL, resulting in a broken image alongside the fallback icon.
🔎 Proposed fix
<v-list-item
class="list-item-avatar"
- :prepend-avatar="authStore.getUserAvatar"
+ :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined"
:prepend-icon="
authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined
"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <v-list-item | |
| class="list-item-avatar" | |
| :prepend-avatar="authStore.getUserAvatar" | |
| :prepend-icon=" | |
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | |
| " | |
| :title="authStore.getUsername" | |
| lines="two" | |
| color="white" | |
| ></v-list-item> | |
| <v-list-item | |
| class="list-item-avatar" | |
| :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined" | |
| :prepend-icon=" | |
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | |
| " | |
| :title="authStore.getUsername" | |
| lines="two" | |
| color="white" | |
| ></v-list-item> |
🤖 Prompt for AI Agents
In @src/components/Navbar/User/UserMenu.vue around lines 24 - 33, The
v-list-item is passing the literal 'default' string into :prepend-avatar which
makes Vuetify try to load it as an image; change the binding to only provide the
avatar URL when authStore.getUserAvatar !== 'default' (e.g.
:prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar
: undefined") and keep the prepend-icon conditional (authStore.getUserAvatar ===
'default' ? 'mdi-account-circle-outline' : undefined) so the broken image is
avoided and the fallback icon is shown.
| const url = isLoginMode.value | ||
| ? 'http://localhost:4000/api/v1/auth/login' | ||
| : 'http://localhost:4000/api/v1/auth/signup' | ||
|
|
||
| const body = isLoginMode.value | ||
| ? { email: email.value, password: password.value } | ||
| : { email: email.value, password: password.value, name: name.value } | ||
|
|
||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Accept': 'application/json', | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(body) | ||
| }) |
There was a problem hiding this comment.
Hard-coded localhost:4000 breaks production and HTTPS environments.
This will fail in production. Use an environment variable for the API base URL.
🔎 Proposed fix
- const url = isLoginMode.value
- ? 'http://localhost:4000/api/v1/auth/login'
- : 'http://localhost:4000/api/v1/auth/signup'
+ const API_BASE = import.meta.env.VITE_API_BASE_URL ?? 'https://circuitverse.org'
+ const url = `${API_BASE}/api/v1/auth/${isLoginMode.value ? 'login' : 'signup'}`📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const url = isLoginMode.value | |
| ? 'http://localhost:4000/api/v1/auth/login' | |
| : 'http://localhost:4000/api/v1/auth/signup' | |
| const body = isLoginMode.value | |
| ? { email: email.value, password: password.value } | |
| : { email: email.value, password: password.value, name: name.value } | |
| const response = await fetch(url, { | |
| method: 'POST', | |
| headers: { | |
| 'Accept': 'application/json', | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(body) | |
| }) | |
| const API_BASE = import.meta.env.VITE_API_BASE_URL ?? 'https://circuitverse.org' | |
| const url = `${API_BASE}/api/v1/auth/${isLoginMode.value ? 'login' : 'signup'}` | |
| const body = isLoginMode.value | |
| ? { email: email.value, password: password.value } | |
| : { email: email.value, password: password.value, name: name.value } | |
| const response = await fetch(url, { | |
| method: 'POST', | |
| headers: { | |
| 'Accept': 'application/json', | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(body) | |
| }) |
🤖 Prompt for AI Agents
In @src/components/Navbar/User/UserMenu.vue around lines 291 - 306, The API base
URL is hard-coded to http://localhost:4000; update the construction of url
inside the login/signup flow (the url const that depends on isLoginMode.value)
to use a configurable environment variable (e.g. import.meta.env.VITE_API_BASE
or process.env.API_BASE) with a sensible default, then append the path
'/api/v1/auth/login' or '/api/v1/auth/signup' to that base; ensure the fetch
call that currently uses url, headers and body remains unchanged but now targets
the environment-driven base so production/HTTPS environments are supported.
| const data = await response.json() | ||
| console.log('Auth successful:', data) |
There was a problem hiding this comment.
Remove sensitive console log that may expose auth tokens.
Logging the full data response object could expose tokens or other sensitive auth payload data in browser dev tools or log aggregators.
🔎 Proposed fix
const data = await response.json()
- console.log('Auth successful:', data)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const data = await response.json() | |
| console.log('Auth successful:', data) | |
| const data = await response.json() |
🤖 Prompt for AI Agents
In @src/components/Navbar/User/UserMenu.vue around lines 320 - 321, Remove the
sensitive console.log call that prints the full auth response; do not log the
result of response.json() (the data variable) as it may contain tokens. Instead,
either remove the console.log entirely or replace it with a non-sensitive
success indicator (e.g., log a static "Auth successful" message or only safe
fields such as user id/username after explicit filtering). Update the code
around the response.json() usage (the data variable and the console.log('Auth
successful:', data) line) to avoid exposing auth payloads.
| } catch (error) { | ||
| console.error('Authentication error:', error) | ||
| showSnackbar(`Authentication failed: ${error.message}`, 'error') |
There was a problem hiding this comment.
Type-guard the error before accessing .message.
error is of type unknown in catch blocks. Accessing .message directly will fail TypeScript strict checks.
🔎 Proposed fix
} catch (error) {
- console.error('Authentication error:', error)
- showSnackbar(`Authentication failed: ${error.message}`, 'error')
+ console.error('Authentication error')
+ const message = error instanceof Error ? error.message : String(error)
+ showSnackbar(`Authentication failed: ${message}`, 'error')
} finally {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| console.error('Authentication error:', error) | |
| showSnackbar(`Authentication failed: ${error.message}`, 'error') | |
| } catch (error) { | |
| console.error('Authentication error') | |
| const message = error instanceof Error ? error.message : String(error) | |
| showSnackbar(`Authentication failed: ${message}`, 'error') |
🤖 Prompt for AI Agents
In @src/components/Navbar/User/UserMenu.vue around lines 333 - 335, The catch
block in UserMenu.vue accesses error.message directly even though `error` is
`unknown`; update the catch to type-guard the error before using `.message`
(e.g., check `error instanceof Error` or use a utility like `isError`) and pass
a safe string to showSnackbar and console.error; specifically modify the catch
around the authentication code that currently calls
console.error('Authentication error:', error) and showSnackbar(`Authentication
failed: ${error.message}`, 'error') to log the raw value and derive a message
via the type guard (fallback to String(error) or a default message) before
calling showSnackbar.
| <v-list-item | ||
| class="list-item-avatar" | ||
| :prepend-avatar="authStore.getUserAvatar" | ||
| :prepend-icon=" | ||
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | ||
| " | ||
| :title="authStore.getUsername" | ||
| lines="two" | ||
| color="white" | ||
| ></v-list-item> |
There was a problem hiding this comment.
Passing 'default' to :prepend-avatar renders a broken image.
Same issue as root variant.
🔎 Proposed fix
<v-list-item
class="list-item-avatar"
- :prepend-avatar="authStore.getUserAvatar"
+ :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined"
:prepend-icon="
authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined
"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <v-list-item | |
| class="list-item-avatar" | |
| :prepend-avatar="authStore.getUserAvatar" | |
| :prepend-icon=" | |
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | |
| " | |
| :title="authStore.getUsername" | |
| lines="two" | |
| color="white" | |
| ></v-list-item> | |
| <v-list-item | |
| class="list-item-avatar" | |
| :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined" | |
| :prepend-icon=" | |
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | |
| " | |
| :title="authStore.getUsername" | |
| lines="two" | |
| color="white" | |
| ></v-list-item> |
| <v-list-item | ||
| class="list-item-avatar" | ||
| :prepend-avatar="authStore.getUserAvatar" | ||
| :prepend-icon=" | ||
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | ||
| " | ||
| :title="authStore.getUsername" | ||
| lines="two" | ||
| color="white" | ||
| ></v-list-item> |
There was a problem hiding this comment.
Passing 'default' to :prepend-avatar renders a broken image.
Same issue across all UserMenu.vue variants.
🔎 Proposed fix
<v-list-item
class="list-item-avatar"
- :prepend-avatar="authStore.getUserAvatar"
+ :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined"
:prepend-icon="
authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined
"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <v-list-item | |
| class="list-item-avatar" | |
| :prepend-avatar="authStore.getUserAvatar" | |
| :prepend-icon=" | |
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | |
| " | |
| :title="authStore.getUsername" | |
| lines="two" | |
| color="white" | |
| ></v-list-item> | |
| <v-list-item | |
| class="list-item-avatar" | |
| :prepend-avatar="authStore.getUserAvatar !== 'default' ? authStore.getUserAvatar : undefined" | |
| :prepend-icon=" | |
| authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined | |
| " | |
| :title="authStore.getUsername" | |
| lines="two" | |
| color="white" | |
| ></v-list-item> |
🤖 Prompt for AI Agents
In @v1/src/components/Navbar/User/UserMenu.vue around lines 24 - 33, The
v-list-item currently passes authStore.getUserAvatar directly to :prepend-avatar
which causes a broken image when the value is the literal string 'default';
change the binding so :prepend-avatar only receives a real avatar URL (e.g., set
it to undefined when authStore.getUserAvatar === 'default') and keep the
existing :prepend-icon fallback logic that checks authStore.getUserAvatar ===
'default'; update the template bindings around the v-list-item (:prepend-avatar,
:prepend-icon, authStore.getUserAvatar) to implement this conditional so the
default case uses the icon instead of rendering a broken image.
| const url = isLoginMode.value | ||
| ? 'http://localhost:4000/api/v1/auth/login' | ||
| : 'http://localhost:4000/api/v1/auth/signup' | ||
|
|
||
| const body = isLoginMode.value | ||
| ? { email: email.value, password: password.value } | ||
| : { email: email.value, password: password.value, name: name.value } | ||
|
|
||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Accept': 'application/json', | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(body) | ||
| }) |
There was a problem hiding this comment.
Hard-coded localhost:4000 breaks production.
Same issue across all UserMenu.vue variants - use environment variable.
| const data = await response.json() | ||
| console.log('Auth successful:', data) |
There was a problem hiding this comment.
Remove sensitive console log.
Same issue across all variants - may expose auth tokens.
🤖 Prompt for AI Agents
In @v1/src/components/Navbar/User/UserMenu.vue around lines 320 - 321, Remove
the sensitive console.log call that prints the full auth response; locate the
console.log('Auth successful:', data) after the await response.json() and delete
it or replace it with a safe log that only outputs non-sensitive status (e.g.,
data.user?.id or a boolean success flag). If you must log parts of data,
explicitly redact tokens/credentials (e.g., remove or mask data.token /
data.access_token) before logging to ensure no auth tokens are emitted to the
console.
| } catch (error) { | ||
| console.error('Authentication error:', error) | ||
| showSnackbar(`Authentication failed: ${error.message}`, 'error') |
There was a problem hiding this comment.
Type-guard the error before accessing .message.
Same fix needed across all variants.
🔎 Proposed fix
} catch (error) {
- console.error('Authentication error:', error)
- showSnackbar(`Authentication failed: ${error.message}`, 'error')
+ console.error('Authentication error')
+ const message = error instanceof Error ? error.message : String(error)
+ showSnackbar(`Authentication failed: ${message}`, 'error')
} finally {🤖 Prompt for AI Agents
In @v1/src/components/Navbar/User/UserMenu.vue around lines 333 - 335, The catch
block in UserMenu.vue accesses error.message directly which can throw if error
is not an Error; update the catch to type-guard the caught value (e.g., use
"error instanceof Error" or check typeof error === 'object' && 'message' in
error) and pass a safe string to showSnackbar (fallback to String(error) or a
generic message) and log accordingly; apply the same pattern to all similar
catch handlers that call showSnackbar with error.message.
| signOut(): void { | ||
| this.isLoggedIn = false | ||
| this.userId = '' | ||
| this.username = 'Guest' | ||
| this.userAvatar = 'default' | ||
| this.locale = 'en' | ||
| this.isAdmin = false | ||
| }, |
There was a problem hiding this comment.
signOut should clear persisted token from localStorage.
Same issue as v0 variant - persisted token not cleared on sign-out.
🔎 Proposed fix
signOut(): void {
this.isLoggedIn = false
this.userId = ''
this.username = 'Guest'
this.userAvatar = 'default'
this.locale = 'en'
this.isAdmin = false
+ try { if (typeof localStorage !== 'undefined') localStorage.removeItem('cv_token') } catch {}
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| signOut(): void { | |
| this.isLoggedIn = false | |
| this.userId = '' | |
| this.username = 'Guest' | |
| this.userAvatar = 'default' | |
| this.locale = 'en' | |
| this.isAdmin = false | |
| }, | |
| signOut(): void { | |
| this.isLoggedIn = false | |
| this.userId = '' | |
| this.username = 'Guest' | |
| this.userAvatar = 'default' | |
| this.locale = 'en' | |
| this.isAdmin = false | |
| try { if (typeof localStorage !== 'undefined') localStorage.removeItem('cv_token') } catch {} | |
| }, |
🤖 Prompt for AI Agents
In @v1/src/store/authStore.ts around lines 70 - 77, Update the signOut method in
authStore.ts to also remove the persisted auth token from localStorage: call
localStorage.removeItem(...) using the same storage key the app uses for the
token (or reference the existing TOKEN_STORAGE_KEY / get/set token utility if
one exists) before resetting isLoggedIn/userId/username/etc., so signing out
clears the stored token.
|
Yeah sure we ll do that. We also need to add authentication through google etc too. I ll raise an issue for the same |

Implemented the Authentication model for the Desktop and the Vue simulator
Fixes #557
Fixes #569
Describe the changes you have made in this PR -
Video of the working Authentication model
Authentication-2.mp4
Summary by CodeRabbit
New Features
Style
Chores
✏️ Tip: You can customize this high-level summary in your review settings.