Documentation

Authentication

How user data and auth state are managed with Pinia — and how to connect a real backend.

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:

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

PageWhat happens
sign-in.vueValidates email + password format, saves email to useUserStore only if "Remember me" is checked, navigates to Home
sign-up.vueValidates all fields, saves name and email to useUserStore, navigates to the phone verification screen
verify-your-phone-number.vueconfirmation-code.vuePhone OTP step, then navigates to the account created screen
edit-profile.vueUpdates name, email, phone, location, birthday, and photo directly in useUserStore
profile.vue "Sign Out" menu itemLinks straight to sign-in.vuedoes not clear the store

Persistence Plugin

The plugin is registered in app/plugins/pinia-persistedstate.client.ts:

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:

app/stores/useUserStore.ts
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:

app/pages/sign-in.vue
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:

app/pages/profile.vue
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:

app/middleware/auth.ts
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' })
Once you set up the 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.