179 lines
No EOL
4.1 KiB
JavaScript
179 lines
No EOL
4.1 KiB
JavaScript
import { ref } from 'vue'
|
|
|
|
const userToken = ref(null)
|
|
const refreshToken = ref(null)
|
|
const userProfile = ref(null)
|
|
const isAuthenticated = ref(false)
|
|
const isInitialized = ref(false)
|
|
|
|
let sessionPollTimer = null
|
|
|
|
export const useAuth = () => {
|
|
const fetchUserProfile = async () => {
|
|
if (!userToken.value) return
|
|
try {
|
|
const profile = await $fetch('/api/user/profile', {
|
|
headers: {
|
|
Authorization: `Bearer ${userToken.value}`
|
|
}
|
|
})
|
|
userProfile.value = profile
|
|
} catch (err) {
|
|
console.error('Failed to fetch user profile:', err)
|
|
}
|
|
}
|
|
|
|
const setSession = (data) => {
|
|
userToken.value = data.access_token
|
|
if (data.refresh_token) {
|
|
refreshToken.value = data.refresh_token
|
|
if (import.meta.client) {
|
|
localStorage.setItem('keycloak_refresh_token', data.refresh_token)
|
|
}
|
|
}
|
|
isAuthenticated.value = true
|
|
}
|
|
|
|
const clearSession = () => {
|
|
stopLiveSessionPolling()
|
|
userToken.value = null
|
|
refreshToken.value = null
|
|
userProfile.value = null
|
|
isAuthenticated.value = false
|
|
if (import.meta.client) {
|
|
localStorage.removeItem('keycloak_refresh_token')
|
|
}
|
|
}
|
|
|
|
const exchangeCodeForToken = async (code) => {
|
|
try {
|
|
const data = await $fetch('/api/auth/token', {
|
|
method: 'POST',
|
|
body: {
|
|
code,
|
|
redirectUri: `${window.location.origin}/oauth`
|
|
}
|
|
})
|
|
|
|
setSession(data)
|
|
window.history.replaceState({}, document.title, window.location.pathname)
|
|
await fetchUserProfile()
|
|
startLiveSessionPolling()
|
|
|
|
return { success: true, isFirstLogin: data.isFirstLogin }
|
|
} catch (error) {
|
|
console.error('Code exchange failed:', error)
|
|
clearSession()
|
|
return { success: false, isFirstLogin: false }
|
|
} finally {
|
|
isInitialized.value = true
|
|
}
|
|
}
|
|
|
|
const silentRefresh = async () => {
|
|
const storedRefreshToken = refreshToken.value || (import.meta.client && localStorage.getItem('keycloak_refresh_token'))
|
|
|
|
if (!storedRefreshToken) {
|
|
clearSession()
|
|
isInitialized.value = true
|
|
return false
|
|
}
|
|
|
|
try {
|
|
const data = await $fetch('/api/auth/refresh', {
|
|
method: 'POST',
|
|
body: { refreshToken: storedRefreshToken }
|
|
})
|
|
|
|
setSession(data)
|
|
await fetchUserProfile()
|
|
startLiveSessionPolling()
|
|
return true
|
|
} catch (error) {
|
|
console.warn('Silent refresh failed:', error)
|
|
clearSession()
|
|
return false
|
|
} finally {
|
|
isInitialized.value = true
|
|
}
|
|
}
|
|
|
|
const verifySession = async () => {
|
|
if (!userToken.value) {
|
|
return await silentRefresh()
|
|
}
|
|
|
|
try {
|
|
const res = await $fetch('/api/auth/check-session', {
|
|
method: 'POST',
|
|
body: { token: userToken.value }
|
|
})
|
|
|
|
if (res.active) {
|
|
isAuthenticated.value = true
|
|
if (!userProfile.value) {
|
|
await fetchUserProfile()
|
|
}
|
|
return true
|
|
} else {
|
|
return await silentRefresh()
|
|
}
|
|
} catch (e) {
|
|
return await silentRefresh()
|
|
} finally {
|
|
isInitialized.value = true
|
|
}
|
|
}
|
|
|
|
const startLiveSessionPolling = () => {
|
|
if (import.meta.server) return
|
|
stopLiveSessionPolling()
|
|
|
|
sessionPollTimer = setInterval(async () => {
|
|
if (isAuthenticated.value) {
|
|
const valid = await verifySession()
|
|
if (!valid) {
|
|
handleLocalLogout()
|
|
}
|
|
}
|
|
}, 5000)
|
|
}
|
|
|
|
const stopLiveSessionPolling = () => {
|
|
if (sessionPollTimer) {
|
|
clearInterval(sessionPollTimer)
|
|
sessionPollTimer = null
|
|
}
|
|
}
|
|
|
|
const handleLocalLogout = () => {
|
|
clearSession()
|
|
isInitialized.value = true
|
|
navigateTo('/oauth')
|
|
}
|
|
|
|
const logout = async () => {
|
|
try {
|
|
await $fetch('/api/auth/logout', {
|
|
method: 'POST',
|
|
body: { refreshToken: refreshToken.value }
|
|
})
|
|
} catch (e) {
|
|
console.warn('Server logout failed:', e)
|
|
} finally {
|
|
handleLocalLogout()
|
|
}
|
|
}
|
|
|
|
return {
|
|
userToken,
|
|
userProfile,
|
|
isAuthenticated,
|
|
isInitialized,
|
|
exchangeCodeForToken,
|
|
silentRefresh,
|
|
verifySession,
|
|
fetchUserProfile,
|
|
logout
|
|
}
|
|
} |