From 3e0a38931ff8c8b7be8f6db4ac281f9f94c095a1 Mon Sep 17 00:00:00 2001 From: sebadob Date: Wed, 25 Oct 2023 13:51:41 +0200 Subject: [PATCH] handle HTTP 429 during login nicely for better UX --- .../src/routes/oidc/authorize/+page.svelte | 51 +++++++++++++++---- frontend/src/utils/helpers.js | 34 ------------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/frontend/src/routes/oidc/authorize/+page.svelte b/frontend/src/routes/oidc/authorize/+page.svelte index 417f377f..de3d3e01 100644 --- a/frontend/src/routes/oidc/authorize/+page.svelte +++ b/frontend/src/routes/oidc/authorize/+page.svelte @@ -2,7 +2,7 @@ import {onMount, tick} from "svelte"; import {authorize, authorizeRefresh, getClientLogo, postPasswordResetRequest} from "../../../utils/dataFetching.js"; import * as yup from 'yup'; - import {extractFormErrors, getQueryParams, saveCsrfToken} from "../../../utils/helpers.js"; + import {extractFormErrors, formatDateFromTs, getQueryParams, saveCsrfToken} from "../../../utils/helpers.js"; import Button from "$lib/Button.svelte"; import WebauthnRequest from "../../../components/webauthn/WebauthnRequest.svelte"; import {scale} from 'svelte/transition'; @@ -44,6 +44,7 @@ let showReset = false; let showResetRequest = false; let emailSuccess = false; + let tooManyRequests = false; let emailAfterSubmit = ''; let formValues = {email: '', password: ''}; @@ -134,6 +135,8 @@ } async function onSubmit() { + err = ''; + try { await schema.validate(formValues, {abortEarly: false}); formErrors = {}; @@ -172,6 +175,23 @@ } else if (res.status === 200) { err = ''; webauthnData = await res.json(); + } else if (res.status === 429) { + let notBefore = Number.parseInt(res.headers.get('x-retry-not-before')); + let nbfDate = formatDateFromTs(notBefore); + let diff = notBefore * 1000 - new Date().getTime(); + console.log(diff); + + tooManyRequests = true; + err = `${t.http429} ${nbfDate}`; + + formValues.email = ''; + formValues.password = ''; + needsPassword = false; + + setTimeout(() => { + tooManyRequests = false; + err = ''; + }, diff); } else if (!needsPassword) { // this will happen always if the user does the first try with a password-only account // the good thing about this is, that it is a prevention against autofill passwords from the browser @@ -189,6 +209,8 @@ // a password and afterward changes his email again if (needsPassword && emailAfterSubmit !== formValues.email) { needsPassword = false; + formValues.password = ''; + err = ''; } } @@ -270,6 +292,7 @@ bind:error={formErrors.email} autocomplete="email" placeholder={t.email} + disabled={tooManyRequests} on:enter={onSubmit} on:input={onEmailInput} > @@ -284,12 +307,13 @@ bind:error={formErrors.password} autocomplete="current-password" placeholder={t.password} + disabled={tooManyRequests} on:enter={onSubmit} > {t.password?.toUpperCase()} - {#if showResetRequest} + {#if showResetRequest && !tooManyRequests}
- -
- {:else} -
- -
+ {#if !tooManyRequests} + {#if showReset} +
+ +
+ {:else} +
+ +
+ {/if} {/if} {#if err} @@ -346,6 +376,7 @@ } .errMsg { + max-width: 15rem; margin: -5px 10px 0 10px; color: var(--col-err) } diff --git a/frontend/src/utils/helpers.js b/frontend/src/utils/helpers.js index 156c57b5..ecab660e 100644 --- a/frontend/src/utils/helpers.js +++ b/frontend/src/utils/helpers.js @@ -13,26 +13,6 @@ import { import sjcl from "sjcl"; import { decode, encode } from "base64-arraybuffer"; -// export function getCookie(cname) { -// let name = cname + "="; -// let decodedCookie = decodeURIComponent(document.cookie); -// let ca = decodedCookie.split(';'); -// for (let i = 0; i < ca.length; i++) { -// let c = ca[i]; -// while (c.charAt(0) === ' ') { -// c = c.substring(1); -// } -// if (c.indexOf(name) === 0) { -// return c.substring(name.length, c.length); -// } -// } -// return ""; -// } -// -// function deleteCookie(name) { -// document.cookie = name + '=; Max-Age=-1;'; -// } - export function extractFormErrors(err) { return err.inner.reduce((acc, err) => { return {...acc, [err.path]: err.message}; @@ -72,18 +52,9 @@ export const saveIdToken = (token) => { localStorage.setItem(ID_TOKEN, token); } -export const getIdToken = () => { - return localStorage.getItem(ID_TOKEN) || ''; -} - export const saveAccessToken = (token) => { localStorage.setItem(ACCESS_TOKEN, token); } - -export const getAccessToken = () => { - return localStorage.getItem(ACCESS_TOKEN) || ''; -} - export const getVerifierFromStorage = () => { return localStorage.getItem(PKCE_VERIFIER) || ''; }; @@ -134,11 +105,6 @@ export const computePow = (powChallenge) => { } -// export const dateFromUtcTs = (ts) => { -// const utcOffsetMinutes = -new Date().getTimezoneOffset(); -// return new Date((ts + utcOffsetMinutes * 60) * 1000); -// } - export const formatDateToDateInput = date => { return date.toISOString().split('.')[0]; }