Documentation Index
Fetch the complete documentation index at: https://docs-mstore.faisalaffan.com/llms.txt
Use this file to discover all available pages before exploring further.
Authentication Flow
Dokumentasi lengkap tentang alur autentikasi di MStore Dashboard, termasuk login, token refresh, dan logout.
Overview
MStore Dashboard menggunakan JWT authentication dengan strategi keamanan tinggi:
- Access Token: Disimpan di memory (Pinia store)
- Refresh Token: HttpOnly cookie (set oleh backend)
Login Flow
Login Implementation
// features/01_core/composables/useAuth.ts
export const useAuth = () => {
const store = useAuthStore()
const router = useRouter()
const route = useRoute()
const login = async (credentials: LoginCredentials) => {
store.isLoading = true
try {
const response = await $fetch<LoginResponse>('/api/v1/auth/login', {
method: 'POST',
body: credentials,
credentials: 'include', // Important for cookies
})
// Store access token in memory
store.setToken(response.access_token)
store.setUser(response.user)
// Redirect to intended page or dashboard
const redirectUrl = route.query.redirect as string
router.push(redirectUrl || '/')
return response
} catch (error) {
throw error
} finally {
store.isLoading = false
}
}
return { login }
}
Token Refresh Flow
Token Refresh Implementation
// plugins/auth.client.ts
export default defineNuxtPlugin(() => {
const authStore = useAuthStore()
let refreshPromise: Promise<string> | null = null
const apiFetch = $fetch.create({
async onResponseError({ response, options }) {
if (response.status !== 401) throw error
// Prevent refresh loop
if (options.url?.includes('/auth/refresh')) {
authStore.logout()
throw new Error('Session expired')
}
// Deduplicate refresh requests
if (!refreshPromise) {
refreshPromise = refreshToken()
}
try {
const newToken = await refreshPromise
// Retry original request
return $fetch(response.url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${newToken}`,
},
})
} catch {
authStore.logout()
navigateTo('/login')
throw error
} finally {
refreshPromise = null
}
},
})
const refreshToken = async (): Promise<string> => {
const response = await $fetch<{ access_token: string }>(
'/api/v1/auth/refresh-token',
{
method: 'POST',
credentials: 'include',
}
)
authStore.setToken(response.access_token)
return response.access_token
}
return { provide: { api: apiFetch } }
})
Logout Flow
Logout Implementation
// features/01_core/store/auth.ts
export const useAuthStore = defineStore('auth', () => {
const accessToken = ref<string | null>(null)
const user = ref<User | null>(null)
const isAuthenticated = ref(false)
const logout = async () => {
try {
// Notify backend to invalidate refresh token
await $fetch('/api/v1/auth/logout', {
method: 'POST',
credentials: 'include',
})
} catch {
// Ignore errors - proceed with local logout
} finally {
// Clear local state
accessToken.value = null
user.value = null
isAuthenticated.value = false
// Redirect to login
navigateTo('/login')
}
}
return { accessToken, user, isAuthenticated, logout }
})
Route Protection Flow
Route Guard Implementation
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware(async (to) => {
const authStore = useAuthStore()
// Public routes
const publicRoutes = ['/login', '/register', '/forgot-password']
if (publicRoutes.includes(to.path)) {
// Redirect authenticated users away from auth pages
if (authStore.isAuthenticated) {
return navigateTo('/')
}
return
}
// Protected routes - require authentication
if (!authStore.isAuthenticated) {
return navigateTo({
path: '/login',
query: { redirect: to.fullPath },
})
}
// Check token expiration
if (authStore.isTokenExpired) {
try {
await refreshToken()
} catch {
return navigateTo('/login')
}
}
})
Session Recovery Flow
Session Recovery Implementation
// plugins/auth.client.ts
export default defineNuxtPlugin(async () => {
const authStore = useAuthStore()
// Try to recover session on app init
if (authStore.isAuthenticated) {
try {
const response = await $fetch<{ access_token: string; user: User }>(
'/api/v1/auth/refresh-token',
{
method: 'POST',
credentials: 'include',
}
)
authStore.setToken(response.access_token)
if (response.user) {
authStore.setUser(response.user)
}
} catch {
// Session invalid - clear state
authStore.logout()
}
}
})
Complete Auth State Machine
Security Considerations
- Access Token: Memory only (not localStorage)
- Refresh Token: HttpOnly cookie
- Never expose tokens to JavaScript
- Access Token: 15-30 minutes
- Refresh Token: 7-30 days
- Auto-refresh before expiration
- All auth requests over HTTPS
- Secure flag on cookies
- SameSite=Strict atau Lax
- Refresh token dalam HttpOnly cookie
- Origin validation di backend
- SameSite cookie attribute
Error Handling
| Error | User Action | System Action |
|---|
| Invalid credentials | Show error message | Clear form |
| Token expired | None (auto-refresh) | Refresh token |
| Refresh failed | Redirect to login | Clear all tokens |
| Network error | Show retry option | Retry with backoff |
| Account locked | Show support contact | Log attempt |
Next Steps
Core Authentication
Implementasi detail auth
API Interceptors
Auto-refresh implementation