port-nimara-client-portal/composables/useCustomAuth.ts

119 lines
3.0 KiB
TypeScript

interface User {
id: string
email: string
username: string
name: string
authMethod: string
}
interface AuthState {
user: User | null
authenticated: boolean
}
export const useCustomAuth = () => {
const user = ref<User | null>(null)
const authenticated = ref(false)
const loading = ref(true)
const refreshing = ref(false)
// Check authentication status
const checkAuth = async () => {
try {
loading.value = true
const data = await $fetch<AuthState>('/api/auth/session')
user.value = data.user
authenticated.value = data.authenticated
console.log('[CUSTOM_AUTH] Session check result:', {
authenticated: data.authenticated,
userId: data.user?.id
})
} catch (error) {
console.error('[CUSTOM_AUTH] Session check failed:', error)
user.value = null
authenticated.value = false
} finally {
loading.value = false
}
}
// Refresh token
const refreshToken = async () => {
if (refreshing.value) return false
try {
refreshing.value = true
console.log('[CUSTOM_AUTH] Attempting token refresh...')
const response = await $fetch<{ success: boolean }>('/api/auth/refresh', {
method: 'POST'
})
if (response.success) {
console.log('[CUSTOM_AUTH] Token refresh successful')
await checkAuth() // Re-check auth state after refresh
return true
}
return false
} catch (error) {
console.error('[CUSTOM_AUTH] Token refresh failed:', error)
// Clear auth state on refresh failure
user.value = null
authenticated.value = false
return false
} finally {
refreshing.value = false
}
}
// Login with Keycloak
const login = () => {
const authUrl = 'https://auth.portnimara.dev/realms/client-portal/protocol/openid-connect/auth?' +
new URLSearchParams({
client_id: 'client-portal',
redirect_uri: 'https://client.portnimara.dev/api/auth/keycloak/callback',
response_type: 'code',
scope: 'openid profile email',
state: Math.random().toString(36).substring(2)
}).toString()
console.log('[CUSTOM_AUTH] Redirecting to Keycloak login:', authUrl)
navigateTo(authUrl, { external: true })
}
// Logout
const logout = async () => {
try {
console.log('[CUSTOM_AUTH] Initiating logout...')
// Clear local state immediately
user.value = null
authenticated.value = false
// Redirect to logout endpoint
await navigateTo('/api/auth/logout', { external: true })
} catch (error) {
console.error('[CUSTOM_AUTH] Logout failed:', error)
// Fallback: redirect to login
await navigateTo('/login')
}
}
// Initialize auth state on composable creation
onMounted(() => {
checkAuth()
})
return {
user: readonly(user),
authenticated: readonly(authenticated),
loading: readonly(loading),
refreshing: readonly(refreshing),
login,
logout,
checkAuth,
refreshToken
}
}