127 lines
3.4 KiB
Vue
127 lines
3.4 KiB
Vue
<script setup lang="ts">
|
||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||
|
||
definePageMeta({
|
||
middleware: 'guest'
|
||
})
|
||
|
||
useHead({ title: 'Accesso CIE - Visione' })
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const { fetchCieSession, handleLoginSuccess } = useAuth()
|
||
|
||
const state = computed(() => {
|
||
const value = route.query.state
|
||
return typeof value === 'string' ? value : ''
|
||
})
|
||
|
||
const isLoading = ref(true)
|
||
const errorMessage = ref('')
|
||
const statusMessage = ref('Verifica dell’autenticazione CIE in corso...')
|
||
|
||
let pollTimer: ReturnType<typeof setTimeout> | null = null
|
||
let attempts = 0
|
||
const maxAttempts = 30
|
||
|
||
const stopPolling = () => {
|
||
if (pollTimer) {
|
||
clearTimeout(pollTimer)
|
||
pollTimer = null
|
||
}
|
||
}
|
||
|
||
const scheduleNextPoll = () => {
|
||
pollTimer = setTimeout(checkSession, 2000)
|
||
}
|
||
|
||
const redirectToLoginError = async (code: string, fallbackMessage: string) => {
|
||
stopPolling()
|
||
isLoading.value = false
|
||
|
||
try {
|
||
await router.replace({
|
||
path: '/auth/login',
|
||
query: { cie_error: code }
|
||
})
|
||
} catch {
|
||
errorMessage.value = fallbackMessage
|
||
}
|
||
}
|
||
|
||
const checkSession = async () => {
|
||
if (!state.value) {
|
||
await redirectToLoginError('invalid_session', 'Sessione CIE non valida: parametro state mancante.')
|
||
return
|
||
}
|
||
|
||
attempts += 1
|
||
|
||
try {
|
||
const res = await fetchCieSession(state.value)
|
||
|
||
if (res.status === 'authenticated' && res.auth?.token) {
|
||
stopPolling()
|
||
await handleLoginSuccess(res.auth.token)
|
||
return
|
||
}
|
||
|
||
if (['failed', 'expired', 'cancelled', 'error'].includes(res.status)) {
|
||
await redirectToLoginError('failed', 'Autenticazione CIE non completata. Riprova dal login.')
|
||
return
|
||
}
|
||
|
||
if (attempts >= maxAttempts) {
|
||
await redirectToLoginError('timeout', 'Tempo di attesa scaduto durante la verifica CIE. Riprova.')
|
||
return
|
||
}
|
||
|
||
statusMessage.value = 'Autenticazione completata sul provider. Attendo conferma finale da Visione...'
|
||
scheduleNextPoll()
|
||
} catch (err: any) {
|
||
await redirectToLoginError('verification_error', err.data?.message || 'Errore durante la verifica della sessione CIE.')
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
checkSession()
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
stopPolling()
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="d-flex align-items-center bg-light py-5">
|
||
<div class="container">
|
||
<div class="row justify-content-center">
|
||
<div class="col-12 col-md-8 col-lg-6">
|
||
<div class="card card-bg shadow-sm">
|
||
<div class="card-body p-4 p-md-5 text-center">
|
||
<BIIcon name="it-card" size="xl" color="primary" class="mb-3" />
|
||
<h1 class="h3 font-weight-bold mb-3">Accesso con CIE</h1>
|
||
|
||
<div v-if="isLoading" class="py-3">
|
||
<div class="spinner-border text-primary mb-3" role="status">
|
||
<span class="visually-hidden">Caricamento...</span>
|
||
</div>
|
||
<p class="text-muted mb-0">{{ statusMessage }}</p>
|
||
</div>
|
||
|
||
<div v-else-if="errorMessage" class="py-2">
|
||
<div class="alert alert-danger text-start mb-4" role="alert">
|
||
{{ errorMessage }}
|
||
</div>
|
||
<button class="btn btn-primary" @click="router.push('/auth/login')">
|
||
Torna al login
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|