Overview
The Service Orders Management System uses JWT (JSON Web Token) based authentication to secure access to the application. The authentication system manages user login, token storage, and route protection.
Authentication Flow
User Login
User enters email and password on the login page
API Validation
Credentials are sent to /login endpoint for validation
Token Issuance
Server returns JWT access token if credentials are valid
Token Storage
Token is stored in browser localStorage
Authenticated Access
Token is included in all subsequent API requests
Login Page
The login interface (pages/index.vue) provides a user-friendly authentication form:
< div class = "col-md-6 bg-white rounded-end-4 p-4 d-flex flex-column justify-content-center" >
<h2 class="title fw-bold text-center mb-4 fs-2">BIENVENIDO</h2>
<form action="#" class="w-100 px-3">
<div class="mb-4">
<label for="email" class="form-label fw-bold">Correo Electrónico</label>
<input type="email" class="form-control p-3 shadow" name="email" id="email"
placeholder="Ingrese su email" v-model="form.email" @input="validateEmail()" />
</ div >
< div class = "mb-4 position-relative" >
<label for="password" class="form-label fw-bold">Contraseña</label>
<div class="input-group shadow rounded">
<input id="password" type="password" class="form-control border-end-0 p-3" name="password"
placeholder="Ingrese su contraseña" required v-model="form.password"
@input="validatePassword()" />
<span class="input-group-text bg-transparent border border-start-0 pe-3"
@click="toggleInputPassword">
<i class="fas fa-eye"></i>
</span>
</ div >
< / div >
<div class="d-grid mt-4">
<button type="submit" class="btn p-3 text-white fw-bold" @click.prevent="checkLogin">Acceder</button>
</ div >
< / form >
</div>
Email Validation Real-time email validation ensures proper email format before submission.
Password Toggle Users can toggle password visibility for easier input verification.
Responsive Design Login form adapts to different screen sizes with Bootstrap grid system.
Visual Feedback Shadow effects and hover states provide clear interactive feedback.
The system validates login credentials before submission:
const form = ref ({
email: '' ,
password: ''
})
const formFlag = ref ( true )
const validateEmail = () => {
if ( ! form . value . email ) {
alert ( "El email no puede estar vacío" )
formFlag . value = false
return false
}
formFlag . value = true
return true
}
const validatePassword = () => {
if ( ! form . value . password ) {
alert ( "La contraseña no puede estar vacía" )
formFlag . value = false
return false
}
formFlag . value = true
return true
}
const validateForm = () => {
if ( ! validateEmail ()) {
return
}
if ( ! validatePassword ()) {
return
}
formFlag . value = true
}
Validation occurs both on input (real-time) and before form submission to ensure data integrity.
Login Authentication
The login process authenticates users and stores the access token:
const checkLogin = async () => {
validateForm ()
if ( ! formFlag . value ) {
return
}
await api . post ( '/login' , form . value ). then (
( response ) => {
localStorage . setItem ( 'access_token' , response . data . access_token )
window . location . href = '/serviceorders'
}). catch (
( error ) => {
if ( error . response . data . message ) {
alert ( `Validacion Incorrecta: ${ error . response . data . message } ` )
}
else {
alert ( `Validacion Incorrecta` )
}
form . value . email = ""
form . value . password = ""
})
}
Login Process Breakdown
The form is validated to ensure email and password are provided before making the API call.
Credentials are sent via POST request to /login endpoint using the API composable.
On successful authentication:
Extract access_token from response
Store token in localStorage
Redirect user to /serviceorders page
On authentication failure:
Display error message from server or generic message
Clear email and password fields
Allow user to retry login
Token Storage
JWT tokens are stored in browser localStorage:
localStorage . setItem ( 'access_token' , response . data . access_token )
Storing tokens in localStorage makes them accessible to JavaScript on the page. Ensure your application is protected against XSS (Cross-Site Scripting) attacks.
Token Persistence
Tokens in localStorage:
✅ Persist across browser sessions
✅ Survive page refreshes
✅ Available to all tabs/windows from the same origin
❌ Vulnerable to XSS if application has security flaws
Auth Store (Pinia)
The authentication state is managed using Pinia store:
import { defineStore } from 'pinia'
export const useAuthStore = defineStore ( 'auth' , {
state : () => ({
user: null ,
token: null as string | null
}),
actions: {
setUser ( userData : any , token : string ) {
this . user = userData
this . token = token
localStorage . setItem ( 'access_token' , token )
},
logout () {
this . user = null
this . token = null
localStorage . removeItem ( 'access_token' )
},
initializeAuth () {
const storedToken = localStorage . getItem ( 'access_token' )
if ( storedToken ) {
this . token = storedToken
}
}
}
})
Auth Store Actions
Sets the authenticated user and token in both the store and localStorage. Parameters:
userData: User information object
token: JWT access token string
Clears user session by removing user data and token from both store and localStorage.
Restores authentication state from localStorage on app initialization. Called when app mounts to maintain session across page refreshes.
API Authentication
The useApi composable automatically includes authentication tokens in API requests:
import axios from 'axios'
export const useApi = () => {
const config = useRuntimeConfig ()
const token = import . meta . client ? localStorage . getItem ( 'access_token' ) : null
const { public : { apiBaseUrl } } = useRuntimeConfig ()
const api = axios . create ({
baseURL: apiBaseUrl as string ,
headers: {
'Accept' : 'application/json' ,
'Content-Type' : 'application/json' ,
... ( token ? { Authorization: `Bearer ${ token } ` } : {})
}
})
return api
}
How It Works
Token Retrieval
Retrieves access token from localStorage (only on client-side)
Axios Instance Creation
Creates an Axios instance with baseURL from runtime config
Header Injection
If token exists, adds Authorization: Bearer <token> header to all requests
Request Execution
All API calls using this composable are automatically authenticated
The import.meta.client check ensures token retrieval only happens in the browser, preventing SSR errors when localStorage is not available.
Protected Routes
Routes are protected using Nuxt middleware:
import { useAuthStore } from "~/stores/auth"
export default defineNuxtRouteMiddleware (( to , from ) => {
const auth = useAuthStore ()
if ( ! auth . token ) {
return navigateTo ( '/' )
}
} )
Applying Middleware to Pages
To protect a page, add the middleware to page meta:
pages/serviceorders/index.vue
// definePageMeta({
// middleware: 'auth' //Middleware para proteger esta ruta
// })
In the current codebase, the middleware is commented out on the service orders page. Uncomment this to enable route protection.
How Route Protection Works
Session Initialization
The auth store is initialized when the app mounts:
onMounted (() => {
const auth = useAuthStore ()
auth . initializeAuth ()
})
This ensures that users with valid tokens in localStorage remain authenticated across page refreshes.
Logout Implementation
The application implements logout functionality in the default layout header:
const api = useApi ()
const logout = async () => {
await api . post ( '/logout' )
localStorage . removeItem ( 'access_token' )
window . location . href = '/'
}
Logout process:
Calls the /logout API endpoint to invalidate the session on the server
Removes the JWT token from localStorage
Redirects the user to the login page
The logout button is accessible from the user dropdown menu in the application header.
You can also use the auth store’s logout action for programmatic logout:
import { useAuthStore } from '~/stores/auth'
const handleLogout = () => {
const auth = useAuthStore ()
auth . logout ()
navigateTo ( '/' )
}
Security Best Practices
HTTPS Only Always use HTTPS in production to prevent token interception during transmission.
Token Expiration Implement token expiration and refresh mechanisms to limit exposure window.
XSS Protection Sanitize all user inputs and use Content Security Policy headers to prevent XSS attacks.
CSRF Protection Implement CSRF tokens for state-changing operations beyond just authentication.
Common Authentication Errors
Error : “Validacion Incorrecta”Solution : Verify email and password are correct. Check if account exists and is active.
Error : API returns 401 UnauthorizedSolution : Implement token refresh logic or redirect user to login page to obtain new token.
Error : Connection timeout or network unreachableSolution : Check API baseURL configuration and network connectivity.
Error : CORS policy blocked requestSolution : Configure backend to allow requests from your frontend domain.
Example: Complete Authentication Flow
// 1. User submits login form
const loginData = {
email: '[email protected] ' ,
password: 'securePassword123'
}
// 2. API validates credentials and returns token
const response = await api . post ( '/login' , loginData )
// Response: { access_token: 'eyJhbGciOiJIUzI1NiIs...' }
// 3. Token stored in localStorage
localStorage . setItem ( 'access_token' , response . data . access_token )
// 4. User redirected to service orders
window . location . href = '/serviceorders'
// 5. Subsequent API calls include token
const api = useApi () // Automatically includes Bearer token
const orders = await api . get ( '/service-orders' )
// Request headers: { Authorization: 'Bearer eyJhbGciOiJIUzI1NiIs...' }
For improved user experience, consider implementing:
“Remember me” functionality
Automatic token refresh before expiration
Session timeout warnings
Multi-factor authentication for sensitive operations