From 738ce4a17131d766b37cf6718458c7af4e24c857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 21:25:42 +0200 Subject: [PATCH 01/20] Ajout du bouton ProConnect officiel --- src/app.postcss | 26 +++++++++++++++++ .../components/specialized/pc-button.svelte | 28 +++++++++++++++++++ src/routes/auth/connexion/+page.svelte | 2 ++ 3 files changed, 56 insertions(+) create mode 100644 src/lib/components/specialized/pc-button.svelte diff --git a/src/app.postcss b/src/app.postcss index 04ab6d82b..782387a9e 100644 --- a/src/app.postcss +++ b/src/app.postcss @@ -172,4 +172,30 @@ @page { size: 26.25cm 37.125cm; /* A4 * 1.25, afin de réduire la taille de l'impression */ } + + proconnect-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + } + + .proconnect-button { + background-color: transparent !important; + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMTEnIGhlaWdodD0nNTgnIGZpbGw9J25vbmUnPjxwYXRoIGZpbGw9JyMwMDAwOTEnIGQ9J00wIDBoMjExdjU4SDB6Jy8+PHBhdGggZmlsbD0nI2ZmZicgZD0nbTY5Ljk4NiAyNi4zNjggMS4xNTYtMS4wNzFjLjgzMyAxLjA1NCAxLjgxOSAxLjU5OCAyLjk0MSAxLjU5OCAxLjI5MiAwIDIuMDQtLjgxNiAyLjA0LTEuOTA0IDAtMi41NS01LjYyNy0yLjI0NC01LjYyNy02LjAzNSAwLTEuNzM0IDEuNDI4LTMuMTk2IDMuNDUxLTMuMTk2IDEuNjgzIDAgMi45MDcuNzY1IDMuNzkxIDEuOTM4bC0xLjE5IDEuMDM3Yy0uNjk3LTEuMDAzLTEuNTQ3LTEuNTQ3LTIuNTg0LTEuNTQ3LTEuMTA1IDAtMS44MzYuNzQ4LTEuODM2IDEuNzM0IDAgMi41NjcgNS42MjcgMi4yNDQgNS42MjcgNi4wNTIgMCAyLjAyMy0xLjU4MSAzLjM0OS0zLjY1NSAzLjM0OS0xLjc2OCAwLTMuMDc3LS42NjMtNC4xMTQtMS45NTVabTEwLjgxNy01LjcxMkg3OS40NmwxLjQ0NS00LjU1NmgxLjY0OWwtMS43NTEgNC41NTZabTQuODE4LTMuNDUxYy0uNTYgMC0xLjAyLS40NTktMS4wMi0xLjAyYTEuMDIgMS4wMiAwIDAgMSAxLjAyLTEuMDAzYy41NjEgMCAxLjAwMy40NTkgMS4wMDMgMS4wMDMgMCAuNTYxLS40NDIgMS4wMi0xLjAwMyAxLjAyWk04NC44OTEgMjh2LTguNTY4aDEuNDQ0VjI4SDg0Ljg5Wm0zLjc2Ny00LjI4NGMwLTIuNDk5IDEuNzE3LTQuNjI0IDQuNDAzLTQuNjI0IDEuMjQxIDAgMi4yNjEuNDU5IDMuMDQzIDEuMjkyVjE1LjI1aDEuNDQ1VjI4aC0xLjQ0NXYtLjk1MmMtLjc4Mi44MzMtMS44MDIgMS4yOTItMy4wNDMgMS4yOTItMi42ODYgMC00LjQwMy0yLjEyNS00LjQwMy00LjYyNFptMS41MyAwYzAgMS44MTkgMS4yMjQgMy4yNjQgMy4wNDMgMy4yNjQgMS4xOSAwIDIuMjEtLjU3OCAyLjg3My0xLjU5OFYyMi4wNWMtLjY4LTEuMDM3LTEuNy0xLjU5OC0yLjg3My0xLjU5OC0xLjgxOSAwLTMuMDQzIDEuNDQ1LTMuMDQzIDMuMjY0Wm0xOC4wMjMgMi44NzNjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xMTAuNDczIDI4di04LjU2OGgxLjQ0NXYuOTY5Yy42OTctLjc2NSAxLjU4MS0xLjMwOSAyLjg1Ni0xLjMwOSAxLjkyMSAwIDMuMzQ5IDEuMjkyIDMuMzQ5IDMuNzIzVjI4aC0xLjQ2MnYtNS4xMzRjMC0xLjUzLS44NS0yLjQxNC0yLjE3Ni0yLjQxNC0xLjI0MSAwLTIuMDIzLjcxNC0yLjU2NyAxLjYxNVYyOGgtMS40NDVabTExLjA1Mi0yLjg3M3YtNC4zNjloLTEuNjE1di0xLjMyNmgxLjYxNVYxNy4yOWgxLjQ2MnYyLjE0MmgyLjk3NXYxLjMyNmgtMi45NzV2NC4zNjljMCAxLjM0My42OCAxLjcxNyAxLjcxNyAxLjcxNy41NjEgMCAuOTUyLS4wNjggMS4yNzUtLjIwNHYxLjI5MmMtLjQwOC4xNy0uODY3LjIzOC0xLjQ3OS4yMzgtMS45MDQgMC0yLjk3NS0uOTUyLTIuOTc1LTMuMDQzWm03LjM3Ny03LjkyMmMtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEyOC4xNzEgMjh2LTguNTY4aDEuNDQ1VjI4aC0xLjQ0NVptMy4zNzctOC41NjhoMS42MTV2LTEuMDU0YzAtMS44MzYgMS4yMDctMy4xMjggMy4wNDMtMy4xMjguOTUyIDAgMS43LjM0IDIuMjEuODMzbC0uOTAxIDEuMDU0YTEuNjMzIDEuNjMzIDAgMCAwLTEuMjkyLS41NzhjLS45MzUgMC0xLjU5OC42OC0xLjU5OCAxLjc4NXYxLjA4OGgyLjk3NXYxLjMyNmgtMi45NzVWMjhoLTEuNDYydi03LjI0MmgtMS42MTV2LTEuMzI2Wm04LjU0My0yLjIyN2MtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEzOS4zNiAyOHYtOC41NjhoMS40NDVWMjhoLTEuNDQ1Wm0xMi4xMTUtMS40MTFjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xNTMuNzM3IDI4di04LjU2OGgxLjQ0NXYxLjA3MWMuNjI5LS43NDggMS40MTEtMS4yNDEgMi40OTktMS4yNDEuMjcyIDAgLjUyNy4wMzQuNzMxLjEwMnYxLjQ5NmEzLjEwNSAzLjEwNSAwIDAgMC0uODUtLjExOWMtMS4xMjIgMC0xLjg1My41NzgtMi4zOCAxLjQ0NVYyOGgtMS40NDVabTEzLjY4NS4zNGMtMS42ODMgMC0yLjgyMi0uOTUyLTIuODIyLTIuNDQ4IDAtMS4zMjYuOTg2LTIuMjc4IDIuODIyLTIuNTY3bDIuODczLS40NzZ2LS41OTVjMC0xLjE5LS44NS0xLjg3LTIuMDU3LTEuODctMS4wMDMgMC0xLjgzNi40NDItMi4zMjkgMS4xOWwtMS4wODgtLjgzM2MuNzQ4LTEuMDIgMS45NTUtMS42NDkgMy40NTEtMS42NDkgMi4xNzYgMCAzLjQ2OCAxLjI3NSAzLjQ2OCAzLjE2MlYyOGgtMS40NDV2LTEuMDg4Yy0uNjQ2LjkwMS0xLjcxNyAxLjQyOC0yLjg3MyAxLjQyOFptLTEuMzc3LTIuNDk5YzAgLjczMS42MjkgMS4yOTIgMS42MTUgMS4yOTIgMS4xMzkgMCAyLjA0LS41OTUgMi42MzUtMS41ODFWMjMuOTJsLTIuNTMzLjQ0MmMtMS4xOS4xODctMS43MTcuNzMxLTEuNzE3IDEuNDc5Wm03LjI1Mi02LjQwOWgxLjU2NGwyLjczNyA3LjA1NSAyLjczNy03LjA1NWgxLjU2NEwxNzguNTUgMjhoLTEuOTA0bC0zLjM0OS04LjU2OFptMTcuODU2IDcuMTU3Yy0uNzk5IDEuMDcxLTIuMDc0IDEuNzUxLTMuNjcyIDEuNzUxLTIuODkgMC00LjY3NS0yLjEyNS00LjY3NS00LjYyNCAwLTIuNjAxIDEuNjY2LTQuNjI0IDQuMzE4LTQuNjI0IDIuMzI5IDAgMy44NDIgMS41ODEgMy44NDIgMy43MjMgMCAuMzQtLjA1MS42OC0uMTAyLjkxOGgtNi41NjJ2LjAzNGMwIDEuODg3IDEuMjkyIDMuMjY0IDMuMjEzIDMuMjY0IDEuMDg4IDAgMi4wMDYtLjUxIDIuNTY3LTEuMjc1bDEuMDcxLjgzM1ptLTQuMDEyLTYuMjU2Yy0xLjM5NCAwLTIuMzguNzgyLTIuNzIgMi4yNjFoNS4wODNjLS4wNTEtMS4yNDEtLjk1Mi0yLjI2MS0yLjM2My0yLjI2MVptMTAuMTg1IDYuNjQ3YzEuMDU0IDAgMS45MDQtLjUxIDIuNDMxLTEuMjc1bDEuMTU2Ljg4NGMtLjc5OSAxLjA3MS0yLjA0IDEuNzUxLTMuNjA0IDEuNzUxLTIuODM5IDAtNC42NTgtMi4xMjUtNC42NTgtNC42MjQgMC0yLjQ5OSAxLjgxOS00LjYyNCA0LjY1OC00LjYyNCAxLjU0NyAwIDIuODA1LjY5NyAzLjYwNCAxLjc1MWwtMS4xNTYuODg0YTIuOTI1IDIuOTI1IDAgMCAwLTIuNDQ4LTEuMjc1Yy0xLjgzNiAwLTMuMTQ1IDEuNDQ1LTMuMTQ1IDMuMjY0IDAgMS44MzYgMS4zMDkgMy4yNjQgMy4xNjIgMy4yNjRaTTcwLjg1NCA0NVYzMi40aDQuMTU4YzIuNzcyIDAgNC40NjQgMS40MjIgNC40NjQgMy43NjIgMCAyLjMyMi0xLjY5MiAzLjc0NC00LjQ2NCAzLjc0NEg3My40MVY0NWgtMi41NTZabTQuMjY2LTEwLjQyMmgtMS43MXYzLjE1aDEuNzFjMS4wOCAwIDEuNzI4LS41NzYgMS43MjgtMS42MDIgMC0uOTU0LS42NDgtMS41NDgtMS43MjgtMS41NDhaTTgxLjI0OSA0NXYtOS4wNzJoMi4yODZ2LjljLjU5NC0uNjEyIDEuMzY4LTEuMDggMi4zOTQtMS4wOC4zMDYgMCAuNTc2LjA1NC43OTIuMTI2djIuMzk0YTMuOTM4IDMuOTM4IDAgMCAwLTEuMDA4LS4xMjZjLTEuMTE2IDAtMS44MzYuNjEyLTIuMTc4IDEuMTdWNDVoLTIuMjg2Wm0xMS4zODYtOS40MzJjMi45NTIgMCA0Ljk2OCAyLjE3OCA0Ljk2OCA0Ljg5NnMtMi4wMTYgNC44OTYtNC45NjggNC44OTYtNC45NjgtMi4xNzgtNC45NjgtNC44OTYgMi4wMTYtNC44OTYgNC45NjgtNC44OTZabS4wMzYgNy42MzJjMS40NTggMCAyLjU1Ni0xLjE3IDIuNTU2LTIuNzM2IDAtMS41ODQtMS4wOTgtMi43MzYtMi41NTYtMi43MzYtMS41MTIgMC0yLjYyOCAxLjE1Mi0yLjYyOCAyLjczNiAwIDEuNTg0IDEuMTE2IDIuNzM2IDIuNjI4IDIuNzM2Wm0xMy4xNzItLjIzNGMxLjQ0IDAgMi41NzQtLjcwMiAzLjI5NC0xLjcyOGwyLjAxNiAxLjU0OGMtMS4xNTIgMS41NjYtMy4wMjQgMi41NzQtNS4zMSAyLjU3NC0zLjk3OCAwLTYuNjk2LTMuMDYtNi42OTYtNi42NnMyLjcxOC02LjY2IDYuNjk2LTYuNjZjMi4yODYgMCA0LjE1OCAxLjAyNiA1LjMxIDIuNTU2bC0yLjAxNiAxLjU2NmMtLjcyLTEuMDI2LTEuODU0LTEuNzI4LTMuMjk0LTEuNzI4LTIuMzc2IDAtNC4wNjggMS44NTQtNC4wNjggNC4yNjZzMS42OTIgNC4yNjYgNC4wNjggNC4yNjZabTExLjM2Ni03LjM5OGMyLjk1MiAwIDQuOTY4IDIuMTc4IDQuOTY4IDQuODk2cy0yLjAxNiA0Ljg5Ni00Ljk2OCA0Ljg5Ni00Ljk2OC0yLjE3OC00Ljk2OC00Ljg5NiAyLjAxNi00Ljg5NiA0Ljk2OC00Ljg5NlptLjAzNiA3LjYzMmMxLjQ1OCAwIDIuNTU2LTEuMTcgMi41NTYtMi43MzYgMC0xLjU4NC0xLjA5OC0yLjczNi0yLjU1Ni0yLjczNi0xLjUxMiAwLTIuNjI4IDEuMTUyLTIuNjI4IDIuNzM2IDAgMS41ODQgMS4xMTYgMi43MzYgMi42MjggMi43MzZabTcuMDE4IDEuOHYtOS4wNzJoMi4yODZ2LjcyYy42My0uNjEyIDEuNDc2LTEuMDggMi42ODItMS4wOCAxLjk2MiAwIDMuNTI4IDEuMzUgMy41MjggNC4wMzJWNDVoLTIuMzIydi01LjMxYzAtMS4yMDYtLjY2Ni0xLjk2Mi0xLjc4Mi0xLjk2Mi0xLjE1MiAwLTEuNzY0Ljc3NC0yLjEwNiAxLjM1VjQ1aC0yLjI4NlptMTEuMDkxIDB2LTkuMDcyaDIuMjg2di43MmMuNjMtLjYxMiAxLjQ3Ni0xLjA4IDIuNjgyLTEuMDggMS45NjIgMCAzLjUyOCAxLjM1IDMuNTI4IDQuMDMyVjQ1aC0yLjMyMnYtNS4zMWMwLTEuMjA2LS42NjYtMS45NjItMS43ODItMS45NjItMS4xNTIgMC0xLjc2NC43NzQtMi4xMDYgMS4zNVY0NWgtMi4yODZabTE5LjQ0NC0xLjQ3NmMtLjg0NiAxLjEzNC0yLjI1IDEuODM2LTMuOTYgMS44MzYtMy4yMjIgMC01LjA0LTIuMjUtNS4wNC00Ljg5NiAwLTIuNjgyIDEuNjkyLTQuODk2IDQuNjYyLTQuODk2IDIuNTIgMCA0LjE3NiAxLjY5MiA0LjE3NiA0LjA2OCAwIC41MDQtLjA3Mi45OS0uMTQ0IDEuMjk2aC02LjM1NGMuMTQ0IDEuNDk0IDEuMTg4IDIuMzc2IDIuNzM2IDIuMzc2Ljk5IDAgMS44LS40MzIgMi4yODYtMS4wOGwxLjYzOCAxLjI5NlptLTQuMzM4LTYuMDQ4Yy0xLjExNiAwLTEuODcyLjU0LTIuMTc4IDEuNzI4aDQuMDg2Yy0uMDM2LS45LS43MDItMS43MjgtMS45MDgtMS43MjhabTEwLjY5NiA1LjcyNGMuODgyIDAgMS41ODQtLjQzMiAyLjAxNi0xLjA2MmwxLjgxOCAxLjM4NmMtLjg0NiAxLjExNi0yLjE3OCAxLjgzNi0zLjgzNCAxLjgzNi0zLjEzMiAwLTUuMDA0LTIuMjUtNS4wMDQtNC44OTZzMS44NzItNC44OTYgNS4wMDQtNC44OTZjMS42NTYgMCAyLjk4OC43MiAzLjgzNCAxLjgzNmwtMS44MTggMS4zODZjLS40MzItLjYzLTEuMTE2LTEuMDYyLTIuMDUyLTEuMDYyLTEuNDk0IDAtMi41OTIgMS4xNTItMi41OTIgMi43MzYgMCAxLjYwMiAxLjA5OCAyLjczNiAyLjYyOCAyLjczNlptNi4yMDQtMS41MTJ2LTMuNjcyaC0xLjY5MnYtMi4wODhoMS42OTJWMzMuNjZoMi4zMDR2Mi4yNjhoMi43NzJ2Mi4wODhoLTIuNzcydjMuNjcyYzAgMS4wMDguNTQgMS40MDQgMS40NCAxLjQwNC42MyAwIDEuMDQ0LS4wNzIgMS4zNS0uMTk4djEuOTk4Yy0uNDUuMTk4LS45OS4yODgtMS43NDYuMjg4LTIuMjY4IDAtMy4zNDgtMS4yNzgtMy4zNDgtMy40OTJaJy8+PHBhdGggZmlsbD0nIzAwMDA5MScgZD0nTTQ2Ljk5MiAxOS4wOTggMzEuOTk4IDEwLjQybC0xNC45OTQgOC43NmEuNjA2LjYwNiAwIDAgMC0uMzA2LjUyNXYxNi45NDhhLjY2Ni42NjYgMCAwIDAgLjMwNi41MjRsMTQuOTkyIDguNiAxNC45OTQtOC43MDZhLjY2Ni42NjYgMCAwIDAgLjMwNi0uNTI0VjE5LjYyNmEuNjA0LjYwNCAwIDAgMC0uMzA0LS41MjhaJy8+PHBhdGggZmlsbD0nI0ZDQzYzQScgZD0nbTI2LjY0MSAxOS41OTgtNS4wMjkgOC42MjgtNC41NTctOS4xNzUgNS4zOS0zLjExMyA0LjQ4OSAzLjE2LS4yOTMuNVptMjAuNjU2IDE2Ljk4VjE5LjYyYS42LjYgMCAwIDAtLjMwNi0uNTIzTDMxLjk5OCAxMC40MicvPjxwYXRoIGZpbGw9JyMwMDYzQ0InIGQ9J00xNi43IDM2LjU3OCAzMiAxMC40MnYzNS4zNjJsLTE0Ljk5Ni04LjYwNWEuNjY1LjY2NSAwIDAgMS0uMzA2LS41MjRWMTkuNzA2bC4wMDIgMTYuODcyWm0yNC42NjktMjAuNzM1IDUuNDU4IDMuMTU1LTQuNDg5IDkuMTUtNS4zODctOS4yMzYgNC40MTgtMy4wN1onLz48cGF0aCBmaWxsPScjZmZmJyBkPSdtNTEuNjA2IDE2LjMwMy0xOS4xOS0xMS4wMmEuOTMzLjkzMyAwIDAgMC0uODMyIDBsLTE5LjE5IDExLjAyYS44ODcuODg3IDAgMCAwLS4zOTQuNjk1djIyYS44ODUuODg1IDAgMCAwIC4zOTQuN2wxOS4xODkgMTEuMDJhLjkzMi45MzIgMCAwIDAgLjgzMiAwbDE5LjE5MS0xMS4wMmEuODg2Ljg4NiAwIDAgMCAuMzk0LS43di0yMmEuODg3Ljg4NyAwIDAgMC0uMzk0LS42OTVaTTIyLjc4OSAzNC4wNTloLjA3OWMtLjA0MiAwLS4wNzkuMDA3LS4wNzkuMDUgMCAuMS4xNTEgMCAuMi4xYS45MTIuOTEyIDAgMCAwLS42MjkuMjc2YzAgLjA1LjEuMDUuMTUxLjA1LS4wNzUuMS0uMjI2LjA1LS4yNzcuMTUyYS4xNzYuMTc2IDAgMCAwIC4xLjA1Yy0uMDUgMC0uMSAwLS4xLjA1di4xNTJjLS4xMjYgMC0uMTc2LjEtLjI3Ny4xNS4yLjE1Mi4zMjcgMCAuNTI4IDAtLjUyOC4yLS45NTYuNDc5LTEuNDg0LjYzLS4xIDAgMCAuMTUtLjEuMTUuMTUxLjEuMjI3LS4wNS4zNzctLjA1LS42NTQuMzc4LTEuMzMzLjctMi4wMzcgMS4xMzNhLjM1MS4zNTEgMCAwIDAtLjEuMmgtLjJjLS4xLjA1LS4wNS4xNzYtLjE1MS4yNzcuMjI2LjE1LjUtLjIuNjU0IDAgLjA1IDAtLjEuMDUtLjIuMDUtLjA1IDAtLjA1LjEtLjEuMWgtLjE1NGMtLjEuMDc1LS4yLjEyNi0uMi4yNzZhLjIyLjIyIDAgMCAwLS4yMjYuMSA5LjAzMSA5LjAzMSAwIDAgMCAzLjE0NC0uNTc4IDcuNjgzIDcuNjgzIDAgMCAwIDIuMDg4LTEuNTYuMTc2LjE3NiAwIDAgMSAuMDUuMWMtLjE0Ny40MzctLjQzLjgxNi0uODA2IDEuMDgtLjI3Ny4xNTItLjQ3OC4zNzgtLjcuNDc5YTQuMDU3IDQuMDU3IDAgMCAwLS40MjguMjc2Yy0uNjMyLjE5Ny0xLjI4MS4zMzUtMS45MzkuNDEybC0uMzA1LjA0NGMtLjIyNS4wMzMtLjQ0OS4wNjktLjY3MS4xMDhsLTEuOTkzLTEuMTM4YS42NDcuNjQ3IDAgMCAxLS4yODgtLjQxMS41Ny41NyAwIDAgMCAuMDk0LS4wNjMuMjY2LjI2NiAwIDAgMC0uMTEzLS4wNzF2LS42NWExMi43ODIgMTIuNzgyIDAgMCAwIDMuMDM4LS45NDIgOC43NDYgOC43NDYgMCAwIDAtMy4wMzctMS4zNDN2LTEuNTE1YTExLjY3IDExLjY3IDAgMCAxIDEuNjM5LjM5MiA2LjQyIDYuNDIgMCAwIDEgMS4xODIuNTc4Yy4xNDcuMTQuMzA3LjI2Ny40NzguMzc3YS45MS45MSAwIDAgMCAuOC4wNWguMzNhMy45NjEgMy45NjEgMCAwIDAgMS45MzctLjkwNWMwIC4wNS4wNS4wNS4xLjA1YTMuNjI5IDMuNjI5IDAgMCAxLS40MjggMS4xMzJjLjAwMy4wNS0uMDQ4LjE1Mi4wNTMuMjAyWm0yLjgxNyAzLjU3Yy4yNTEtLjEuNC0uMjc2LjYyOS0uMzc2LS4wNS4wNS0uMDUuMTUtLjEuMmEzLjY5OSAzLjY5OSAwIDAgMC0uNTI4LjQgMTUuOTY1IDE1Ljk2NSAwIDAgMC0xLjU4NSAxLjYxYy0uMjUyLjMtLjUyOC41NzgtLjguODU1LS4wOTYuMDktLjIuMTcyLS4zMS4yNDVsLTIuNTI3LTEuNDVjLjM2LjAzLjcyMS4wMTMgMS4wNzYtLjA1My4yOTQtLjA4My41OC0uMTkyLjg1NS0uMzI3di4xYy43LS4yNzcgMS4yMzItLjkwNiAxLjkzNy0xLjEzMi4wMjUgMCAuMTI2LjEuMjI2LjA1YTEuODgzIDEuODgzIDAgMCAxIDEuNTA5LS43YzAgLjA1IDAgLjEuMDUuMWguMDI1Yy0uMTUxLjEyNi0uMzI3LjI1LS41LjM3Ny0uMDU3LjA1Mi0uMDA3LjEwMi4wNDMuMTAyWm0tOC45MDgtNi4xNjN2LS4xODZhNS44MTcgNS44MTcgMCAwIDEgMS41ODgtLjE4OCAxLjUyIDEuNTIgMCAwIDEgLjQ3OCAwIDUuODYgNS44NiAwIDAgMC0yLjA2Ni4zNzRabTMwLjYgNS4wODhhLjY2NS42NjUgMCAwIDEtLjMwNi41MjRsLTEwLjA3OSA1Ljg1YTMyLjI5NiAzMi4yOTYgMCAwIDEtMy40MDgtMS4xODQgMi44MjYgMi44MjYgMCAwIDEtLjA1LTIuMjQ1Yy4wOC0uMzA4LjE5OC0uNjA1LjM1Mi0uODgzLjAyNS0uMDI1LjA1LS4wNS4wNS0uMDc2YS4wMjUuMDI1IDAgMCAwIC4wMjUtLjAyNSA0LjMyIDQuMzIgMCAwIDEgLjM3Ny0uNTU1bC4wMTUtLjAxNS4wMi0uMDIxLjAxNS0uMDE1YzAtLjAyNS4wMjUtLjA1LjA1LS4wNzYuMDI1LS4wNTEuMDc1LS4wNzYuMS0uMTI2LjE3Ni0uMTg2LjM3LS4zNTQuNTc5LS41LjIxMy0uMDc3LjQzMS0uMTM2LjY1NC0uMTc3LjgxMS4wNiAxLjYxNy4xNyAyLjQxNS4zMjhhLjc1Mi43NTIgMCAwIDEgLjI3Ny4xYy4zMDEuMDU5LjYxMi4wNDEuOTA1LS4wNWExLjEzNyAxLjEzNyAwIDAgMCAuODU1LS43MDYgMS4yMTIgMS4yMTIgMCAwIDAgLjA1LTEuMDZjLS4xNzgtLjI3NS0uMDEzLS40MzYuMTgxLS41OWwuMDY4LS4wNTRjLjA4Ni0uMDYxLjE2NC0uMTM0LjIzMS0uMjE2LjEyNi0uMjUyLS4xLS40LS4xNTEtLjYzLS4wNS0uMS0uMjI2LS4wNS0uMzI3LS4yLjM1Mi0uMTUxLjg1NS0uNDMuNjI5LS44NTctLjE1MS0uMjI3LS4zNzctLjYzLS4xLS44NTcuMzUyLS4yLjg1NS0uMTUxIDEuMDA2LS40OGExLjEzNyAxLjEzNyAwIDAgMC0uMjkyLTEuMDg0bC0uMDc1LS4xMDhhNC43NTQgNC43NTQgMCAwIDEtLjIxMS0uMzIgNi45MDUgNi45MDUgMCAwIDAtLjUyOC0uNzU3IDQuMjk3IDQuMjk3IDAgMCAxLS41MjgtMS4wMWMtLjE1MS0uMzc3LjA1LS43MDUuMDUtMS4wODNhNi4zNDcgNi4zNDcgMCAwIDAtLjMyNy0yLjE0NGMtLjEyNi0uMzUzLS4xNzYtLjczMS0uMzI3LTEuMDZhMS4xMiAxLjEyIDAgMCAwLS4yMjYtLjU4LjM3NC4zNzQgMCAwIDEgMC0uMzI3Yy4yMDUtLjE0NS4zOTktLjMwNS41NzktLjQ4YS41NjcuNTY3IDAgMCAwLS4yLS43MDVjLS4zMjctLjE1MS0uMy4zMjgtLjUyOC40MjloLS4xNTFjLS4wNS0uMTI2LjA1LS4xNzcuMTUxLS4yNzcgMC0uMDUgMC0uMTUxLS4wNS0uMTUxLS4yIDAtLjM3Ny0uMDUxLS40MjgtLjE1MWEzLjk1NyAzLjk1NyAwIDAgMC0xLjg2MS0xLjI4NmMuMTg4LjA1OC4zODIuMDkxLjU3OS4xLjMzOC4wNzEuNjkuMDM2IDEuMDA2LS4xLjIyNy0uMDc2LjI3Ny0uNDguMzc3LS43MDZhLjguOCAwIDAgMC0uMTUxLS42MzEgMi4xOSAyLjE5IDAgMCAwLS45MDYtLjc1NiA5LjEzIDkuMTMgMCAwIDEtLjY3OS0uMzUzLjk1Ni45NTYgMCAwIDAtLjI1MS0uMTI2Yy0yLjk2NS0xLjQ4NS05LjA2OS0uMi05LjUzNCAwaC0uMDA5YTguMjU0IDguMjU0IDAgMCAwLTEuMjQ5LjQ3NSAzLjkyMiAzLjkyMiAwIDAgMC0yLjM2NSAyLjQ2NSAzLjgzIDMuODMgMCAwIDAtMS4zMzMgMS41MDljLS40MjguOC0xLjA1NiAxLjUwOS0uOTU2IDIuNDE0LjEuNzguMjc3IDEuNDg0LjQyOCAyLjI4OS4wNDMuMjcyLjExLjU0LjIuOC4xLjI3NiAwIC42MjkuMTUxLjg1NS4wNzUuMTUuMDI1LjMyNy4yMjcuNDI4di4yYy4wNS4wNS4wNS4xLjE1MS4xdi4yYy40MzUuNDIzLjgwNy45MDYgMS4xMDcgMS40MzQuMS4yNzYtLjQ3OC4xNS0uNy4wNWE1Ljk3NyA1Ljk3NyAwIDAgMS0xLjEzMi0uOTU2LjE3Ni4xNzYgMCAwIDAtLjA1MS4xYy4yLjM1Mi45MDYuNzguNTI4IDEuMDA2LS4yLjEtLjQyOC0uMTUxLS42MjkuMDUtLjA1LjA3NiAwIC4xNzcgMCAuMjc3LS4yNzctLjItLjU3OC0uMS0uODU1LS4yLS4yLS4wNS0uMjUyLS40MjctLjQ3OC0uNDI3YTE1LjE5MSAxNS4xOTEgMCAwIDAtMS44MTEtLjMyNyAxNS4xNDQgMTUuMTQ0IDAgMCAwLTEuNzM5LS4xNlYxOS43MDdhLjYwNi42MDYgMCAwIDEgLjMwNi0uNTI0bDE0Ljk4Ny04Ljc2MSAxNC45OTQgOC42NzdhLjYwNS42MDUgMCAwIDEgLjMwNi41MjR2MTYuOTMyWm0tNy45NTQtOC4yNjFhLjMyNS4zMjUgMCAwIDEtLjI4Mi4xNDkgMi44NCAyLjg0IDAgMCAwLS4yODIuMjczYy4xIDAgMCAuMTQ5LjEuMTQ5LS4yMDUuMjIzLjA3Ny42OTQtLjIwNS43OTMtLjM3LjA5OS0uNzU4LjA5OS0xLjEyNyAwYS43MjcuNzI3IDAgMCAxIC4xNjctLjAxNmguMDg1YS4zODIuMzgyIDAgMCAwIC4zMzctLjEzMnYtLjJjMC0uMDUtLjA1MS0uMDUtLjEtLjA1YS4xNi4xNiAwIDAgMS0uMS4wNS4yMjMuMjIzIDAgMCAwLS4xNTQtLjIuODA2LjgwNiAwIDAgMS0uNzE4LS4yNzMuNjcuNjcgMCAwIDEgLjQzNi0uMDVjLjEyOCAwIC4wNzctLjIyMy4yMzEtLjMyMmguMTU0Yy4zMDctLjM3Mi44NzEtLjQ3MS45NzQtLjg0MyAwLS4xLS4yODItLjEtLjQ4Ny0uMTVhMi4yNiAyLjI2IDAgMCAwLS44Mi4wNWMtLjM2LjA1LS43MTIuMTQyLTEuMDUxLjI3NC4yOC0uMjA2LjU5Mi0uMzY1LjkyMy0uNDcxLjIzMi0uMDkuNDczLS4xNTcuNzE4LS4ybC4xMzItLjAyNi4xMzMtLjAyN2EuOTcuOTcgMCAwIDEgLjU1NiAwYy4yMzEuMS42MTUuMS42NjYuMjQ4LjEuMjczLS4xNTQuNTQ1LS40MzUuNzQ0LS4wNTcuMDguMTQ5LjEzNS4xNDkuMjNaJy8+PHJlY3Qgd2lkdGg9JzI5LjU2JyBoZWlnaHQ9JzEzLjMwMicgeD0nMzcnIHk9JzUnIGZpbGw9JyNGQ0M2M0EnIHJ4PScyJy8+PHBhdGggZmlsbD0nIzE2MTYxNicgZD0nTTM5LjU2MiAxNi4xNjhWNy4zMTZoMi45MjFjLjk3IDAgMS43MzIuMjM2IDIuMjg5LjcwOC41NjUuNDcyLjg0NyAxLjExNy44NDcgMS45MzUgMCAuODEtLjI4MiAxLjQ1LS44NDcgMS45MjItLjU1Ny40NzItMS4zMi43MDgtMi4yODkuNzA4aC0xLjEyNXYzLjU3OWgtMS43OTZabTIuOTk3LTcuMzIyaC0xLjIwMXYyLjIxM2gxLjJjLjM4IDAgLjY3NS0uMDk3Ljg4Ni0uMjkuMjItLjE5NS4zMjktLjQ3My4zMjktLjgzNiAwLS4zMzctLjExLS42MDItLjMyOS0uNzk2LS4yMS0uMTk0LS41MDYtLjI5MS0uODg1LS4yOTFaTTQ3LjIzIDE2LjE2OFY3LjMxNmgyLjcwN2MuOTcgMCAxLjczNi4yMzYgMi4zMDEuNzA4LjU2NS40NzIuODQ3IDEuMTE3Ljg0NyAxLjkzNSAwIC41My0uMTI2Ljk5NS0uMzc5IDEuMzktLjI0NC4zODktLjU5LjY4OC0xLjAzNy44OTlsMi43ODIgMy45MmgtMi4xNWwtMi4zNTItMy41NzloLS45MjN2My41NzloLTEuNzk1Wm0yLjgwOC03LjMyMmgtMS4wMTJ2Mi4yMTNoMS4wMTJjLjM4IDAgLjY3NC0uMDk3Ljg4NS0uMjkuMjEtLjE5NS4zMTYtLjQ3My4zMTYtLjgzNiAwLS4zMzctLjEwNS0uNjAyLS4zMTYtLjc5Ni0uMjEtLjE5NC0uNTA2LS4yOTEtLjg4NS0uMjkxWk01OS41NDkgNy4wNjNjLjY5IDAgMS4zMjMuMTI2IDEuODk2LjM4LjU4Mi4yNTIgMS4wOC41OSAxLjQ5MiAxLjAxMS40MTQuNDIxLjczNC45MTkuOTYyIDEuNDkyLjIyNy41NjUuMzQxIDEuMTY0LjM0MSAxLjc5NiAwIC42MzItLjExNCAxLjIzNS0uMzQxIDEuODA4YTQuNDg1IDQuNDg1IDAgMCAxLS45NjIgMS40OGMtLjQxMy40MjEtLjkxLjc1OC0xLjQ5MiAxLjAxMWE0LjY0OCA0LjY0OCAwIDAgMS0xLjg5Ni4zOCA0LjczOCA0LjczOCAwIDAgMS0zLjQwMi0xLjM5MSA0LjQ4NCA0LjQ4NCAwIDAgMS0uOTYxLTEuNDggNC44NTUgNC44NTUgMCAwIDEtLjM0Mi0xLjgwOGMwLS42MzMuMTE0LTEuMjMxLjM0Mi0xLjc5Ni4yMjctLjU3My41NDgtMS4wNy45NjEtMS40OTIuNDEzLS40MjIuOTEtLjc1OSAxLjQ5Mi0xLjAxMmE0LjczNyA0LjczNyAwIDAgMSAxLjkxLS4zNzlabTAgNy42NzZhMi44IDIuOCAwIDAgMCAxLjEzOC0uMjI4Yy4zNTQtLjE2LjY1My0uMzcuODk4LS42MzIuMjUyLS4yNy40NS0uNTg2LjU5NC0uOTQ5YTMuMjcgMy4yNyAwIDAgMCAuMjE1LTEuMTg4IDMuMTcgMy4xNyAwIDAgMC0uMjE1LTEuMTc2IDIuNzkxIDIuNzkxIDAgMCAwLS41OTUtLjk0OSAyLjU0OCAyLjU0OCAwIDAgMC0uODk3LS42MzIgMi42NzMgMi42NzMgMCAwIDAtMS4xMzgtLjI0Yy0uNDEzIDAtLjc5Ny4wOC0xLjE1MS4yNGEyLjY3OCAyLjY3OCAwIDAgMC0uOTEuNjMyIDIuODk5IDIuODk5IDAgMCAwLS41ODIuOTQ5IDMuMTcgMy4xNyAwIDAgMC0uMjE1IDEuMTc2YzAgLjQyMS4wNzEuODE3LjIxNSAxLjE4OC4xNDMuMzYzLjMzNy42NzkuNTgxLjk0OS4yNTMuMjYxLjU1Ny40NzIuOTEuNjMyLjM1NS4xNTIuNzM5LjIyOCAxLjE1Mi4yMjhaJy8+PC9zdmc+"); + background-position: 50% 50%; + background-repeat: no-repeat; + width: 214px; + height: 56px; + border: none; + } + + .proconnect-button:hover { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMTEnIGhlaWdodD0nNTgnIGZpbGw9J25vbmUnPjxnIGNsaXAtcGF0aD0ndXJsKCNhKSc+PHBhdGggZmlsbD0nIzEyMTJGRicgZD0nTTIxMSAwSDB2NThoMjExVjBaJy8+PHBhdGggZmlsbD0nI2ZmZicgZD0nbTY5Ljk4NiAyNi4zNjggMS4xNTYtMS4wNzFjLjgzMyAxLjA1NCAxLjgxOSAxLjU5OCAyLjk0MSAxLjU5OCAxLjI5MiAwIDIuMDQtLjgxNiAyLjA0LTEuOTA0IDAtMi41NS01LjYyNy0yLjI0NC01LjYyNy02LjAzNSAwLTEuNzM0IDEuNDI4LTMuMTk2IDMuNDUxLTMuMTk2IDEuNjgzIDAgMi45MDcuNzY1IDMuNzkxIDEuOTM4bC0xLjE5IDEuMDM3Yy0uNjk3LTEuMDAzLTEuNTQ3LTEuNTQ3LTIuNTg0LTEuNTQ3LTEuMTA1IDAtMS44MzYuNzQ4LTEuODM2IDEuNzM0IDAgMi41NjcgNS42MjcgMi4yNDQgNS42MjcgNi4wNTIgMCAyLjAyMy0xLjU4MSAzLjM0OS0zLjY1NSAzLjM0OS0xLjc2OCAwLTMuMDc3LS42NjMtNC4xMTQtMS45NTVabTEwLjgxNy01LjcxMkg3OS40NmwxLjQ0NS00LjU1NmgxLjY0OWwtMS43NTEgNC41NTZabTQuODE4LTMuNDUxYy0uNTYgMC0xLjAyLS40NTktMS4wMi0xLjAyYTEuMDIgMS4wMiAwIDAgMSAxLjAyLTEuMDAzYy41NjEgMCAxLjAwMy40NTkgMS4wMDMgMS4wMDMgMCAuNTYxLS40NDIgMS4wMi0xLjAwMyAxLjAyWk04NC44OTEgMjh2LTguNTY4aDEuNDQ0VjI4SDg0Ljg5Wm0zLjc2Ny00LjI4NGMwLTIuNDk5IDEuNzE3LTQuNjI0IDQuNDAzLTQuNjI0IDEuMjQxIDAgMi4yNjEuNDU5IDMuMDQzIDEuMjkyVjE1LjI1aDEuNDQ1VjI4aC0xLjQ0NXYtLjk1MmMtLjc4Mi44MzMtMS44MDIgMS4yOTItMy4wNDMgMS4yOTItMi42ODYgMC00LjQwMy0yLjEyNS00LjQwMy00LjYyNFptMS41MyAwYzAgMS44MTkgMS4yMjQgMy4yNjQgMy4wNDMgMy4yNjQgMS4xOSAwIDIuMjEtLjU3OCAyLjg3My0xLjU5OFYyMi4wNWMtLjY4LTEuMDM3LTEuNy0xLjU5OC0yLjg3My0xLjU5OC0xLjgxOSAwLTMuMDQzIDEuNDQ1LTMuMDQzIDMuMjY0Wm0xOC4wMjMgMi44NzNjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xMTAuNDczIDI4di04LjU2OGgxLjQ0NXYuOTY5Yy42OTctLjc2NSAxLjU4MS0xLjMwOSAyLjg1Ni0xLjMwOSAxLjkyMSAwIDMuMzQ5IDEuMjkyIDMuMzQ5IDMuNzIzVjI4aC0xLjQ2MnYtNS4xMzRjMC0xLjUzLS44NS0yLjQxNC0yLjE3Ni0yLjQxNC0xLjI0MSAwLTIuMDIzLjcxNC0yLjU2NyAxLjYxNVYyOGgtMS40NDVabTExLjA1Mi0yLjg3M3YtNC4zNjloLTEuNjE1di0xLjMyNmgxLjYxNVYxNy4yOWgxLjQ2MnYyLjE0MmgyLjk3NXYxLjMyNmgtMi45NzV2NC4zNjljMCAxLjM0My42OCAxLjcxNyAxLjcxNyAxLjcxNy41NjEgMCAuOTUyLS4wNjggMS4yNzUtLjIwNHYxLjI5MmMtLjQwOC4xNy0uODY3LjIzOC0xLjQ3OS4yMzgtMS45MDQgMC0yLjk3NS0uOTUyLTIuOTc1LTMuMDQzWm03LjM3Ny03LjkyMmMtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEyOC4xNzEgMjh2LTguNTY4aDEuNDQ1VjI4aC0xLjQ0NVptMy4zNzctOC41NjhoMS42MTV2LTEuMDU0YzAtMS44MzYgMS4yMDctMy4xMjggMy4wNDMtMy4xMjguOTUyIDAgMS43LjM0IDIuMjEuODMzbC0uOTAxIDEuMDU0YTEuNjMzIDEuNjMzIDAgMCAwLTEuMjkyLS41NzhjLS45MzUgMC0xLjU5OC42OC0xLjU5OCAxLjc4NXYxLjA4OGgyLjk3NXYxLjMyNmgtMi45NzVWMjhoLTEuNDYydi03LjI0MmgtMS42MTV2LTEuMzI2Wm04LjU0My0yLjIyN2MtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEzOS4zNiAyOHYtOC41NjhoMS40NDVWMjhoLTEuNDQ1Wm0xMi4xMTUtMS40MTFjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xNTMuNzM3IDI4di04LjU2OGgxLjQ0NXYxLjA3MWMuNjI5LS43NDggMS40MTEtMS4yNDEgMi40OTktMS4yNDEuMjcyIDAgLjUyNy4wMzQuNzMxLjEwMnYxLjQ5NmEzLjEwNSAzLjEwNSAwIDAgMC0uODUtLjExOWMtMS4xMjIgMC0xLjg1My41NzgtMi4zOCAxLjQ0NVYyOGgtMS40NDVabTEzLjY4NS4zNGMtMS42ODMgMC0yLjgyMi0uOTUyLTIuODIyLTIuNDQ4IDAtMS4zMjYuOTg2LTIuMjc4IDIuODIyLTIuNTY3bDIuODczLS40NzZ2LS41OTVjMC0xLjE5LS44NS0xLjg3LTIuMDU3LTEuODctMS4wMDMgMC0xLjgzNi40NDItMi4zMjkgMS4xOWwtMS4wODgtLjgzM2MuNzQ4LTEuMDIgMS45NTUtMS42NDkgMy40NTEtMS42NDkgMi4xNzYgMCAzLjQ2OCAxLjI3NSAzLjQ2OCAzLjE2MlYyOGgtMS40NDV2LTEuMDg4Yy0uNjQ2LjkwMS0xLjcxNyAxLjQyOC0yLjg3MyAxLjQyOFptLTEuMzc3LTIuNDk5YzAgLjczMS42MjkgMS4yOTIgMS42MTUgMS4yOTIgMS4xMzkgMCAyLjA0LS41OTUgMi42MzUtMS41ODFWMjMuOTJsLTIuNTMzLjQ0MmMtMS4xOS4xODctMS43MTcuNzMxLTEuNzE3IDEuNDc5Wm03LjI1Mi02LjQwOWgxLjU2NGwyLjczNyA3LjA1NSAyLjczNy03LjA1NWgxLjU2NEwxNzguNTUgMjhoLTEuOTA0bC0zLjM0OS04LjU2OFptMTcuODU2IDcuMTU3Yy0uNzk5IDEuMDcxLTIuMDc0IDEuNzUxLTMuNjcyIDEuNzUxLTIuODkgMC00LjY3NS0yLjEyNS00LjY3NS00LjYyNCAwLTIuNjAxIDEuNjY2LTQuNjI0IDQuMzE4LTQuNjI0IDIuMzI5IDAgMy44NDIgMS41ODEgMy44NDIgMy43MjMgMCAuMzQtLjA1MS42OC0uMTAyLjkxOGgtNi41NjJ2LjAzNGMwIDEuODg3IDEuMjkyIDMuMjY0IDMuMjEzIDMuMjY0IDEuMDg4IDAgMi4wMDYtLjUxIDIuNTY3LTEuMjc1bDEuMDcxLjgzM1ptLTQuMDEyLTYuMjU2Yy0xLjM5NCAwLTIuMzguNzgyLTIuNzIgMi4yNjFoNS4wODNjLS4wNTEtMS4yNDEtLjk1Mi0yLjI2MS0yLjM2My0yLjI2MVptMTAuMTg1IDYuNjQ3YzEuMDU0IDAgMS45MDQtLjUxIDIuNDMxLTEuMjc1bDEuMTU2Ljg4NGMtLjc5OSAxLjA3MS0yLjA0IDEuNzUxLTMuNjA0IDEuNzUxLTIuODM5IDAtNC42NTgtMi4xMjUtNC42NTgtNC42MjQgMC0yLjQ5OSAxLjgxOS00LjYyNCA0LjY1OC00LjYyNCAxLjU0NyAwIDIuODA1LjY5NyAzLjYwNCAxLjc1MWwtMS4xNTYuODg0YTIuOTI1IDIuOTI1IDAgMCAwLTIuNDQ4LTEuMjc1Yy0xLjgzNiAwLTMuMTQ1IDEuNDQ1LTMuMTQ1IDMuMjY0IDAgMS44MzYgMS4zMDkgMy4yNjQgMy4xNjIgMy4yNjRaTTcwLjg1NCA0NVYzMi40aDQuMTU4YzIuNzcyIDAgNC40NjQgMS40MjIgNC40NjQgMy43NjIgMCAyLjMyMi0xLjY5MiAzLjc0NC00LjQ2NCAzLjc0NEg3My40MVY0NWgtMi41NTZabTQuMjY2LTEwLjQyMmgtMS43MXYzLjE1aDEuNzFjMS4wOCAwIDEuNzI4LS41NzYgMS43MjgtMS42MDIgMC0uOTU0LS42NDgtMS41NDgtMS43MjgtMS41NDhaTTgxLjI0OSA0NXYtOS4wNzJoMi4yODZ2LjljLjU5NC0uNjEyIDEuMzY4LTEuMDggMi4zOTQtMS4wOC4zMDYgMCAuNTc2LjA1NC43OTIuMTI2djIuMzk0YTMuOTM4IDMuOTM4IDAgMCAwLTEuMDA4LS4xMjZjLTEuMTE2IDAtMS44MzYuNjEyLTIuMTc4IDEuMTdWNDVoLTIuMjg2Wm0xMS4zODYtOS40MzJjMi45NTIgMCA0Ljk2OCAyLjE3OCA0Ljk2OCA0Ljg5NnMtMi4wMTYgNC44OTYtNC45NjggNC44OTYtNC45NjgtMi4xNzgtNC45NjgtNC44OTYgMi4wMTYtNC44OTYgNC45NjgtNC44OTZabS4wMzYgNy42MzJjMS40NTggMCAyLjU1Ni0xLjE3IDIuNTU2LTIuNzM2IDAtMS41ODQtMS4wOTgtMi43MzYtMi41NTYtMi43MzYtMS41MTIgMC0yLjYyOCAxLjE1Mi0yLjYyOCAyLjczNiAwIDEuNTg0IDEuMTE2IDIuNzM2IDIuNjI4IDIuNzM2Wm0xMy4xNzItLjIzNGMxLjQ0IDAgMi41NzQtLjcwMiAzLjI5NC0xLjcyOGwyLjAxNiAxLjU0OGMtMS4xNTIgMS41NjYtMy4wMjQgMi41NzQtNS4zMSAyLjU3NC0zLjk3OCAwLTYuNjk2LTMuMDYtNi42OTYtNi42NnMyLjcxOC02LjY2IDYuNjk2LTYuNjZjMi4yODYgMCA0LjE1OCAxLjAyNiA1LjMxIDIuNTU2bC0yLjAxNiAxLjU2NmMtLjcyLTEuMDI2LTEuODU0LTEuNzI4LTMuMjk0LTEuNzI4LTIuMzc2IDAtNC4wNjggMS44NTQtNC4wNjggNC4yNjZzMS42OTIgNC4yNjYgNC4wNjggNC4yNjZabTExLjM2Ni03LjM5OGMyLjk1MiAwIDQuOTY4IDIuMTc4IDQuOTY4IDQuODk2cy0yLjAxNiA0Ljg5Ni00Ljk2OCA0Ljg5Ni00Ljk2OC0yLjE3OC00Ljk2OC00Ljg5NiAyLjAxNi00Ljg5NiA0Ljk2OC00Ljg5NlptLjAzNiA3LjYzMmMxLjQ1OCAwIDIuNTU2LTEuMTcgMi41NTYtMi43MzYgMC0xLjU4NC0xLjA5OC0yLjczNi0yLjU1Ni0yLjczNi0xLjUxMiAwLTIuNjI4IDEuMTUyLTIuNjI4IDIuNzM2IDAgMS41ODQgMS4xMTYgMi43MzYgMi42MjggMi43MzZabTcuMDE4IDEuOHYtOS4wNzJoMi4yODZ2LjcyYy42My0uNjEyIDEuNDc2LTEuMDggMi42ODItMS4wOCAxLjk2MiAwIDMuNTI4IDEuMzUgMy41MjggNC4wMzJWNDVoLTIuMzIydi01LjMxYzAtMS4yMDYtLjY2Ni0xLjk2Mi0xLjc4Mi0xLjk2Mi0xLjE1MiAwLTEuNzY0Ljc3NC0yLjEwNiAxLjM1VjQ1aC0yLjI4NlptMTEuMDkxIDB2LTkuMDcyaDIuMjg2di43MmMuNjMtLjYxMiAxLjQ3Ni0xLjA4IDIuNjgyLTEuMDggMS45NjIgMCAzLjUyOCAxLjM1IDMuNTI4IDQuMDMyVjQ1aC0yLjMyMnYtNS4zMWMwLTEuMjA2LS42NjYtMS45NjItMS43ODItMS45NjItMS4xNTIgMC0xLjc2NC43NzQtMi4xMDYgMS4zNVY0NWgtMi4yODZabTE5LjQ0NC0xLjQ3NmMtLjg0NiAxLjEzNC0yLjI1IDEuODM2LTMuOTYgMS44MzYtMy4yMjIgMC01LjA0LTIuMjUtNS4wNC00Ljg5NiAwLTIuNjgyIDEuNjkyLTQuODk2IDQuNjYyLTQuODk2IDIuNTIgMCA0LjE3NiAxLjY5MiA0LjE3NiA0LjA2OCAwIC41MDQtLjA3Mi45OS0uMTQ0IDEuMjk2aC02LjM1NGMuMTQ0IDEuNDk0IDEuMTg4IDIuMzc2IDIuNzM2IDIuMzc2Ljk5IDAgMS44LS40MzIgMi4yODYtMS4wOGwxLjYzOCAxLjI5NlptLTQuMzM4LTYuMDQ4Yy0xLjExNiAwLTEuODcyLjU0LTIuMTc4IDEuNzI4aDQuMDg2Yy0uMDM2LS45LS43MDItMS43MjgtMS45MDgtMS43MjhabTEwLjY5NiA1LjcyNGMuODgyIDAgMS41ODQtLjQzMiAyLjAxNi0xLjA2MmwxLjgxOCAxLjM4NmMtLjg0NiAxLjExNi0yLjE3OCAxLjgzNi0zLjgzNCAxLjgzNi0zLjEzMiAwLTUuMDA0LTIuMjUtNS4wMDQtNC44OTZzMS44NzItNC44OTYgNS4wMDQtNC44OTZjMS42NTYgMCAyLjk4OC43MiAzLjgzNCAxLjgzNmwtMS44MTggMS4zODZjLS40MzItLjYzLTEuMTE2LTEuMDYyLTIuMDUyLTEuMDYyLTEuNDk0IDAtMi41OTIgMS4xNTItMi41OTIgMi43MzYgMCAxLjYwMiAxLjA5OCAyLjczNiAyLjYyOCAyLjczNlptNi4yMDQtMS41MTJ2LTMuNjcyaC0xLjY5MnYtMi4wODhoMS42OTJWMzMuNjZoMi4zMDR2Mi4yNjhoMi43NzJ2Mi4wODhoLTIuNzcydjMuNjcyYzAgMS4wMDguNTQgMS40MDQgMS40NCAxLjQwNC42MyAwIDEuMDQ0LS4wNzIgMS4zNS0uMTk4djEuOTk4Yy0uNDUuMTk4LS45OS4yODgtMS43NDYuMjg4LTIuMjY4IDAtMy4zNDgtMS4yNzgtMy4zNDgtMy40OTJaJy8+PHBhdGggZmlsbD0nIzAwMDA5MScgZD0nTTQ2Ljk5MiAxOS4wOTggMzEuOTk4IDEwLjQybC0xNC45OTQgOC43NmEuNjA2LjYwNiAwIDAgMC0uMzA2LjUyNXYxNi45NDhhLjY2Ni42NjYgMCAwIDAgLjMwNi41MjRsMTQuOTkyIDguNiAxNC45OTQtOC43MDZhLjY2Ni42NjYgMCAwIDAgLjMwNi0uNTI0VjE5LjYyNmEuNjA0LjYwNCAwIDAgMC0uMzA0LS41MjhaJy8+PHBhdGggZmlsbD0nI0ZDQzYzQScgZD0nbTI2LjY0MSAxOS41OTgtNS4wMjkgOC42MjgtNC41NTctOS4xNzUgNS4zOS0zLjExMyA0LjQ4OSAzLjE2LS4yOTMuNVptMjAuNjU2IDE2Ljk4VjE5LjYyYS42LjYgMCAwIDAtLjMwNi0uNTIzTDMxLjk5OCAxMC40MicvPjxwYXRoIGZpbGw9JyMwMDYzQ0InIGQ9J00xNi43IDM2LjU3OCAzMiAxMC40MnYzNS4zNjJsLTE0Ljk5Ni04LjYwNWEuNjY1LjY2NSAwIDAgMS0uMzA2LS41MjRWMTkuNzA2bC4wMDIgMTYuODcyWm0yNC42NjktMjAuNzM1IDUuNDU4IDMuMTU1LTQuNDg5IDkuMTUtNS4zODctOS4yMzYgNC40MTgtMy4wN1onLz48cGF0aCBmaWxsPScjZmZmJyBkPSdtNTEuNjA2IDE2LjMwMy0xOS4xOS0xMS4wMmEuOTMzLjkzMyAwIDAgMC0uODMyIDBsLTE5LjE5IDExLjAyYS44ODcuODg3IDAgMCAwLS4zOTQuNjk1djIyYS44ODUuODg1IDAgMCAwIC4zOTQuN2wxOS4xODkgMTEuMDJhLjkzMi45MzIgMCAwIDAgLjgzMiAwbDE5LjE5MS0xMS4wMmEuODg2Ljg4NiAwIDAgMCAuMzk0LS43di0yMmEuODg3Ljg4NyAwIDAgMC0uMzk0LS42OTVaTTIyLjc4OSAzNC4wNTloLjA3OWMtLjA0MiAwLS4wNzkuMDA3LS4wNzkuMDUgMCAuMS4xNTEgMCAuMi4xYS45MTIuOTEyIDAgMCAwLS42MjkuMjc2YzAgLjA1LjEuMDUuMTUxLjA1LS4wNzUuMS0uMjI2LjA1LS4yNzcuMTUyYS4xNzYuMTc2IDAgMCAwIC4xLjA1Yy0uMDUgMC0uMSAwLS4xLjA1di4xNTJjLS4xMjYgMC0uMTc2LjEtLjI3Ny4xNS4yLjE1Mi4zMjcgMCAuNTI4IDAtLjUyOC4yLS45NTYuNDc5LTEuNDg0LjYzLS4xIDAgMCAuMTUtLjEuMTUuMTUxLjEuMjI3LS4wNS4zNzctLjA1LS42NTQuMzc4LTEuMzMzLjctMi4wMzcgMS4xMzNhLjM1MS4zNTEgMCAwIDAtLjEuMmgtLjJjLS4xLjA1LS4wNS4xNzYtLjE1MS4yNzcuMjI2LjE1LjUtLjIuNjU0IDAgLjA1IDAtLjEuMDUtLjIuMDUtLjA1IDAtLjA1LjEtLjEuMWgtLjE1NGMtLjEuMDc1LS4yLjEyNi0uMi4yNzZhLjIyLjIyIDAgMCAwLS4yMjYuMSA5LjAzMSA5LjAzMSAwIDAgMCAzLjE0NC0uNTc4IDcuNjgzIDcuNjgzIDAgMCAwIDIuMDg4LTEuNTYuMTc2LjE3NiAwIDAgMSAuMDUuMWMtLjE0Ny40MzctLjQzLjgxNi0uODA2IDEuMDgtLjI3Ny4xNTItLjQ3OC4zNzgtLjcuNDc5YTQuMDU3IDQuMDU3IDAgMCAwLS40MjguMjc2Yy0uNjMyLjE5Ny0xLjI4MS4zMzUtMS45MzkuNDEybC0uMzA1LjA0NGMtLjIyNS4wMzMtLjQ0OS4wNjktLjY3MS4xMDhsLTEuOTkzLTEuMTM4YS42NDcuNjQ3IDAgMCAxLS4yODgtLjQxMS41Ny41NyAwIDAgMCAuMDk0LS4wNjMuMjY2LjI2NiAwIDAgMC0uMTEzLS4wNzF2LS42NWExMi43ODIgMTIuNzgyIDAgMCAwIDMuMDM4LS45NDIgOC43NDYgOC43NDYgMCAwIDAtMy4wMzctMS4zNDN2LTEuNTE1YTExLjY3IDExLjY3IDAgMCAxIDEuNjM5LjM5MiA2LjQyIDYuNDIgMCAwIDEgMS4xODIuNTc4Yy4xNDcuMTQuMzA3LjI2Ny40NzguMzc3YS45MS45MSAwIDAgMCAuOC4wNWguMzNhMy45NjEgMy45NjEgMCAwIDAgMS45MzctLjkwNWMwIC4wNS4wNS4wNS4xLjA1YTMuNjI5IDMuNjI5IDAgMCAxLS40MjggMS4xMzJjLjAwMy4wNS0uMDQ4LjE1Mi4wNTMuMjAyWm0yLjgxNyAzLjU3Yy4yNTEtLjEuNC0uMjc2LjYyOS0uMzc2LS4wNS4wNS0uMDUuMTUtLjEuMmEzLjY5OSAzLjY5OSAwIDAgMC0uNTI4LjQgMTUuOTY1IDE1Ljk2NSAwIDAgMC0xLjU4NSAxLjYxYy0uMjUyLjMtLjUyOC41NzgtLjguODU1LS4wOTYuMDktLjIuMTcyLS4zMS4yNDVsLTIuNTI3LTEuNDVjLjM2LjAzLjcyMS4wMTMgMS4wNzYtLjA1My4yOTQtLjA4My41OC0uMTkyLjg1NS0uMzI3di4xYy43LS4yNzcgMS4yMzItLjkwNiAxLjkzNy0xLjEzMi4wMjUgMCAuMTI2LjEuMjI2LjA1YTEuODgzIDEuODgzIDAgMCAxIDEuNTA5LS43YzAgLjA1IDAgLjEuMDUuMWguMDI1Yy0uMTUxLjEyNi0uMzI3LjI1LS41LjM3Ny0uMDU3LjA1Mi0uMDA3LjEwMi4wNDMuMTAyWm0tOC45MDgtNi4xNjN2LS4xODZhNS44MTcgNS44MTcgMCAwIDEgMS41ODgtLjE4OCAxLjUyIDEuNTIgMCAwIDEgLjQ3OCAwIDUuODYgNS44NiAwIDAgMC0yLjA2Ni4zNzRabTMwLjYgNS4wODhhLjY2NS42NjUgMCAwIDEtLjMwNi41MjRsLTEwLjA3OSA1Ljg1YTMyLjI5NiAzMi4yOTYgMCAwIDEtMy40MDgtMS4xODQgMi44MjYgMi44MjYgMCAwIDEtLjA1LTIuMjQ1Yy4wOC0uMzA4LjE5OC0uNjA1LjM1Mi0uODgzLjAyNS0uMDI1LjA1LS4wNS4wNS0uMDc2YS4wMjUuMDI1IDAgMCAwIC4wMjUtLjAyNSA0LjMyIDQuMzIgMCAwIDEgLjM3Ny0uNTU1bC4wMTUtLjAxNS4wMi0uMDIxLjAxNS0uMDE1YzAtLjAyNS4wMjUtLjA1LjA1LS4wNzYuMDI1LS4wNTEuMDc1LS4wNzYuMS0uMTI2LjE3Ni0uMTg2LjM3LS4zNTQuNTc5LS41LjIxMy0uMDc3LjQzMS0uMTM2LjY1NC0uMTc3LjgxMS4wNiAxLjYxNy4xNyAyLjQxNS4zMjhhLjc1Mi43NTIgMCAwIDEgLjI3Ny4xYy4zMDEuMDU5LjYxMi4wNDEuOTA1LS4wNWExLjEzNyAxLjEzNyAwIDAgMCAuODU1LS43MDYgMS4yMTIgMS4yMTIgMCAwIDAgLjA1LTEuMDZjLS4xNzgtLjI3NS0uMDEzLS40MzYuMTgxLS41OWwuMDY4LS4wNTRjLjA4Ni0uMDYxLjE2NC0uMTM0LjIzMS0uMjE2LjEyNi0uMjUyLS4xLS40LS4xNTEtLjYzLS4wNS0uMS0uMjI2LS4wNS0uMzI3LS4yLjM1Mi0uMTUxLjg1NS0uNDMuNjI5LS44NTctLjE1MS0uMjI3LS4zNzctLjYzLS4xLS44NTcuMzUyLS4yLjg1NS0uMTUxIDEuMDA2LS40OGExLjEzNyAxLjEzNyAwIDAgMC0uMjkyLTEuMDg0bC0uMDc1LS4xMDhhNC43NTQgNC43NTQgMCAwIDEtLjIxMS0uMzIgNi45MDUgNi45MDUgMCAwIDAtLjUyOC0uNzU3IDQuMjk3IDQuMjk3IDAgMCAxLS41MjgtMS4wMWMtLjE1MS0uMzc3LjA1LS43MDUuMDUtMS4wODNhNi4zNDcgNi4zNDcgMCAwIDAtLjMyNy0yLjE0NGMtLjEyNi0uMzUzLS4xNzYtLjczMS0uMzI3LTEuMDZhMS4xMiAxLjEyIDAgMCAwLS4yMjYtLjU4LjM3NC4zNzQgMCAwIDEgMC0uMzI3Yy4yMDUtLjE0NS4zOTktLjMwNS41NzktLjQ4YS41NjcuNTY3IDAgMCAwLS4yLS43MDVjLS4zMjctLjE1MS0uMy4zMjgtLjUyOC40MjloLS4xNTFjLS4wNS0uMTI2LjA1LS4xNzcuMTUxLS4yNzcgMC0uMDUgMC0uMTUxLS4wNS0uMTUxLS4yIDAtLjM3Ny0uMDUxLS40MjgtLjE1MWEzLjk1NyAzLjk1NyAwIDAgMC0xLjg2MS0xLjI4NmMuMTg4LjA1OC4zODIuMDkxLjU3OS4xLjMzOC4wNzEuNjkuMDM2IDEuMDA2LS4xLjIyNy0uMDc2LjI3Ny0uNDguMzc3LS43MDZhLjguOCAwIDAgMC0uMTUxLS42MzEgMi4xOSAyLjE5IDAgMCAwLS45MDYtLjc1NiA5LjEzIDkuMTMgMCAwIDEtLjY3OS0uMzUzLjk1Ni45NTYgMCAwIDAtLjI1MS0uMTI2Yy0yLjk2NS0xLjQ4NS05LjA2OS0uMi05LjUzNCAwaC0uMDA5YTguMjU0IDguMjU0IDAgMCAwLTEuMjQ5LjQ3NSAzLjkyMiAzLjkyMiAwIDAgMC0yLjM2NSAyLjQ2NSAzLjgzIDMuODMgMCAwIDAtMS4zMzMgMS41MDljLS40MjguOC0xLjA1NiAxLjUwOS0uOTU2IDIuNDE0LjEuNzguMjc3IDEuNDg0LjQyOCAyLjI4OS4wNDMuMjcyLjExLjU0LjIuOC4xLjI3NiAwIC42MjkuMTUxLjg1NS4wNzUuMTUuMDI1LjMyNy4yMjcuNDI4di4yYy4wNS4wNS4wNS4xLjE1MS4xdi4yYy40MzUuNDIzLjgwNy45MDYgMS4xMDcgMS40MzQuMS4yNzYtLjQ3OC4xNS0uNy4wNWE1Ljk3NyA1Ljk3NyAwIDAgMS0xLjEzMi0uOTU2LjE3Ni4xNzYgMCAwIDAtLjA1MS4xYy4yLjM1Mi45MDYuNzguNTI4IDEuMDA2LS4yLjEtLjQyOC0uMTUxLS42MjkuMDUtLjA1LjA3NiAwIC4xNzcgMCAuMjc3LS4yNzctLjItLjU3OC0uMS0uODU1LS4yLS4yLS4wNS0uMjUyLS40MjctLjQ3OC0uNDI3YTE1LjE5MSAxNS4xOTEgMCAwIDAtMS44MTEtLjMyNyAxNS4xNDQgMTUuMTQ0IDAgMCAwLTEuNzM5LS4xNlYxOS43MDdhLjYwNi42MDYgMCAwIDEgLjMwNi0uNTI0bDE0Ljk4Ny04Ljc2MSAxNC45OTQgOC42NzdhLjYwNS42MDUgMCAwIDEgLjMwNi41MjR2MTYuOTMyWm0tNy45NTQtOC4yNjFhLjMyNS4zMjUgMCAwIDEtLjI4Mi4xNDkgMi44NCAyLjg0IDAgMCAwLS4yODIuMjczYy4xIDAgMCAuMTQ5LjEuMTQ5LS4yMDUuMjIzLjA3Ny42OTQtLjIwNS43OTMtLjM3LjA5OS0uNzU4LjA5OS0xLjEyNyAwYS43MjcuNzI3IDAgMCAxIC4xNjctLjAxNmguMDg1YS4zODIuMzgyIDAgMCAwIC4zMzctLjEzMnYtLjJjMC0uMDUtLjA1MS0uMDUtLjEtLjA1YS4xNi4xNiAwIDAgMS0uMS4wNS4yMjMuMjIzIDAgMCAwLS4xNTQtLjIuODA2LjgwNiAwIDAgMS0uNzE4LS4yNzMuNjcuNjcgMCAwIDEgLjQzNi0uMDVjLjEyOCAwIC4wNzctLjIyMy4yMzEtLjMyMmguMTU0Yy4zMDctLjM3Mi44NzEtLjQ3MS45NzQtLjg0MyAwLS4xLS4yODItLjEtLjQ4Ny0uMTVhMi4yNiAyLjI2IDAgMCAwLS44Mi4wNWMtLjM2LjA1LS43MTIuMTQyLTEuMDUxLjI3NC4yOC0uMjA2LjU5Mi0uMzY1LjkyMy0uNDcxLjIzMi0uMDkuNDczLS4xNTcuNzE4LS4ybC4xMzItLjAyNi4xMzMtLjAyN2EuOTcuOTcgMCAwIDEgLjU1NiAwYy4yMzEuMS42MTUuMS42NjYuMjQ4LjEuMjczLS4xNTQuNTQ1LS40MzUuNzQ0LS4wNTcuMDguMTQ5LjEzNS4xNDkuMjNaJy8+PHBhdGggZmlsbD0nI0ZDQzYzQScgZD0nTTY0LjU2IDVIMzlhMiAyIDAgMCAwLTIgMnY5LjMwMmEyIDIgMCAwIDAgMiAyaDI1LjU2YTIgMiAwIDAgMCAyLTJWN2EyIDIgMCAwIDAtMi0yWicvPjxwYXRoIGZpbGw9JyMxNjE2MTYnIGQ9J00zOS41NjIgMTYuMTY4VjcuMzE2aDIuOTIxYy45NyAwIDEuNzMyLjIzNiAyLjI4OS43MDguNTY1LjQ3Mi44NDcgMS4xMTcuODQ3IDEuOTM1IDAgLjgxLS4yODIgMS40NS0uODQ3IDEuOTIyLS41NTcuNDcyLTEuMzIuNzA4LTIuMjg5LjcwOGgtMS4xMjV2My41NzloLTEuNzk2Wm0yLjk5Ny03LjMyMmgtMS4yMDF2Mi4yMTNoMS4yYy4zOCAwIC42NzUtLjA5Ny44ODYtLjI5LjIyLS4xOTUuMzI5LS40NzMuMzI5LS44MzYgMC0uMzM3LS4xMS0uNjAyLS4zMjktLjc5Ni0uMjEtLjE5NC0uNTA2LS4yOTEtLjg4NS0uMjkxWk00Ny4yMyAxNi4xNjhWNy4zMTZoMi43MDdjLjk3IDAgMS43MzYuMjM2IDIuMzAxLjcwOC41NjUuNDcyLjg0NyAxLjExNy44NDcgMS45MzUgMCAuNTMtLjEyNi45OTUtLjM3OSAxLjM5LS4yNDQuMzg5LS41OS42ODgtMS4wMzcuODk5bDIuNzgyIDMuOTJoLTIuMTVsLTIuMzUyLTMuNTc5aC0uOTIzdjMuNTc5aC0xLjc5NVptMi44MDgtNy4zMjJoLTEuMDEydjIuMjEzaDEuMDEyYy4zOCAwIC42NzQtLjA5Ny44ODUtLjI5LjIxLS4xOTUuMzE2LS40NzMuMzE2LS44MzYgMC0uMzM3LS4xMDUtLjYwMi0uMzE2LS43OTYtLjIxLS4xOTQtLjUwNi0uMjkxLS44ODUtLjI5MVpNNTkuNTQ5IDcuMDYzYy42OSAwIDEuMzIzLjEyNiAxLjg5Ni4zOC41ODIuMjUyIDEuMDguNTkgMS40OTIgMS4wMTEuNDE0LjQyMS43MzQuOTE5Ljk2MiAxLjQ5Mi4yMjcuNTY1LjM0MSAxLjE2NC4zNDEgMS43OTYgMCAuNjMyLS4xMTQgMS4yMzUtLjM0MSAxLjgwOGE0LjQ4NSA0LjQ4NSAwIDAgMS0uOTYyIDEuNDhjLS40MTMuNDIxLS45MS43NTgtMS40OTIgMS4wMTFhNC42NDggNC42NDggMCAwIDEtMS44OTYuMzggNC43MzggNC43MzggMCAwIDEtMy40MDItMS4zOTEgNC40ODQgNC40ODQgMCAwIDEtLjk2MS0xLjQ4IDQuODU1IDQuODU1IDAgMCAxLS4zNDItMS44MDhjMC0uNjMzLjExNC0xLjIzMS4zNDItMS43OTYuMjI3LS41NzMuNTQ4LTEuMDcuOTYxLTEuNDkyLjQxMy0uNDIyLjkxLS43NTkgMS40OTItMS4wMTJhNC43MzcgNC43MzcgMCAwIDEgMS45MS0uMzc5Wm0wIDcuNjc2YTIuOCAyLjggMCAwIDAgMS4xMzgtLjIyOGMuMzU0LS4xNi42NTMtLjM3Ljg5OC0uNjMyLjI1Mi0uMjcuNDUtLjU4Ni41OTQtLjk0OWEzLjI3IDMuMjcgMCAwIDAgLjIxNS0xLjE4OCAzLjE3IDMuMTcgMCAwIDAtLjIxNS0xLjE3NiAyLjc5MSAyLjc5MSAwIDAgMC0uNTk1LS45NDkgMi41NDggMi41NDggMCAwIDAtLjg5Ny0uNjMyIDIuNjczIDIuNjczIDAgMCAwLTEuMTM4LS4yNGMtLjQxMyAwLS43OTcuMDgtMS4xNTEuMjRhMi42NzggMi42NzggMCAwIDAtLjkxLjYzMiAyLjg5OSAyLjg5OSAwIDAgMC0uNTgyLjk0OSAzLjE3IDMuMTcgMCAwIDAtLjIxNSAxLjE3NmMwIC40MjEuMDcxLjgxNy4yMTUgMS4xODguMTQzLjM2My4zMzcuNjc5LjU4MS45NDkuMjUzLjI2MS41NTcuNDcyLjkxLjYzMi4zNTUuMTUyLjczOS4yMjggMS4xNTIuMjI4WicvPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9J2EnPjxwYXRoIGZpbGw9JyNmZmYnIGQ9J00wIDBoMjExdjU4SDB6Jy8+PC9jbGlwUGF0aD48L2RlZnM+PC9zdmc+"); + } } diff --git a/src/lib/components/specialized/pc-button.svelte b/src/lib/components/specialized/pc-button.svelte new file mode 100644 index 000000000..f0cb2d1d7 --- /dev/null +++ b/src/lib/components/specialized/pc-button.svelte @@ -0,0 +1,28 @@ + + +
+ +
+
+ + +
diff --git a/src/routes/auth/connexion/+page.svelte b/src/routes/auth/connexion/+page.svelte index 58cd8ffb2..1b928aefe 100644 --- a/src/routes/auth/connexion/+page.svelte +++ b/src/routes/auth/connexion/+page.svelte @@ -14,6 +14,7 @@ import CenteredGrid from "$lib/components/display/centered-grid.svelte"; import Breadcrumb from "$lib/components/display/breadcrumb.svelte"; import IcButton from "$lib/components/specialized/ic-button.svelte"; + import PcButton from "$lib/components/specialized/pc-button.svelte"; function getLoginHint() { const loginHint = $page.url.searchParams.get("login_hint"); @@ -62,6 +63,7 @@ + From b5525a09353c6bfebac3c8b426143be982bcb6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 21:27:15 +0200 Subject: [PATCH 02/20] =?UTF-8?q?oidc:=20redirection=20apr=C3=A8s=20callba?= =?UTF-8?q?ck=20d'identification=20OIDC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/auth/ic-callback/+page.ts | 2 ++ src/routes/auth/pc-callback/+page.ts | 2 ++ src/routes/auth/pc-callback/[token]/+page.ts | 12 ++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 src/routes/auth/pc-callback/+page.ts create mode 100644 src/routes/auth/pc-callback/[token]/+page.ts diff --git a/src/routes/auth/ic-callback/+page.ts b/src/routes/auth/ic-callback/+page.ts index f9bb84c7c..74a93655c 100644 --- a/src/routes/auth/ic-callback/+page.ts +++ b/src/routes/auth/ic-callback/+page.ts @@ -30,6 +30,8 @@ export const load: PageLoad = async ({ url, parent }) => { window.localStorage.removeItem("oidcState"); const targetUrl = `${getApiURL()}/inclusion-connect-authenticate/`; + // ce call retourne une structure avec le token DRF initialisé coté backend + const result = await fetch(targetUrl, { method: "POST", headers: { diff --git a/src/routes/auth/pc-callback/+page.ts b/src/routes/auth/pc-callback/+page.ts new file mode 100644 index 000000000..76717b012 --- /dev/null +++ b/src/routes/auth/pc-callback/+page.ts @@ -0,0 +1,2 @@ +export const ssr = false; +export const load = () => {}; diff --git a/src/routes/auth/pc-callback/[token]/+page.ts b/src/routes/auth/pc-callback/[token]/+page.ts new file mode 100644 index 000000000..e2d3185dc --- /dev/null +++ b/src/routes/auth/pc-callback/[token]/+page.ts @@ -0,0 +1,12 @@ +import { CANONICAL_URL } from "$lib/env"; +import { setToken } from "$lib/utils/auth"; +import { redirect } from "@sveltejs/kit"; +import { getNextPage } from "../../utils"; + +export const load = ({ params, url }) => { + const token = params.token; + setToken(token); + + // home pour l'instant + redirect(302, CANONICAL_URL + getNextPage(url)); +}; From 33788033c0b6cf7a0b03818a4f72905e74808424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 21:28:13 +0200 Subject: [PATCH 03/20] =?UTF-8?q?oidc:=20effacement=20des=20donn=C3=A9es?= =?UTF-8?q?=20de=20connexion=20avant=20logout=20OIDC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En 2 phases : - effacement du token coté frontend - finalisation de la déconnexion coté backend --- src/routes/auth/pc-logout/+page.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/routes/auth/pc-logout/+page.ts diff --git a/src/routes/auth/pc-logout/+page.ts b/src/routes/auth/pc-logout/+page.ts new file mode 100644 index 000000000..0f29b9af4 --- /dev/null +++ b/src/routes/auth/pc-logout/+page.ts @@ -0,0 +1,8 @@ +import { getApiURL } from "$lib/utils/api"; +import { disconnect } from "$lib/utils/auth"; +import { redirect } from "@sveltejs/kit"; + +export const load = () => { + disconnect(); + redirect(302, getApiURL() + "/oidc/pre_logout/"); +}; From a4c61d6d59c45b7cb4930117546c092e0229552e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 21:29:21 +0200 Subject: [PATCH 04/20] =?UTF-8?q?Ajout=20du=20bouton=20de=20d=C3=A9connexi?= =?UTF-8?q?on=20ProConnect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/_index/menu-mon-compte.svelte | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/routes/_index/menu-mon-compte.svelte b/src/routes/_index/menu-mon-compte.svelte index 181c105ae..7cf9d6556 100644 --- a/src/routes/_index/menu-mon-compte.svelte +++ b/src/routes/_index/menu-mon-compte.svelte @@ -51,6 +51,15 @@ {@html logoutBoxLineIcon} - Déconnexion + Déconnexion (IC) + + +
+ + + + {@html logoutBoxLineIcon} + + Déconnexion (PC) From 98d8ac38fbbf22c04f5a89cdb21c46d6795f7d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 15:50:13 +0200 Subject: [PATCH 05/20] models: ajout du champ `sub_pc` pour ProConnect --- dora/users/migrations/0029_user_sub_pc.py | 17 +++++++++++++++++ dora/users/models.py | 5 +++++ 2 files changed, 22 insertions(+) create mode 100644 dora/users/migrations/0029_user_sub_pc.py diff --git a/dora/users/migrations/0029_user_sub_pc.py b/dora/users/migrations/0029_user_sub_pc.py new file mode 100644 index 000000000..04be2d4ba --- /dev/null +++ b/dora/users/migrations/0029_user_sub_pc.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.14 on 2024-08-07 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0028_data_migration_cap_emploi"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="sub_pc", + field=models.UUIDField(null=True, verbose_name="Identifiant ProConnect"), + ), + ] diff --git a/dora/users/models.py b/dora/users/models.py index 74d559034..82f32287a 100644 --- a/dora/users/models.py +++ b/dora/users/models.py @@ -62,9 +62,14 @@ def members_invited(self): class User(AbstractBaseUser): + # obsolète : sera remplacé par `sub_pc` pour ProConnect ic_id = models.UUIDField( verbose_name="Identifiant Inclusion Connect", null=True, blank=True ) + + # null possible en base ... pour l'instant + sub_pc = models.UUIDField(verbose_name="Identifiant ProConnect", null=True) + email = models.EmailField( verbose_name="email address", max_length=255, From 70251c66a200e677c9d8c0a88fe79882a8a48e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 15:53:53 +0200 Subject: [PATCH 06/20] oidc: modification de l'app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ajouts des routes pesonnalisées OIDC pour les redirections vers le front-end - modification de certaines portions de `mozilla-django-oidc` avec une vue custom --- dora/oidc/__init__.py | 113 ++++++++++++++++++++++++++++++++++++++++++ dora/oidc/apps.py | 13 +++++ dora/oidc/urls.py | 18 ++++++- dora/oidc/views.py | 89 +++++++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 dora/oidc/apps.py diff --git a/dora/oidc/__init__.py b/dora/oidc/__init__.py index b05f193d1..bfc108195 100644 --- a/dora/oidc/__init__.py +++ b/dora/oidc/__init__.py @@ -1,2 +1,115 @@ +from logging import getLogger + +import requests +from django.core.exceptions import SuspiciousOperation +from mozilla_django_oidc.auth import ( + OIDCAuthenticationBackend as MozillaOIDCAuthenticationBackend, +) +from rest_framework.authtoken.models import Token + +from dora.users.models import User + +logger = getLogger(__name__) + + class OIDCError(Exception): """Exception générique pour les erreurs OIDC""" + + +class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend): + def get_userinfo(self, access_token, id_token, payload): + # Surcharge de la récupération des informations utilisateur: + # le décodage JSON du contenu JWT pose problème avec ProConnect + # qui le retourne en format binaire (content-type: application/jwt) + # d'où ce petit hack. + # Inspiré de : https://github.com/numerique-gouv/people/blob/b637774179d94cecb0ef2454d4762750a6a5e8c0/src/backend/core/authentication/backends.py#L47C1-L47C57 + user_response = requests.get( + self.OIDC_OP_USER_ENDPOINT, + headers={"Authorization": "Bearer {0}".format(access_token)}, + verify=self.get_settings("OIDC_VERIFY_SSL", True), + timeout=self.get_settings("OIDC_TIMEOUT", None), + proxies=self.get_settings("OIDC_PROXY", None), + ) + user_response.raise_for_status() + + try: + # cas où le type du token JWT est `application/json` + return user_response.json() + except requests.exceptions.JSONDecodeError: + # sinon, on présume qu'il s'agit d'un token JWT au format `application/jwt` (+...) + # comme c'est le cas pour ProConnect. + return self.verify_token(user_response.text) + + # Pas nécessaire de surcharger `get_or_create_user` puisque sur DORA, + # les utilisateurs ont un e-mail unique qui leur sert de `username`. + + def create_user(self, claims): + # on peut à la rigueur se passer de certains élements contenus dans les claims, + # mais pas de ceux-là : + email, sub = claims.get("email"), claims.get("sub") + if not email: + raise SuspiciousOperation( + "L'adresse e-mail n'est pas inclue dans les `claims`" + ) + + if not sub: + raise SuspiciousOperation( + "Le sujet (`sub`) n'est pas inclu dans les `claims`" + ) + + # TODO: le SIRET fait partie des claims obligatoire, + # voir comment traiter les rattachements à une structure. + # De plus, il semble que l'appartenance à plusieurs SIRET soit possible. + + # L'utilisateur est créé sans mot de passe (aucune connexion à l'admin), + # et comme venant de ProConnect, on considère l'e-mail vérifié. + new_user = self.UserModel.objects.create_user( + email, + sub_pc=sub, + first_name=claims.get("given_name", "N/D"), + last_name=claims.get("usual_name", "N/D"), + is_valid=True, + ) + + # compatibilité : + # durant la phase de migration vers ProConnect on ne replace *que* le fournisseur d'identité, + # et on ne touche pas aux mécanismes d'identification entre back et front. + self.get_or_create_drf_token(new_user) + + return new_user + + def update_user(self, user, claims): + # L'utilisateur peut déjà étre inscrit à IC, dans ce cas on réutilise la plupart + # des informations déjà connues + + if not user.sub_pc: + # utilisateur existant, mais non-enregistré sur ProConnect + sub = claims.get("sub") + if not sub: + raise SuspiciousOperation( + "Le sujet (`sub`) n'est pas inclu dans les `claims`" + ) + user.sub_pc = sub + user.save() + + return user + + def get_user(self, user_id): + if user := super().get_user(user_id): + self.get_or_create_drf_token(user) + return user + return None + + def get_or_create_drf_token(self, user_email): + # Pour être temporairement compatible, on crée un token d'identification DRF lié au nouvel utilisateur. + if not user_email: + logger.exception("Utilisateur non renseigné pour la création du token DRF") + + user = User.objects.get(email=user_email) + + token, created = Token.objects.get_or_create(user=user) + + if created: + logger.info("Initialisation du token DRF pour l'utilisateur %s", user_email) + + return token diff --git a/dora/oidc/apps.py b/dora/oidc/apps.py new file mode 100644 index 000000000..44656c94a --- /dev/null +++ b/dora/oidc/apps.py @@ -0,0 +1,13 @@ +from django.apps import AppConfig + +""" +dora.oidc: + Gère les connexions OIDC-Connect via ProConnect. + Basée sur un provider custom de django-allauth. + Remplace l'ancien système de connexion à Inclusion-Connect à partir de novembre 2024. +""" + + +class OIDCConfig(AppConfig): + name = "dora.oidc" + verbose_name = "Gestion des connexions ProConnect" diff --git a/dora/oidc/urls.py b/dora/oidc/urls.py index 61c06b230..880dff54f 100644 --- a/dora/oidc/urls.py +++ b/dora/oidc/urls.py @@ -1,8 +1,9 @@ +import mozilla_django_oidc.urls # noqa: F401 from django.urls import path import dora.oidc.views as views -oidc_patterns = [ +inclusion_connect_patterns = [ path( "inclusion-connect-get-login-info/", views.inclusion_connect_get_login_info, @@ -20,3 +21,18 @@ views.inclusion_connect_authenticate, ), ] + +proconnect_patterns = [ + # les patterns internes pour le callback et le logout sont définis + # dans le fichier `urls.py` de mozilla_django_oidc + # redirection vers ProConnect pour la connexion + path("oidc/login/", views.oidc_login, name="oidc_login"), + # redirection une fois la connexion terminée + path("oidc/logged_in/", views.oidc_logged_in, name="oidc_logged_in"), + # preparation au logout : 2 étapes nécessaires + # l'une de déconnexion sur ProConnect, l'autre locale de destruction de la session active + path("oidc/pre_logout/", views.oidc_pre_logout, name="oidc_pre_logout"), +] + + +oidc_patterns = inclusion_connect_patterns + proconnect_patterns diff --git a/dora/oidc/views.py b/dora/oidc/views.py index ddc0df37c..34249d404 100644 --- a/dora/oidc/views.py +++ b/dora/oidc/views.py @@ -6,8 +6,12 @@ from django.conf import settings from django.core.cache import cache from django.db import transaction +from django.http import HttpResponseForbidden +from django.http.response import HttpResponseRedirect +from django.urls import reverse from django.utils.crypto import get_random_string from furl import furl +from mozilla_django_oidc.views import OIDCAuthenticationCallbackView, resolve_url from rest_framework import permissions from rest_framework.authtoken.models import Token from rest_framework.decorators import api_view, permission_classes @@ -171,3 +175,88 @@ def inclusion_connect_authenticate(request): except requests.exceptions.RequestException as e: logging.exception(e) raise APIException("Erreur de communication avec le fournisseur d'identité") + + +# Migration vers ProConnect : +# En parallèle des différents endpoints OIDC inclusion-connect (gardés pour problème éventuel). + + +@api_view(["GET"]) +@permission_classes([permissions.AllowAny]) +def oidc_login(request): + # Simple redirection vers la page d'identification ProConnect (si pas identifié) + return HttpResponseRedirect( + redirect_to=reverse("oidc_authentication_init") + + f"?{request.META.get("QUERY_STRING")}" + ) + + +@api_view(["GET"]) +@permission_classes([permissions.AllowAny]) +def oidc_logged_in(request): + # étape indispensable pour le passage du token au frontend_state : + # malheuresement, cette étape est "zappée" si un paramètre `next` est passé lors de l'identification + # mozilla-django-oidc ne le prends pas en compte, il faut pour modifier la vue de callback et le redirect final + + # attention : l'utilisateur est toujours anonyme (a ce point il n'existe qu'un token DRF) + token = Token.objects.get(user_id=request.session["_auth_user_id"]) + + redirect_uri = f"{settings.FRONTEND_URL}/auth/pc-callback/{token}/" + + # gestion du next : + if next := request.GET.get("next"): + redirect_uri += f"?next={next}" + + # on redirige (pour l'instant) vers le front en faisant passer le token DRF + return HttpResponseRedirect(redirect_to=redirect_uri) + + +@api_view(["GET"]) +@permission_classes([permissions.AllowAny]) +def oidc_pre_logout(request): + # attention : le nom oidc_logout est pris par mozilla-django-oidc + # récuperation du token stocké en session: + if oidc_token := request.session.get("oidc_id_token"): + # construction de l'URL de logout + params = { + "id_token_hint": oidc_token, + "state": "todo_xxx", + "post_logout_redirect_uri": request.build_absolute_uri( + reverse("oidc_logout") + ), + } + logout_url = furl(settings.OIDC_OP_LOGOUT_ENDPOINT, args=params) + return HttpResponseRedirect(redirect_to=logout_url.url) + + # FIXME: URL de fallback ? + return HttpResponseForbidden("Déconnexion incorrecte") + + +class CustomAuthorizationCallbackView(OIDCAuthenticationCallbackView): + """ + Callback OIDC : + Vue personnalisée basée en grande partie sur celle définie par `mozilla-django-oidc`, + pour la gestion du retour OIDC après identification. + + La gestion du `next_url` par la classe par défaut n'est pas satisfaisante dans le contexte de DORA, + la redirection vers le frontend nécessitant une étape supplémentaire pour l'enregistrement du token DRF. + Cette classe modifie la dernière redirection du flow pour y ajouter le paramètre d'URL suivant, + plutôt que d'effectuer une redirection directement vers ce paramètre. + + A noter qu'il est trés simple de modifier les différentes étapes du flow OIDC pour les adapter, + `mozilla-django-oidc` disposant d'une série de settings pour spécifier les classes de vue à utiliser + pour chaque étape OIDC (dans ce cas via le setting `OIDC_CALLBACK_CLASS`). + """ + + @property + def success_url(self): + # récupération du paramètre d'URL suivant stocké en session en début de flow OIDC + + next_url = self.request.session.get("oidc_login_next", None) + next_fieldname = self.get_settings("OIDC_REDIRECT_FIELD_NAME", "next") + + success_url = resolve_url(self.get_settings("LOGIN_REDIRECT_URL", "/")) + success_url += f"?{next_fieldname}={next_url}" if next_url else "" + + # redirection vers le front via `oidc/logged_in` + return success_url From 7c1b3e4f7b3bd3f6a01056138383283963337cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 3 Oct 2024 15:55:49 +0200 Subject: [PATCH 07/20] libs: ajout de `mozilla-django-oidc` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ainsi que sa configuration et la modification des routes pour permettre à la fois l'utilisation de ProConnect et d'Inclusion-Connect --- config/settings/base.py | 70 +++++++++++++++++++++++++++++++++++++++-- config/settings/test.py | 4 ++- config/urls.py | 3 ++ requirements/base.txt | 1 + 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/config/settings/base.py b/config/settings/base.py index e15f643af..91399b190 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -26,6 +26,8 @@ INSTALLED_APPS = [ "django.contrib.gis", "django.contrib.auth", + # OIDC / ProConnect : doit être chargé après `django.contrib.auth` + "mozilla_django_oidc", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", @@ -64,8 +66,19 @@ "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "csp.middleware.CSPMiddleware", + # Rafraichissement du token ProConnect + "mozilla_django_oidc.middleware.SessionRefresh", ] +# OIDC / ProConnect +AUTHENTICATION_BACKENDS = [ + "dora.oidc.OIDCAuthenticationBackend", +] + +# Permet de garder le comportement d'identification "standard" (e-mail/password) +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_AUTHENTICATION_METHOD = "email" + ROOT_URLCONF = "config.urls" TEMPLATES = [ @@ -287,7 +300,7 @@ # Modération : MATTERMOST_HOOK_KEY = os.getenv("MATTERMOST_HOOK_KEY") -# INCLUSION-CONNECT / PRO-CONNECT +# INCLUSION-CONNECT IC_ISSUER_ID = os.getenv("IC_ISSUER_ID") IC_AUTH_URL = os.getenv("IC_AUTH_URL") IC_TOKEN_URL = os.getenv("IC_TOKEN_URL") @@ -296,7 +309,59 @@ IC_CLIENT_ID = os.getenv("IC_CLIENT_ID") IC_CLIENT_SECRET = os.getenv("IC_CLIENT_SECRET") -# Recherches sauvagardées : +# OIDC / PROCONNECT +PC_CLIENT_ID = os.getenv("PC_CLIENT_ID") +PC_CLIENT_SECRET = os.getenv("PC_CLIENT_SECRET") +PC_DOMAIN = os.getenv("PC_DOMAIN", "fca.integ01.dev-agentconnect.fr") +PC_ISSUER = os.getenv("PC_ISSUER", f"{PC_DOMAIN}/api/v2") +PC_AUTHORIZE_PATH = os.getenv("PC_AUTHORIZE_PATH", "authorize") +PC_TOKEN_PATH = os.getenv("PC_TOKEN_PATH", "token") +PC_USERINFO_PATH = os.getenv("PC_USERINFO_PATH", "userinfo") + +# ProConnect à besoin de ce setting pour le logout +FRONTEND_URL = os.getenv("FRONTEND_URL") + +# mozilla_django_oidc: +OIDC_RP_CLIENT_ID = os.getenv("PC_CLIENT_ID") +OIDC_RP_CLIENT_SECRET = os.getenv("PC_CLIENT_SECRET") +OIDC_RP_SCOPES = "openid given_name usual_name email siret custom uid" + +# `mozilla_django_oidc` n'utilise pas de discovery / .well-known +# on définit donc chaque endpoint +OIDC_RP_SIGN_ALGO = "RS256" +OIDC_OP_JWKS_ENDPOINT = f"https://{PC_ISSUER}/jwks" +OIDC_OP_AUTHORIZATION_ENDPOINT = f"https://{PC_ISSUER}/authorize" +OIDC_OP_TOKEN_ENDPOINT = f"https://{PC_ISSUER}/token" +OIDC_OP_USER_ENDPOINT = f"https://{PC_ISSUER}/userinfo" +OIDC_OP_LOGOUT_ENDPOINT = f"https://{PC_ISSUER}/session/end" + +# Les paramètres suivants servent à adapter la configuration OIDC +# de `mozilla-django_oidc` pour pouvoir fonctionner dans le contexte +# spécifique à DORA et ProConnect. + +# OIDC : intervalle de rafraichissement du token (4h) +OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = 4 * 60 * 60 + +# OIDC : nécessaire pour la gestion de la fin de session coté ProConnect +OIDC_STORE_ID_TOKEN = True +ALLOW_LOGOUT_GET_METHOD = True + +# obligatoire pour ProConnect: à passer en paramètre de requête supplémentaire +# lors de la première phase du flow OIDC +OIDC_AUTH_REQUEST_EXTRA_PARAMS = {"acr_values": "eidas1"} + +# OIDC : redirection vers le front DORA en cas de succès de l'identification +# necessaire pour la gestion de "l'URL suivant" (`next_url`) +LOGIN_REDIRECT_URL = "/oidc/logged_in/" + +# OIDC : redirection vers l'acceuil du front DORA pour la déconnexion +LOGOUT_REDIRECT_URL = FRONTEND_URL + +# OIDC : permet de préciser quelle est la class/vue en charge du callback dans le flow OIDC +# (essentiellement pour la gestion du `next_url`). +OIDC_CALLBACK_CLASS = "dora.oidc.views.CustomAuthorizationCallbackView" + +# Recherches sauvegardées : INCLUDES_DI_SERVICES_IN_SAVED_SEARCH_NOTIFICATIONS = ( os.getenv("INCLUDES_DI_SERVICES_IN_SAVED_SEARCH_NOTIFICATIONS") == "true" ) @@ -353,7 +418,6 @@ EMAIL_PORT = os.getenv("EMAIL_PORT") EMAIL_USE_TLS = True EMAIL_DOMAIN = os.getenv("EMAIL_DOMAIN") -FRONTEND_URL = os.getenv("FRONTEND_URL") SUPPORT_EMAIL = os.getenv("SUPPORT_EMAIL") SUPPORT_LINK = "https://aide.dora.inclusion.beta.gouv.fr" diff --git a/config/settings/test.py b/config/settings/test.py index 32efb52db..e5e353886 100644 --- a/config/settings/test.py +++ b/config/settings/test.py @@ -40,5 +40,7 @@ IC_TOKEN_URL = os.getenv("IC_TOKEN_URL", "https://whatever-oidc-token-url.com") AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME", "dora") SIB_ONBOARDING_LIST = os.getenv("SIB_ONBOARDING_LIST", "1") -SIB_ONBOARDING_PUTATIVE_MEMBER_LIST = os.getenv("SIB_ONBOARDING_PUTATIVE_MEMBER_LIST", "2") +SIB_ONBOARDING_PUTATIVE_MEMBER_LIST = os.getenv( + "SIB_ONBOARDING_PUTATIVE_MEMBER_LIST", "2" +) SIB_ONBOARDING_MEMBER_LIST = os.getenv("SIB_ONBOARDING_MEMBER_LIST", "3") diff --git a/config/urls.py b/config/urls.py index f76b534fe..b9e300856 100644 --- a/config/urls.py +++ b/config/urls.py @@ -112,7 +112,10 @@ urlpatterns = [ *private_api_patterns, *di_api_patterns, + # anciennes routes Inclusion-Connect (en attente de suppression) *oidc_patterns, + # nouvelles routes OIDC pour ProConnect + path("oidc/", include("mozilla_django_oidc.urls")), ] if settings.PROFILE: diff --git a/requirements/base.txt b/requirements/base.txt index cd9ce0d07..aae89bfd0 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -8,6 +8,7 @@ django-storages[boto3]==1.14.4 djangorestframework-camel-case==1.4.2 djangorestframework-gis==1.1 djangorestframework==3.15.2 +mozilla-django-oidc==4.0.1 furl==2.1.3 hiredis==3.0.0 humanize==4.11.0 From a4bff9897713946d213c0030d8919f74415bbecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Mon, 7 Oct 2024 11:17:26 +0200 Subject: [PATCH 08/20] =?UTF-8?q?V=C3=A9rification=20de=20la=20d=C3=A9conn?= =?UTF-8?q?exion=20ProConnect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En 2 étapes déjà gérées, mais cette fois-ci en véfifiant l'état du paramètre `state` passé par ProConnect (sécurité). --- dora/oidc/urls.py | 3 +++ dora/oidc/views.py | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/dora/oidc/urls.py b/dora/oidc/urls.py index 880dff54f..0f2eaaa1d 100644 --- a/dora/oidc/urls.py +++ b/dora/oidc/urls.py @@ -32,6 +32,9 @@ # preparation au logout : 2 étapes nécessaires # l'une de déconnexion sur ProConnect, l'autre locale de destruction de la session active path("oidc/pre_logout/", views.oidc_pre_logout, name="oidc_pre_logout"), + # la plupart des vues de `mozilla-django-oidc` sont paramètrables + # pas le logout + path("oidc/logout/", views.CustomLogoutView.as_view(), name="oidc_logout"), ] diff --git a/dora/oidc/views.py b/dora/oidc/views.py index 34249d404..60ef0606b 100644 --- a/dora/oidc/views.py +++ b/dora/oidc/views.py @@ -5,13 +5,17 @@ import requests from django.conf import settings from django.core.cache import cache +from django.core.exceptions import SuspiciousOperation from django.db import transaction -from django.http import HttpResponseForbidden from django.http.response import HttpResponseRedirect from django.urls import reverse from django.utils.crypto import get_random_string from furl import furl -from mozilla_django_oidc.views import OIDCAuthenticationCallbackView, resolve_url +from mozilla_django_oidc.views import ( + OIDCAuthenticationCallbackView, + OIDCLogoutView, + resolve_url, +) from rest_framework import permissions from rest_framework.authtoken.models import Token from rest_framework.decorators import api_view, permission_classes @@ -215,12 +219,14 @@ def oidc_logged_in(request): @permission_classes([permissions.AllowAny]) def oidc_pre_logout(request): # attention : le nom oidc_logout est pris par mozilla-django-oidc - # récuperation du token stocké en session: + # récupération du token stocké en session: if oidc_token := request.session.get("oidc_id_token"): - # construction de l'URL de logout + # ProConnect nécessite un `state` pour vérifier la déconnexion effective + logout_state = get_random_string(32) + request.session["logout_state"] = logout_state params = { "id_token_hint": oidc_token, - "state": "todo_xxx", + "state": logout_state, "post_logout_redirect_uri": request.build_absolute_uri( reverse("oidc_logout") ), @@ -228,8 +234,7 @@ def oidc_pre_logout(request): logout_url = furl(settings.OIDC_OP_LOGOUT_ENDPOINT, args=params) return HttpResponseRedirect(redirect_to=logout_url.url) - # FIXME: URL de fallback ? - return HttpResponseForbidden("Déconnexion incorrecte") + raise SuspiciousOperation("Tentative de déconnexion avec un token incorrect") class CustomAuthorizationCallbackView(OIDCAuthenticationCallbackView): @@ -260,3 +265,25 @@ def success_url(self): # redirection vers le front via `oidc/logged_in` return success_url + + +class CustomLogoutView(OIDCLogoutView): + """ + Logout OIDC : + ProConnect effectue des vérifications avant de déconnecter l'utilisateur + sur sa plateforme. + Essentiellement en vérifiant la validité d'un `state` passé en paramètre + avant la destruction de la session. + Cette classe effectue simplement la vérification du `state` précédemment stocké + en session (voir `oidc/pre_logout`) et réutilise la classe de vue originale + de `mozilla-django-oidc`. + """ + + def post(self, request): + if logout_state := request.session.pop("logout_state", None): + if request.GET.get("state") != logout_state: + raise SuspiciousOperation("La vérification de la déconnexion a échoué") + else: + raise SuspiciousOperation("Vérification de la déconnexion impossible") + + return super().post(request) From 2c047d6e898d5e50729297e2267f58446e5abb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Mon, 7 Oct 2024 12:12:54 +0200 Subject: [PATCH 09/20] =?UTF-8?q?fix:=20ajout=20du=20backend=20d'identific?= =?UTF-8?q?ation=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dans la configuration précédente, seule l'identification par OIDC était possible. Mais la partie admin de Django à besoin de l'identification par "modéle" (qui est celle par défaut). Les deux oexistent sereinement maintenant. --- config/settings/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/settings/base.py b/config/settings/base.py index 91399b190..093f45d50 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -72,6 +72,8 @@ # OIDC / ProConnect AUTHENTICATION_BACKENDS = [ + # auth par défaut pour la partie admin : + "django.contrib.auth.backends.ModelBackend", "dora.oidc.OIDCAuthenticationBackend", ] From 33961ddd0b29160796a708afd61874941059e772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Mon, 7 Oct 2024 15:43:17 +0200 Subject: [PATCH 10/20] =?UTF-8?q?safir:=20r=C3=A9cup=C3=A9ration=20du=20co?= =?UTF-8?q?de=20SAFIR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Même si on n'en fait encore rien, le point de récupération de la donnée est identifié dans le code. --- dora/oidc/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dora/oidc/__init__.py b/dora/oidc/__init__.py index bfc108195..543a93b52 100644 --- a/dora/oidc/__init__.py +++ b/dora/oidc/__init__.py @@ -57,10 +57,6 @@ def create_user(self, claims): "Le sujet (`sub`) n'est pas inclu dans les `claims`" ) - # TODO: le SIRET fait partie des claims obligatoire, - # voir comment traiter les rattachements à une structure. - # De plus, il semble que l'appartenance à plusieurs SIRET soit possible. - # L'utilisateur est créé sans mot de passe (aucune connexion à l'admin), # et comme venant de ProConnect, on considère l'e-mail vérifié. new_user = self.UserModel.objects.create_user( @@ -71,6 +67,13 @@ def create_user(self, claims): is_valid=True, ) + # recupération du code SAFIR : + # même pour l'instant inutilisé, on pourra par la suite le passer au frontend + # pour rattachement direct à une agence France Travail + if custom := claims.get("custom"): + code_safir = custom.get("structureTravail") # noqa F481 + # TODO: une fois le code SAFIR récupéré, voir quoi en faire (redirection vers un rattachement) + # compatibilité : # durant la phase de migration vers ProConnect on ne replace *que* le fournisseur d'identité, # et on ne touche pas aux mécanismes d'identification entre back et front. From 7b70fb2161db8be491e08cb041af61365356d25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Mon, 14 Oct 2024 14:49:16 +0200 Subject: [PATCH 11/20] fix: modification d'un log en exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Résidu des tests, maintenant l'absence d'e-mail doit lever une exception. --- dora/oidc/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dora/oidc/__init__.py b/dora/oidc/__init__.py index 543a93b52..dd4527f09 100644 --- a/dora/oidc/__init__.py +++ b/dora/oidc/__init__.py @@ -106,7 +106,9 @@ def get_user(self, user_id): def get_or_create_drf_token(self, user_email): # Pour être temporairement compatible, on crée un token d'identification DRF lié au nouvel utilisateur. if not user_email: - logger.exception("Utilisateur non renseigné pour la création du token DRF") + raise SuspiciousOperation( + "Utilisateur non renseigné pour la création du token DRF" + ) user = User.objects.get(email=user_email) From f74045df83d95a782b1464cf2a16b2cffb6e2b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Mon, 21 Oct 2024 17:41:39 +0200 Subject: [PATCH 12/20] Ajout d'une variable d'environnement pour identifier le backend OIDC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On peut encore choisir entre Inclusion Connect et ProConnect via la var-env OIDC_AUTH_BACKEND (par défaut `proconnect`) --- .../components/specialized/pc-button.svelte | 2 +- src/lib/env.ts | 2 ++ src/routes/_index/menu-mon-compte.svelte | 29 ++++++++++--------- src/routes/auth/connexion/+page.svelte | 8 +++-- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/lib/components/specialized/pc-button.svelte b/src/lib/components/specialized/pc-button.svelte index f0cb2d1d7..1b1cd94f9 100644 --- a/src/lib/components/specialized/pc-button.svelte +++ b/src/lib/components/specialized/pc-button.svelte @@ -22,7 +22,7 @@ rel="noopener" href="https://aide.dora.inclusion.beta.gouv.fr" > - Besoin d’aide ? Contactez-nous (mais pas tout de suite) + Besoin d’aide ? Contactez-nous diff --git a/src/lib/env.ts b/src/lib/env.ts index a21b2340b..5c3b6d24e 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -5,3 +5,5 @@ export const SENTRY_DSN = import.meta.env.VITE_SENTRY_DSN; export const CANONICAL_URL = import.meta.env.VITE_PUBLIC_CANONICAL_URL; export const METABASE_EMBED_URL = import.meta.env.VITE_METABASE_EMBED_URL; export const FLAG_STRIKING = import.meta.env.VITE_FLAG_STRIKING === "true"; +export const OIDC_AUTH_BACKEND = + import.meta.env.OIDC_AUTH_BACKEND || "proconnect"; diff --git a/src/routes/_index/menu-mon-compte.svelte b/src/routes/_index/menu-mon-compte.svelte index 7cf9d6556..d3f6ec3bb 100644 --- a/src/routes/_index/menu-mon-compte.svelte +++ b/src/routes/_index/menu-mon-compte.svelte @@ -1,6 +1,7 @@ @@ -23,14 +24,16 @@ Mes informations - + {#if OIDC_AUTH_BACKEND !== "proconnect"} + + {/if}
@@ -52,12 +55,23 @@

+

- Vous utilisez Inclusion Connect pour vous connecter à DORA. + {#if OIDC_AUTH_BACKEND === "proconnect"} + Vous utilisez ProConnect pour vous connecter à DORA. + {:else} + Vous utilisez Inclusion Connect pour vous connecter à DORA. + {/if}

From 0d1e464564386044dc39c95b2492fffa48a42fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Thu, 24 Oct 2024 18:06:34 +0200 Subject: [PATCH 18/20] =?UTF-8?q?fix:=20r=C3=A9cup=C3=A9ration=20de=20la?= =?UTF-8?q?=20var-env=20OIDC=5FAUTH=5FBACKEND?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doit-être préfixée par 'VITE_' --- front/.env-example | 2 ++ front/src/lib/env.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/front/.env-example b/front/.env-example index 401ea3ee8..8751434c1 100644 --- a/front/.env-example +++ b/front/.env-example @@ -9,3 +9,5 @@ GITGUARDIAN_API_KEY= # Variables d'environnement publiques VITE_PUBLIC_MATOMO_CONTAINER_URL=# ex: "https:///js/container_.js" + +VITE_OIDC_AUTH_BACKEND=# proconnect | inclusionconnect diff --git a/front/src/lib/env.ts b/front/src/lib/env.ts index 5c3b6d24e..32389100e 100644 --- a/front/src/lib/env.ts +++ b/front/src/lib/env.ts @@ -6,4 +6,4 @@ export const CANONICAL_URL = import.meta.env.VITE_PUBLIC_CANONICAL_URL; export const METABASE_EMBED_URL = import.meta.env.VITE_METABASE_EMBED_URL; export const FLAG_STRIKING = import.meta.env.VITE_FLAG_STRIKING === "true"; export const OIDC_AUTH_BACKEND = - import.meta.env.OIDC_AUTH_BACKEND || "proconnect"; + import.meta.env.VITE_OIDC_AUTH_BACKEND || "proconnect"; From eb2c6aa637d6ce7c612f67aebbb445ea24982bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Fri, 25 Oct 2024 10:58:58 +0200 Subject: [PATCH 19/20] =?UTF-8?q?fix:=20correction=20du=20`next=5Furl`=20a?= =?UTF-8?q?vec=20multiples=20param=C3=A8tres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/oidc/views.py | 4 ++-- front/src/routes/auth/pc-callback/[token]/+page.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/back/dora/oidc/views.py b/back/dora/oidc/views.py index 60ef0606b..fe2b4352c 100644 --- a/back/dora/oidc/views.py +++ b/back/dora/oidc/views.py @@ -208,8 +208,8 @@ def oidc_logged_in(request): redirect_uri = f"{settings.FRONTEND_URL}/auth/pc-callback/{token}/" # gestion du next : - if next := request.GET.get("next"): - redirect_uri += f"?next={next}" + if request.GET.get("next"): + redirect_uri += "?" + request.GET.urlencode() # on redirige (pour l'instant) vers le front en faisant passer le token DRF return HttpResponseRedirect(redirect_to=redirect_uri) diff --git a/front/src/routes/auth/pc-callback/[token]/+page.ts b/front/src/routes/auth/pc-callback/[token]/+page.ts index e2d3185dc..b79b1d0a9 100644 --- a/front/src/routes/auth/pc-callback/[token]/+page.ts +++ b/front/src/routes/auth/pc-callback/[token]/+page.ts @@ -7,6 +7,10 @@ export const load = ({ params, url }) => { const token = params.token; setToken(token); - // home pour l'instant - redirect(302, CANONICAL_URL + getNextPage(url)); + const nextPage = getNextPage(url); + url.searchParams.delete("next"); + const qsParams = url.searchParams.toString(); + const uri = nextPage + (qsParams !== "" ? "&" + qsParams : ""); + + redirect(302, CANONICAL_URL + uri); }; From 1ac6063341497cde49a8cf1857797406b41a0072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vergez?= Date: Fri, 25 Oct 2024 11:30:52 +0200 Subject: [PATCH 20/20] revue: corrections et typos --- front/src/app.postcss | 12 ------------ .../src/lib/components/specialized/pc-button.svelte | 9 ++++----- front/src/routes/auth/connexion/+page.svelte | 8 ++++---- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/front/src/app.postcss b/front/src/app.postcss index 2361a66ee..04ab6d82b 100644 --- a/front/src/app.postcss +++ b/front/src/app.postcss @@ -172,16 +172,4 @@ @page { size: 26.25cm 37.125cm; /* A4 * 1.25, afin de réduire la taille de l'impression */ } - - proconnect-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; - } } diff --git a/front/src/lib/components/specialized/pc-button.svelte b/front/src/lib/components/specialized/pc-button.svelte index f9bbe6a4c..c51538a0c 100644 --- a/front/src/lib/components/specialized/pc-button.svelte +++ b/front/src/lib/components/specialized/pc-button.svelte @@ -4,8 +4,7 @@ export let nextPage: string; - const loginUrl = - getApiURL() + "/oidc/login/?next=" + encodeURIComponent(nextPage); + const loginUrl = `${getApiURL()}/oidc/login/?next=${encodeURIComponent(nextPage)}`;
@@ -21,17 +20,17 @@ rel="noopener noreferrer" href="https://aide.dora.inclusion.beta.gouv.fr/fr/category/inscription-et-gestion-du-compte-ha8m5b/" > - Besoin d’aide ? Contactez-nous + Besoin d’aide ? Contactez-nous   - Qu'est que ProConnect ? + Qu'est que ProConnect ?
diff --git a/front/src/routes/auth/connexion/+page.svelte b/front/src/routes/auth/connexion/+page.svelte index 513e8124e..1ad67eff4 100644 --- a/front/src/routes/auth/connexion/+page.svelte +++ b/front/src/routes/auth/connexion/+page.svelte @@ -64,7 +64,7 @@
{@html informationLineIcon}
-
DORA passe à Inclusion Connect !
+
DORA passe à Inclusion Connect !

Si vous aviez un ancien compte DORA, vous pouvez @@ -92,11 +92,11 @@ {#if OIDC_AUTH_BACKEND === "proconnect"}

ProConnect - Pourquoi ProConnect ? + Pourquoi ProConnect ?

- 🧑🏻‍💻 Un compte unique pour tous vos services numériques ! + 🧑🏻‍💻 Un compte unique pour tous vos services numériques !

🔐 Accédez aux différents services partenaires avec le même @@ -131,7 +131,7 @@

- 🧑🏻‍💻 Un compte unique pour tous vos services numériques ! + 🧑🏻‍💻 Un compte unique pour tous vos services numériques !

🔐 Accédez aux différents services partenaires avec le même