132 lines
3.1 KiB
TypeScript
132 lines
3.1 KiB
TypeScript
/**
|
|
* Authentication composable using BFF (Backend for Frontend) pattern
|
|
* Auth tokens are stored in HTTP-only cookies managed by Nitro server
|
|
* Tenant context is stored in a readable cookie for client-side access
|
|
*/
|
|
export const useAuth = () => {
|
|
const authMessageCookie = useCookie('authMessage')
|
|
const tenantCookie = useCookie('routebox_tenant')
|
|
const router = useRouter()
|
|
|
|
// Reactive user state - populated from /api/auth/me
|
|
const user = useState<any>('auth_user', () => null)
|
|
const isAuthenticated = useState<boolean>('auth_is_authenticated', () => false)
|
|
const isLoading = useState<boolean>('auth_is_loading', () => false)
|
|
|
|
/**
|
|
* Check if user is logged in
|
|
* Uses server-side session validation via /api/auth/me
|
|
*/
|
|
const isLoggedIn = () => {
|
|
return isAuthenticated.value
|
|
}
|
|
|
|
/**
|
|
* Login with email and password
|
|
* Calls the Nitro BFF login endpoint which sets HTTP-only cookies
|
|
*/
|
|
const login = async (email: string, password: string) => {
|
|
isLoading.value = true
|
|
|
|
try {
|
|
const response = await $fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
body: { email, password },
|
|
})
|
|
|
|
if (response.success) {
|
|
user.value = response.user
|
|
isAuthenticated.value = true
|
|
return { success: true, user: response.user }
|
|
}
|
|
|
|
return { success: false, error: 'Login failed' }
|
|
} catch (error: any) {
|
|
const message = error.data?.message || error.message || 'Login failed'
|
|
return { success: false, error: message }
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logout user
|
|
* Calls the Nitro BFF logout endpoint which clears HTTP-only cookies
|
|
*/
|
|
const logout = async () => {
|
|
try {
|
|
await $fetch('/api/auth/logout', {
|
|
method: 'POST',
|
|
})
|
|
} catch (error) {
|
|
console.error('Logout error:', error)
|
|
}
|
|
|
|
// Clear local state
|
|
user.value = null
|
|
isAuthenticated.value = false
|
|
|
|
// Set flash message for login page
|
|
authMessageCookie.value = 'Logged out successfully'
|
|
|
|
// Redirect to login page
|
|
router.push('/login')
|
|
}
|
|
|
|
/**
|
|
* Check current authentication status
|
|
* Validates session with backend via Nitro BFF
|
|
*/
|
|
const checkAuth = async () => {
|
|
isLoading.value = true
|
|
|
|
try {
|
|
const response = await $fetch('/api/auth/me', {
|
|
method: 'GET',
|
|
})
|
|
|
|
if (response.authenticated && response.user) {
|
|
user.value = response.user
|
|
isAuthenticated.value = true
|
|
return true
|
|
}
|
|
} catch (error) {
|
|
// Session invalid or expired
|
|
user.value = null
|
|
isAuthenticated.value = false
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Get current user
|
|
*/
|
|
const getUser = () => {
|
|
return user.value
|
|
}
|
|
|
|
/**
|
|
* Get current tenant ID from cookie
|
|
*/
|
|
const getTenantId = () => {
|
|
return tenantCookie.value
|
|
}
|
|
|
|
return {
|
|
// State
|
|
user,
|
|
isAuthenticated,
|
|
isLoading,
|
|
// Methods
|
|
isLoggedIn,
|
|
login,
|
|
logout,
|
|
checkAuth,
|
|
getUser,
|
|
getTenantId,
|
|
}
|
|
}
|