Authentication
The template ships with mock authentication — all user data is stored locally in a Pinia store and persisted to localStorage. There is no real server, no JWT, and no session. This section explains how it works and how to replace it with a real backend.
How It Works Now
User Store
User data lives in app/stores/useUserStore.ts:
export const useUserStore = defineStore(
'user',
() => {
const name = ref('Cristina Watson');
const email = ref('cristinawatson@mail.com');
const phone = ref('+1 123 456 7890');
const location = ref('New York, USA');
const birthday = ref('01/01/2000');
const photo = ref('https://george-fx.github.io/teofin-data/photos/01.jpg');
return { name, email, phone, location, birthday, photo };
},
{ persist: true },
);
The persist: true option tells pinia-plugin-persistedstate to automatically save the store to localStorage and restore it on next visit.
Where Data Changes
| Page | What happens |
|---|---|
sign-in.vue | Validates email + password format, saves email to useUserStore only if "Remember me" is checked, navigates to Home |
sign-up.vue | Validates all fields, saves name and email to useUserStore, navigates to the phone verification screen |
verify-your-phone-number.vue → confirmation-code.vue | Phone OTP step, then navigates to the account created screen |
edit-profile.vue | Updates name, email, phone, location, birthday, and photo directly in useUserStore |
profile.vue "Sign Out" menu item | Links straight to sign-in.vue — does not clear the store |
Persistence Plugin
The plugin is registered in app/plugins/pinia-persistedstate.client.ts:
import { createPersistedState } from 'pinia-plugin-persistedstate'
import type { Pinia } from 'pinia'
export default defineNuxtPlugin(({ $pinia }) => {
($pinia as Pinia).use(createPersistedState())
})
The .client.ts suffix means the plugin only runs in the browser — store data is never read from localStorage during SSR.
How to Update Default User Data
To change the initial values shown in the app, edit the ref() defaults in useUserStore:
const name = ref('Your App Name');
const email = ref('user@example.com');
const phone = ref('+10000000000');
const location = ref('New York, USA');
const birthday = ref('01/01/2000');
const photo = ref('/images/default-avatar.png');
Connecting a Real Backend
To replace mock auth with a real API:
Replace sign-in logic
Instead of saving directly to the store, call your auth endpoint and store the returned token in a cookie:
const handleSignIn = async () => {
const { data } = await useFetch('/api/auth/login', {
method: 'POST',
body: { email: email.value, password: password.value },
})
const token = useCookie('auth_token')
token.value = data.value.token
userStore.name = data.value.user.name
userStore.email = data.value.user.email
navigateTo(ROUTES.HOME)
}
Add a real logout
The "Sign Out" menu item in profile.vue currently just links to ROUTES.SIGN_IN. Replace it with a handler that clears the store and removes the token:
const handleSignOut = () => {
const token = useCookie('auth_token')
token.value = null
userStore.$reset()
navigateTo(ROUTES.SIGN_IN)
}
Protect routes with middleware
Create app/middleware/auth.ts to redirect unauthenticated users:
export default defineNuxtRouteMiddleware(() => {
const token = useCookie('auth_token')
if (!token.value) {
return navigateTo(ROUTES.SIGN_IN)
}
})
Then apply it to any protected page:
definePageMeta({ middleware: 'auth' })
useApi composable from the API Usage section, read the same auth_token cookie there so it's automatically attached as a Bearer token to every API request.