From 10e2cc942de9bb46e32d2bd2fad3c3aaafb44df4 Mon Sep 17 00:00:00 2001 From: Okke Harsta Date: Fri, 17 Jan 2025 14:12:04 +0100 Subject: [PATCH] Show control code after creation --- myconext-gui/package.json | 3 +- myconext-gui/src/locale/en.js | 12 +- myconext-gui/src/locale/nl.js | 15 +- myconext-gui/src/routes/PersonalInfo.svelte | 72 ++- myconext-gui/src/utils/date.js | 5 + myconext-gui/src/verify/ServiceDesk.svelte | 45 +- myconext-gui/src/verify/VerifyChoice.svelte | 17 +- .../java/myconext/api/LoginController.java | 11 +- .../src/main/java/myconext/model/User.java | 8 + .../java/myconext/model/UserResponse.java | 2 + .../security/SecurityConfiguration.java | 9 +- ...eslint.config.js => eslint.config.js.nope} | 0 servicedesk-gui/index.html | 2 +- servicedesk-gui/package.json | 6 +- servicedesk-gui/src/App.jsx | 93 ++-- servicedesk-gui/src/api/index.js | 25 +- servicedesk-gui/src/index.scss | 6 +- servicedesk-gui/src/locale/I18n.js | 6 - servicedesk-gui/src/locale/en.js | 505 +---------------- servicedesk-gui/src/locale/nl.js | 518 +----------------- servicedesk-gui/src/main.jsx | 20 +- servicedesk-gui/src/pages/Login.scss | 2 +- servicedesk-gui/src/pages/NotFound.scss | 2 +- servicedesk-gui/src/stores/AppStore.js | 1 - servicedesk-gui/src/styles/mixins.scss | 2 +- servicedesk-gui/src/styles/responsive.scss | 2 +- servicedesk-gui/src/utils/QueryParameters.js | 10 + servicedesk-gui/src/utils/Utils.js | 52 ++ servicedesk-gui/vite.config.js | 13 + servicedesk-gui/yarn.lock | 5 + 30 files changed, 289 insertions(+), 1180 deletions(-) rename servicedesk-gui/{eslint.config.js => eslint.config.js.nope} (100%) create mode 100644 servicedesk-gui/src/utils/QueryParameters.js create mode 100644 servicedesk-gui/src/utils/Utils.js diff --git a/myconext-gui/package.json b/myconext-gui/package.json index d5d18388..c064f8da 100644 --- a/myconext-gui/package.json +++ b/myconext-gui/package.json @@ -25,7 +25,8 @@ "svelte-preprocess": "^6.0.3", "vite": "^6.0.2", "vite-plugin-svelte-svgr": "^1.0.4", - "vite-plugin-svelte-svg-loader": "^1.0.1" + "vite-plugin-svelte-svg-loader": "^1.0.1", + "@castlenine/svelte-qrcode":"^2.3.0" }, "resolutions": { "glob-parent": "^6.0.2", diff --git a/myconext-gui/src/locale/en.js b/myconext-gui/src/locale/en.js index f7da5739..3c9d7e1f 100644 --- a/myconext-gui/src/locale/en.js +++ b/myconext-gui/src/locale/en.js @@ -293,7 +293,6 @@ I18n.translations.en = { deleteToken: "Revoke access token", deleted: "eduID removed", tokenDeleted: "Tokens removed" - }, institution: { title: "Connected institution", @@ -576,7 +575,8 @@ I18n.translations.en = { issuers: { eherkenning: "eIDAS", idin: "Idin", - studielink: "Studielink" + studielink: "Studielink", + servicedesk: "Service Desk" }, serviceDesk: { confirmIdentityHeader: "You need to manually confirm your identity", @@ -597,12 +597,13 @@ I18n.translations.en = { information: "Enter your details as they appear on your ID. Names must be in Roman characters.", lastName: "Last name", firstName: "First name(s)", - dateOfBirth: "Date of birth", + dayOfBirth: "Date of birth", generateControlCode: "Generate verification code" }, controlCode: { + controlCode: "Verification code", yourControlCode: "Your verification code", - info: "You will also receive an email with this code. The code is valid for 14 days and is intended for:", + info: "You will also receive an email with this code. The code is valid for {{nbr}} more days and is intended for:", typoPrefix: "Made a typo? ", typoLink: "Edit your details", todo: "What's next?", @@ -614,7 +615,8 @@ I18n.translations.en = { deleteControlCode: "Delete verification code", deletedControlCode: "Verification code deleted", banner: "Verify your identity at an eduID Service Desk by presenting your ID and verification code.", - showCode: "Show code" + showCode: "Show code", + validityCode: "Your code is valid for {{nbr}} more days." } } } diff --git a/myconext-gui/src/locale/nl.js b/myconext-gui/src/locale/nl.js index 50dcc315..52749396 100644 --- a/myconext-gui/src/locale/nl.js +++ b/myconext-gui/src/locale/nl.js @@ -195,7 +195,6 @@ I18n.translations.nl = { passkey: "Passkey", passkeyAdd: "Voeg een passkey toe" }, - tiqr: { title: "Wil je de volgende keer sneller en veiliger inloggen?", info: "Download de eduID app en log veilig in zonder wachtwoord of toegang tot je e-mail.", @@ -221,7 +220,6 @@ I18n.translations.nl = { getCode: "Vraag een code aan", codeInfo: "Hopelijk bewaard op een veilige plek", getCodeInfo: "En bewaar het op een veilige plek", - } }, home: { @@ -353,7 +351,6 @@ I18n.translations.nl = { flash: { passwordLink: "Een e-mail is verstuurd naar {{name}} om je wachtwoord opnieuw in te stellen." } - }, webauthn: { setTitle: "Beveiligingssleutel toevoegen", @@ -413,7 +410,6 @@ I18n.translations.nl = { maxAttemptsPost: "om opnieuw je telefoonnummer in te voeren en een nieuwe code te ontvangen", maxAttemptsPostNoReEnter: "om een nieuwe code aan te vragen", here: " hier " - }, enrollApp: { header: "Voltooi de installatie in de eduID app", @@ -579,7 +575,8 @@ I18n.translations.nl = { issuers: { eherkenning: "eIDAS", idin: "Idin", - studielink: "Studielink" + studielink: "Studielink", + servicedesk: "Service Desk" }, serviceDesk: { confirmIdentityHeader: "Bevestig wie je bent met je identiteitsbewijs", @@ -600,12 +597,13 @@ I18n.translations.nl = { information: "Vul je gegevens in zoals die op je identiteitsbewijs staan.", lastName: "Achternaam", firstName: "Voornamen", - dateOfBirth: "Geboortedatum", + dayOfBirth: "Geboortedatum", generateControlCode: "Genereer controlcode" }, controlCode: { + controlCode: "Controlecode", yourControlCode: "Je controlecode", - info: "Je krijgt ook een e-mail met deze code. De code is 14 dagen geldig en is bedoeld voor:", + info: "Je krijgt ook een e-mail met deze code. De code is {{nbr}} dagen geldig en is bedoeld voor:", typoPrefix: "Typfout gemaakt? ", typoLink: "Pas gegevens aan", todo: "Wat moet je nu doen?", @@ -617,7 +615,8 @@ I18n.translations.nl = { deleteControlCode: "Verwijder controlecode", deletedControlCode: "Verificatie-code verwijderd", banner: "Bevestig je identiteit bij een eduID Service Desk. Dit doe je door je identiteitsbewijs en controlecode te laten zien.", - showCode: "Toon code" + showCode: "Toon code", + validityCode: "Je code is nog geldig voor {{nbr}} dagen." } }, } diff --git a/myconext-gui/src/routes/PersonalInfo.svelte b/myconext-gui/src/routes/PersonalInfo.svelte index 2fd624d5..5ffd6f5c 100644 --- a/myconext-gui/src/routes/PersonalInfo.svelte +++ b/myconext-gui/src/routes/PersonalInfo.svelte @@ -7,7 +7,7 @@ import alertSvg from "../icons/alert-circle.svg?raw"; import Button from "../components/Button.svelte"; import { - deleteLinkedAccount, deleteUserControlCode, + deleteLinkedAccount, iDINIssuers, preferLinkedAccount, startLinkAccountFlow, @@ -61,7 +61,7 @@ let showControlCode = false; const manageVerifiedInformation = path => { - navigate(`/${path}`, {replace:true}); + navigate(`/${path}`, {replace: true}); } const preferInstitution = (showConfirmation, linkedAccount) => { @@ -136,7 +136,7 @@ } } - const refresh = (retry=false) => { + const refresh = (retry = false) => { ($user.linkedAccounts || []).forEach(account => markExpired(account)); ($user.externalLinkedAccounts || []).forEach(account => markExternalLinkedAccountExpired(account)); sortedAccounts = ($user.linkedAccounts || []).sort((a, b) => b.createdAt - a.createdAt); @@ -213,6 +213,7 @@ showModal = false; showNewInstitutionModal = false; showPreferredInstitutionModal = false; + showControlCode = false; const url = new URL(window.location.href); url.search = ""; history.pushState({}, "", url.toString()); @@ -233,20 +234,20 @@ const newExternalAccountLinked = !isEmpty(verify) && !isEmpty($user.externalLinkedAccounts); if (newAccountLinked || newExternalAccountLinked) { - //Determine if the new account is external or not - const newAccount = newExternalAccountLinked ? - $user.externalLinkedAccounts[0] : - ($user.linkedAccounts || []) - .find(la => la.eduPersonPrincipalName === linkedAccountIdentifier || la.subjectId === linkedAccountIdentifier); - - if (newAccount && (newExternalAccountLinked || !isEmpty(newAccount.givenName) || !isEmpty(newAccount.familyName))) { - newInstitution = newAccount; - if ((($user.linkedAccounts || []).length + ($user.externalLinkedAccounts || []).length) === 1) { - showNewInstitutionModal = true; - } else { - preferInstitution(true, newAccount); - } + //Determine if the new account is external or not + const newAccount = newExternalAccountLinked ? + $user.externalLinkedAccounts[0] : + ($user.linkedAccounts || []) + .find(la => la.eduPersonPrincipalName === linkedAccountIdentifier || la.subjectId === linkedAccountIdentifier); + + if (newAccount && (newExternalAccountLinked || !isEmpty(newAccount.givenName) || !isEmpty(newAccount.familyName))) { + newInstitution = newAccount; + if ((($user.linkedAccounts || []).length + ($user.externalLinkedAccounts || []).length) === 1) { + showNewInstitutionModal = true; + } else { + preferInstitution(true, newAccount); } + } } if (!isEmpty(retry)) { addIdentity(true); @@ -357,8 +358,9 @@ font-size: 22px; font-family: Nunito, sans-serif; color: var(--color-primary-green); + &.second { - margin: 25px 0 15px 0; + margin: 25px 0 15px 0; } } @@ -399,6 +401,7 @@ @media (max-width: $max-width-mobile) { margin-left: 0; } + :global(svg) { height: 28px; width: auto; @@ -476,10 +479,15 @@ display: flex; flex-direction: column; padding: 25px; + span { margin: auto; - font-size: 34px; - letter-spacing: 6px; + + &.code { + font-size: 34px; + margin-bottom: 40px; + letter-spacing: 6px; + } } } @@ -489,12 +497,12 @@ {#if showManageVerifiedInformation}
-
+
manageVerifiedInformation("personal")}> {@html arrowLeft} -

{I18n.t("profile.verifiedInformation")}

-
+

{I18n.t("profile.verifiedInformation")}

+

{I18n.t("profile.verifiedInformationInfo")}

{@html personalInfo} @@ -648,9 +656,10 @@ {/if} -{#if showModal} +{#if showModal || showControlCode} resetModalsAndQueryParams()} - title={showIdinOptions ? I18n.t("verify.modal.header") : I18n.t("profile.addInstitution")} + title={showIdinOptions ? I18n.t("verify.modal.header") : showControlCode ? + I18n.t("verify.serviceDesk.controlCode.controlCode") : I18n.t("profile.addInstitution")} showOptions={false}> resetModalsAndQueryParams()}/> {/if} @@ -693,17 +703,3 @@ /> {/if} - -{#if showControlCode} - showControlCode = false} - close={() => showControlCode = false} - cancelTitle={I18n.t("verify.serviceDesk.controlCode.deleteControlCode")} - largeConfirmation={true} - confirmTitle={I18n.t("profile.ok")} - title={I18n.t("verify.serviceDesk.controlCode.yourControlCode")}> -
- {$user.controlCode.code} -
-
-{/if} - diff --git a/myconext-gui/src/utils/date.js b/myconext-gui/src/utils/date.js index c97a3b33..a2b86bd6 100644 --- a/myconext-gui/src/utils/date.js +++ b/myconext-gui/src/utils/date.js @@ -14,3 +14,8 @@ export function dateFromEpoch(epochMilli, includeTime = false) { const timeFormatted = ` ${I18n.t("security.tiqr.dateTimeOn")} ${date.getHours()}:${minutesFormatted}`; return `${dateFormatted}${timeFormatted}` } + +export function verificationCodeValidityDays(controlCode) { + const millis = new Date().getTime() - controlCode.createdAt || new Date().getTime(); + return Math.floor(millis / (1000 * 60 * 60 * 24)) + 14; +} diff --git a/myconext-gui/src/verify/ServiceDesk.svelte b/myconext-gui/src/verify/ServiceDesk.svelte index ddac1080..1f15175d 100644 --- a/myconext-gui/src/verify/ServiceDesk.svelte +++ b/myconext-gui/src/verify/ServiceDesk.svelte @@ -7,26 +7,38 @@ import idCard from "../icons/verify/idCard.svg?raw"; import {isEmpty} from "../utils/utils.js"; import {createUserControlCode, deleteUserControlCode} from "../api/index.js"; + import {verificationCodeValidityDays} from "../utils/date"; + import {onMount} from "svelte"; export let toggleView; export let cancelView; + export let showControlCode = false; - let step = 0; + let step = showControlCode ? 2 : 0; let lastName = ""; let firstName = ""; - let dateOfBirth = ""; + let dayOfBirth = ""; let code = ""; + onMount(() => { + if (showControlCode) { + lastName = $user.controlCode.lastName; + firstName = $user.controlCode.lastName; + dayOfBirth = $user.controlCode.dayOfBirth; + code = $user.controlCode.code; + } + }) + + const cancel = () => { toggleView(); step = 0; lastName = ""; firstName = ""; - dateOfBirth = ""; + dayOfBirth = ""; } const backToPersonal = () => { - $user.controlCode = {code: code}; cancelView(); } @@ -40,10 +52,10 @@ } const generateControlCode = () => { - createUserControlCode(firstName, lastName, dateOfBirth) + createUserControlCode(firstName, lastName, dayOfBirth) .then(res => { code = res.code; - $user.controlCode = {code: res.code}; + $user.controlCode = res; step = 2; }); } @@ -180,6 +192,11 @@ letter-spacing: 6px; } + input:disabled { + background-color: var(--color-primary-yellow); + border: none; + } + .rethink { display: flex; gap: 2px; @@ -240,13 +257,13 @@ - - {I18n.t("verify.serviceDesk.idCard.dayOfBirth")} + + bind:value={dayOfBirth}/>