codevdesign 1.0.67 → 1.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/outils/rafraichisseurToken.ts +74 -17
- package/package.json +1 -1
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
class RafraichisseurToken {
|
|
2
2
|
private intervalleEnSecondes = 15
|
|
3
3
|
private secondesAvantExpirationTokenPourRafraichir = 20
|
|
4
|
-
private skewSeconds = 5 // marge anti-derives d
|
|
4
|
+
private skewSeconds = 5 // marge anti-derives d'horloge
|
|
5
5
|
private popupAffiche = false
|
|
6
6
|
private timerId: number | null = null
|
|
7
|
+
private nomTemoin = 'csqc_jeton_secure_expiration'
|
|
8
|
+
private urlPortail = ''
|
|
9
|
+
private refreshPromise: Promise<void> | null = null
|
|
7
10
|
|
|
8
11
|
// Lance une seule fois
|
|
9
12
|
public async demarrer(nomTemoin: string | null, urlPortail: string): Promise<void> {
|
|
10
13
|
urlPortail = urlPortail.replace(/\/+$/, '')
|
|
11
14
|
if (nomTemoin == null || nomTemoin === '') nomTemoin = 'csqc_jeton_secure_expiration'
|
|
12
|
-
|
|
15
|
+
this.nomTemoin = nomTemoin as string
|
|
16
|
+
this.urlPortail = urlPortail
|
|
17
|
+
await this.verifierJeton(nomTemoin as string, urlPortail)
|
|
13
18
|
if (this.timerId != null) return
|
|
14
19
|
this.timerId = window.setInterval(() => {
|
|
15
|
-
this.verifierJeton(nomTemoin, urlPortail)
|
|
20
|
+
this.verifierJeton(nomTemoin as string, urlPortail)
|
|
16
21
|
}, this.intervalleEnSecondes * 1000)
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
public attendreRefreshSiNecessaire(force = false): Promise<void> {
|
|
25
|
+
if (this.refreshPromise) return this.refreshPromise
|
|
26
|
+
if (force || !this.estJetonValide(this.nomTemoin)) {
|
|
27
|
+
this.refreshPromise = this.rafraichir(this.nomTemoin, this.urlPortail).finally(() => {
|
|
28
|
+
this.refreshPromise = null
|
|
29
|
+
})
|
|
30
|
+
return this.refreshPromise
|
|
31
|
+
}
|
|
32
|
+
return Promise.resolve()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Permet d'arrêter le timer (ex: au logout / destroy)
|
|
20
36
|
public arreter(): void {
|
|
21
37
|
if (this.timerId != null) {
|
|
22
38
|
clearInterval(this.timerId)
|
|
@@ -32,8 +48,11 @@ class RafraichisseurToken {
|
|
|
32
48
|
if (this.popupAffiche) return
|
|
33
49
|
|
|
34
50
|
if (!this.estJetonValide(nomTemoin)) {
|
|
35
|
-
this.
|
|
36
|
-
|
|
51
|
+
if (!this.refreshPromise) {
|
|
52
|
+
this.refreshPromise = this.rafraichir(nomTemoin, urlPortail).finally(() => {
|
|
53
|
+
this.refreshPromise = null
|
|
54
|
+
})
|
|
55
|
+
}
|
|
37
56
|
}
|
|
38
57
|
}
|
|
39
58
|
|
|
@@ -54,8 +73,6 @@ class RafraichisseurToken {
|
|
|
54
73
|
|
|
55
74
|
const exp = Number(token?.exp)
|
|
56
75
|
if (!Number.isFinite(exp)) {
|
|
57
|
-
// exp manquant/invalide → tente refresh
|
|
58
|
-
|
|
59
76
|
return false
|
|
60
77
|
}
|
|
61
78
|
|
|
@@ -70,6 +87,9 @@ class RafraichisseurToken {
|
|
|
70
87
|
|
|
71
88
|
private async rafraichir(nomCookie: string, urlPortail: string): Promise<void> {
|
|
72
89
|
if (!nomCookie) return
|
|
90
|
+
//console.log('[RafraichisseurToken] rafraichir() appelé')
|
|
91
|
+
if (urlPortail == null || urlPortail == '') return
|
|
92
|
+
this.popupAffiche = true // bloquer tout nouveau tick pendant le refresh
|
|
73
93
|
const url = this.getRefreshUrl(urlPortail)
|
|
74
94
|
const controller = new AbortController()
|
|
75
95
|
const timeout = setTimeout(() => controller.abort(), 10_000)
|
|
@@ -84,51 +104,73 @@ class RafraichisseurToken {
|
|
|
84
104
|
signal: controller.signal,
|
|
85
105
|
})
|
|
86
106
|
|
|
87
|
-
// redirection
|
|
88
|
-
|
|
107
|
+
// redirection vers Azure AD → session expirée, l'iframe ne peut pas compléter le flow OIDC cross-site
|
|
108
|
+
//console.log('[RafraichisseurToken] fetch réponse type:', resp.type, 'status:', resp.status)
|
|
89
109
|
if (resp.type === 'opaqueredirect' || resp.status === 302) {
|
|
90
|
-
this.
|
|
110
|
+
this.estDeconnecteAzure(urlPortail)
|
|
91
111
|
return
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
// OK ou No Content: le cookie devrait être réécrit
|
|
95
115
|
if (resp.status === 200 || resp.status === 204) {
|
|
96
116
|
const jeton = this.lireCookie(nomCookie)
|
|
97
|
-
if (!jeton)
|
|
117
|
+
if (!jeton) {
|
|
118
|
+
// console.log('[RafraichisseurToken] 200 mais cookie absent → iframe')
|
|
119
|
+
this.rafraichirParIframe(nomCookie, urlPortail)
|
|
120
|
+
} else {
|
|
121
|
+
this.popupAffiche = false // succès, reprendre la surveillance
|
|
122
|
+
}
|
|
98
123
|
return
|
|
99
124
|
}
|
|
100
125
|
|
|
101
126
|
// non auth / expiré (401, 419) + IIS timeout (440)
|
|
102
127
|
if (resp.status === 401 || resp.status === 419 || resp.status === 440) {
|
|
128
|
+
// console.log('[RafraichisseurToken] statut auth → iframe')
|
|
103
129
|
this.rafraichirParIframe(nomCookie, urlPortail)
|
|
104
130
|
return
|
|
105
131
|
}
|
|
106
132
|
|
|
107
133
|
console.warn('Rafraichisseur token: statut inattendu', resp.status)
|
|
134
|
+
this.popupAffiche = false // statut inattendu, permettre un retry
|
|
108
135
|
} catch (err: any) {
|
|
109
136
|
if (err?.name === 'AbortError') console.warn('RafraichisseurToken timeout')
|
|
110
137
|
else console.error('Erreur rafraichisseur de token', err)
|
|
111
|
-
|
|
138
|
+
this.reloadSiErreurReseau()
|
|
112
139
|
} finally {
|
|
113
140
|
clearTimeout(timeout)
|
|
114
141
|
}
|
|
115
142
|
}
|
|
116
143
|
|
|
117
144
|
private rafraichirParIframe(nomCookie: string, urlPortail: string): void {
|
|
118
|
-
|
|
119
|
-
// ajax vers le refresh
|
|
145
|
+
this.popupAffiche = true // bloquer le timer pendant que l'iframe tente l'auth
|
|
120
146
|
let iframe = document.createElement('iframe')
|
|
121
147
|
const url = this.getRefreshUrl(urlPortail)
|
|
122
148
|
iframe.src = `${url}?urlRetour=${encodeURI(window.location.href)}`
|
|
123
149
|
iframe.id = 'idRafrToken'
|
|
124
150
|
iframe.style.display = 'none'
|
|
125
151
|
document.body.appendChild(iframe)
|
|
152
|
+
|
|
153
|
+
const iframeTimeout = setTimeout(() => {
|
|
154
|
+
iframe.onload = null
|
|
155
|
+
iframe.src = 'about:blank'
|
|
156
|
+
iframe.remove()
|
|
157
|
+
this.popupAffiche = false
|
|
158
|
+
console.warn('[RafraichisseurToken] iframe timeout — popupAffiche réinitialisé')
|
|
159
|
+
}, 15_000)
|
|
160
|
+
|
|
126
161
|
iframe.onload = () => {
|
|
127
|
-
|
|
128
|
-
|
|
162
|
+
clearTimeout(iframeTimeout)
|
|
163
|
+
iframe.onload = null // empêcher les re-entrées sur les redirects OIDC internes
|
|
164
|
+
const nomSecure = nomCookie.replace('_expiration', '')
|
|
165
|
+
const cookieAVerifier = nomSecure !== nomCookie ? nomSecure : nomCookie
|
|
166
|
+
const jeton = this.lireCookie(cookieAVerifier)
|
|
167
|
+
if (jeton == null || jeton === '') {
|
|
129
168
|
this.estDeconnecteAzure(urlPortail)
|
|
169
|
+
} else {
|
|
170
|
+
this.popupAffiche = false // cookie recréé avec succès, reprendre la surveillance
|
|
130
171
|
}
|
|
131
172
|
|
|
173
|
+
iframe.src = 'about:blank' // stopper les navigations internes
|
|
132
174
|
iframe.remove()
|
|
133
175
|
}
|
|
134
176
|
}
|
|
@@ -176,6 +218,21 @@ class RafraichisseurToken {
|
|
|
176
218
|
return JSON.parse(jsonPayload)
|
|
177
219
|
}
|
|
178
220
|
|
|
221
|
+
private reloadSiErreurReseau(): void {
|
|
222
|
+
const CLE = 'rafraichisseur_dernierReload'
|
|
223
|
+
// console.log('[RafraichisseurToken] reload si erreur')
|
|
224
|
+
const maintenant = Date.now()
|
|
225
|
+
const dernierReload = Number(sessionStorage.getItem(CLE) ?? 0)
|
|
226
|
+
if (maintenant - dernierReload > 30_000) {
|
|
227
|
+
// console.log('[RafraichisseurToken] Écriture sessionStorage:', CLE, maintenant)
|
|
228
|
+
sessionStorage.setItem(CLE, String(maintenant))
|
|
229
|
+
|
|
230
|
+
globalThis.location.reload()
|
|
231
|
+
} else {
|
|
232
|
+
this.popupAffiche = false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
179
236
|
// URL refresh selon env (dev = proxy Vite ; prod = portail)
|
|
180
237
|
private getRefreshUrl(urlPortail: string): string {
|
|
181
238
|
if (import.meta.env.MODE === 'development') return '/portail-refresh'
|