feat(FE) : start integrate api
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
// composables/useAuth.ts - Enhanced version with better error handling
|
||||
import type { User, SessionResponse, LoginResponse, LogoutResponse } from '~/types/auth'
|
||||
|
||||
export const useAuth = () => {
|
||||
const user = ref<User | null>(null)
|
||||
const accessToken = ref<string | null>(null)
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const isAuthenticated = computed(() => !!user.value)
|
||||
|
||||
const clearError = () => {
|
||||
error.value = null
|
||||
}
|
||||
|
||||
const checkAuth = async (): Promise<User | null> => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
clearError()
|
||||
|
||||
// The session API returns SessionResponse, or throws 401 if not authenticated
|
||||
// $fetch automatically sends cookies for same-origin requests
|
||||
const response = await $fetch<SessionResponse>('/api/auth/session', {
|
||||
credentials: 'include' // Explicitly include cookies (though $fetch does this by default)
|
||||
})
|
||||
|
||||
// Handle response structure - session API returns { success, user, ... }
|
||||
if (response && response.user) {
|
||||
user.value = response.user
|
||||
accessToken.value = response.accessToken || null
|
||||
return response.user
|
||||
}
|
||||
|
||||
// If response exists but no user, clear and return null
|
||||
user.value = null
|
||||
accessToken.value = null
|
||||
return null
|
||||
} catch (fetchError: any) {
|
||||
console.error('Session check failed:', fetchError)
|
||||
// 401 errors are expected when not authenticated
|
||||
if (fetchError.statusCode === 401 || fetchError.status === 401) {
|
||||
error.value = null // Don't show error for expected unauthenticated state
|
||||
} else {
|
||||
error.value = 'Failed to check authentication status'
|
||||
}
|
||||
user.value = null
|
||||
accessToken.value = null
|
||||
return null
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const login = async (): Promise<void> => {
|
||||
try {
|
||||
clearError()
|
||||
const response = await $fetch<LoginResponse>('/api/auth/keycloak-login', {
|
||||
method: 'POST'
|
||||
})
|
||||
|
||||
if (response?.success && response?.data?.authUrl) {
|
||||
console.log('🔗 Redirecting to Keycloak login...')
|
||||
window.location.href = response.data.authUrl
|
||||
} else {
|
||||
const errorMsg = response?.error || 'Failed to get authorization URL'
|
||||
error.value = errorMsg
|
||||
throw new Error(errorMsg)
|
||||
}
|
||||
} catch (loginError: any) {
|
||||
console.error('❌ Login error:', loginError)
|
||||
error.value = loginError.message || 'Login failed'
|
||||
throw loginError
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async (): Promise<void> => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
clearError()
|
||||
console.log('🚪 Starting logout process...')
|
||||
|
||||
const response = await $fetch<LogoutResponse>('/api/auth/logout', {
|
||||
method: 'POST'
|
||||
})
|
||||
|
||||
// Clear user immediately regardless of response
|
||||
user.value = null
|
||||
accessToken.value = null
|
||||
|
||||
if (response?.success && response?.logoutUrl) {
|
||||
console.log('🔗 Redirecting to Keycloak logout...')
|
||||
// Keycloak will redirect back to the base URL after logout
|
||||
// We can add a query param to detect the logout and show a message
|
||||
// window.location.href = response.logoutUrl
|
||||
window.location.href = "/auth/login?logout=success"
|
||||
} else {
|
||||
const warningMsg = response?.error || response?.message || 'No logout URL received'
|
||||
console.warn('⚠️', warningMsg)
|
||||
error.value = warningMsg
|
||||
await navigateTo('/auth/login')
|
||||
}
|
||||
} catch (logoutError: any) {
|
||||
console.error('❌ Logout error:', logoutError)
|
||||
error.value = logoutError.message || 'Logout failed'
|
||||
user.value = null
|
||||
accessToken.value = null
|
||||
await navigateTo('/auth/login')
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to refresh user data
|
||||
const refreshUser = async (): Promise<boolean> => {
|
||||
const userData = await checkAuth()
|
||||
return !!userData
|
||||
}
|
||||
|
||||
// Helper function to check if user has specific role
|
||||
const hasRole = (role: string): boolean => {
|
||||
if (!user.value) return false
|
||||
|
||||
// Check in roles array
|
||||
if (user.value.roles?.includes(role)) return true
|
||||
|
||||
// Check in realm_access.roles
|
||||
if (user.value.realm_access?.roles?.includes(role)) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Helper function to check if user has any of the specified roles
|
||||
const hasAnyRole = (roles: string[]): boolean => {
|
||||
return roles.some(role => hasRole(role))
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
user: readonly(user),
|
||||
accessToken: readonly(accessToken),
|
||||
isAuthenticated,
|
||||
isLoading: readonly(isLoading),
|
||||
error: readonly(error),
|
||||
|
||||
// Actions
|
||||
checkAuth,
|
||||
login,
|
||||
logout,
|
||||
refreshUser,
|
||||
clearError,
|
||||
|
||||
// Utilities
|
||||
hasRole,
|
||||
hasAnyRole
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user