From bf9efff9ef8a899dab328885391c3737b6e59c77 Mon Sep 17 00:00:00 2001 From: kiwan Date: Tue, 19 Mar 2024 12:42:46 -0400 Subject: [PATCH 1/4] refactor verifyEmailPage, get expired case to work again --- client/src/components/AuthClient.ts | 5 + client/src/containers/VerifyEmailPage.tsx | 111 +++++++++++++++------- 2 files changed, 82 insertions(+), 34 deletions(-) diff --git a/client/src/components/AuthClient.ts b/client/src/components/AuthClient.ts index 6e809fff..b9611d8d 100644 --- a/client/src/components/AuthClient.ts +++ b/client/src/components/AuthClient.ts @@ -236,9 +236,14 @@ const postAuthRequest = async ( try { if (result.ok) { + console.log("result is ok"); + return await result.json(); + } else { + console.log("result is not ok"); return await result.json(); } } catch { + console.log("in error"); return result; } }; diff --git a/client/src/containers/VerifyEmailPage.tsx b/client/src/containers/VerifyEmailPage.tsx index 03a4c0fc..382250a3 100644 --- a/client/src/containers/VerifyEmailPage.tsx +++ b/client/src/containers/VerifyEmailPage.tsx @@ -7,27 +7,12 @@ import Page from "components/Page"; const VerifyEmailPage = withI18n()((props: withI18nProps) => { const { i18n } = props; - const [user, setUser] = useState(); + const [loading, setLoading] = useState(true); const [isVerified, setIsVerified] = useState(false); + const [isAlreadyVerified, setIsAlreadyVerified] = useState(false); const [isExpired, setIsExpired] = useState(false); const [isEmailResent, setIsEmailResent] = useState(false); - - useEffect(() => { - const asyncFetchUser = async () => { - const _user = await AuthClient.fetchUser(); - if (_user) { - setUser({ - ..._user, - subscriptions: - _user.subscriptions?.map((s: any) => { - return { ...s }; - }) || [], - }); - setIsVerified(_user.verified); - } - }; - asyncFetchUser(); - }, []); + const [unknownError, setUnknownError] = useState(false); useEffect(() => { const asyncVerifyEmail = async () => { @@ -35,22 +20,43 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { }; asyncVerifyEmail().then((result) => { - if ( - result.statusCode === VerifyStatusCode.Success || - result.statusCode === VerifyStatusCode.AlreadyVerified - ) { - setIsVerified(true); - } - - if ( - result.statusCode === VerifyStatusCode.Expired || - result.statusCode === VerifyStatusCode.Unknown - ) { - setIsExpired(true); + switch (result.statusCode) { + case VerifyStatusCode.Success: + setIsVerified(true); + break; + case VerifyStatusCode.AlreadyVerified: + setIsVerified(true); + setIsAlreadyVerified(true); + break; + case VerifyStatusCode.Expired: + setIsExpired(true); + break; + default: + setUnknownError(true); } + console.log(result.statusCode); + console.log(result.statusText); + setLoading(false); }); }, []); + const delaySeconds = 5; + const baseUrl = window.location.origin; + const redirectUrl = `${baseUrl}/${i18n.language}`; + + const updateCountdown = () => { + let timeLeft = delaySeconds; + const delayInterval = delaySeconds * 100; + + setInterval(() => { + timeLeft && timeLeft--; // prevents counter from going below 0 + document.getElementById("countdown")!.textContent = timeLeft.toString(); + if (timeLeft <= 0) { + document.location.href = redirectUrl; + } + }, delayInterval); + }; + const expiredLinkPage = () => { const resendEmailPage = (
@@ -72,9 +78,8 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { Check your email inbox & spam
- Click the link we sent to verify your email address {!!user && user.email}. It may take a - few minutes to arrive. Once your email has been verified, you’ll be signed up for Data - Updates. + Click the link we sent to verify your email address. It may take a few minutes to arrive. + Once your email has been verified, you’ll be signed up for Data Updates.
); @@ -82,6 +87,19 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { return isEmailResent ? resendEmailConfirmation : resendEmailPage; }; + // TODO add error logging + const errorPage = () => ( +
+ We’re having trouble verifying your email at this time. +
+ + Please try again later. If you’re still having issues, contact support@justfix.org. +
+
A report about this issue has been sent to our team. +
+
+ ); + const successPage = () => (
Email verified @@ -90,11 +108,36 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => {
); + const alreadyVerifiedPage = () => ( +
+ Your email is already verified +
+ You will be redirected back to Who Owns What in: +
+
{updateCountdown()}
+ + {delaySeconds} seconds + +
+
+ If you are not redirected, please click this link: +
+ {redirectUrl} +
+ ); + return (
- {isVerified ? successPage() : isExpired && expiredLinkPage()} + {!loading && + (isVerified + ? isAlreadyVerified + ? alreadyVerifiedPage() + : successPage() + : isExpired + ? expiredLinkPage() + : errorPage())}
From d5389b24198a17fad9cc66342c97081483157606 Mon Sep 17 00:00:00 2001 From: kiwan Date: Tue, 19 Mar 2024 18:56:33 -0400 Subject: [PATCH 2/4] add endpoint for email verification with user token --- client/src/components/AuthClient.ts | 24 ++++++++++++++++++----- client/src/containers/VerifyEmailPage.tsx | 11 ++++++----- jfauthprovider/urls.py | 7 ++++++- jfauthprovider/views.py | 21 ++++++++++++++++++-- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/client/src/components/AuthClient.ts b/client/src/components/AuthClient.ts index b9611d8d..9948bd19 100644 --- a/client/src/components/AuthClient.ts +++ b/client/src/components/AuthClient.ts @@ -118,15 +118,32 @@ const verifyEmail = async () => { /** * Sends request to resend the account verification link to the user's email */ -const resendVerifyEmail = async () => { +const resendVerifyEmail = async (token?: string) => { try { - await postAuthRequest(`${BASE_URL}auth/resend_verify_email`); + if (token) { + await postAuthRequest(`${BASE_URL}auth/resend_verification_with_token?u=${token}`); + } else { + await postAuthRequest(`${BASE_URL}auth/resend_verification`); + } return true; } catch { return false; } }; +// /** +// * Sends unauthenticated request for a new verification email +// * Used only in expired verification flow in logged out state +// */ +// const resendVerificationWithoutAuth = async (token: string) => { +// try { +// await postAuthRequest(`${BASE_URL}auth/token_resend_verify_email?u=${token}`); +// return true; +// } catch { +// return false; +// } +// }; + /** * Sends an authenticated request to update the user email */ @@ -236,14 +253,11 @@ const postAuthRequest = async ( try { if (result.ok) { - console.log("result is ok"); return await result.json(); } else { - console.log("result is not ok"); return await result.json(); } } catch { - console.log("in error"); return result; } }; diff --git a/client/src/containers/VerifyEmailPage.tsx b/client/src/containers/VerifyEmailPage.tsx index 382250a3..746adf91 100644 --- a/client/src/containers/VerifyEmailPage.tsx +++ b/client/src/containers/VerifyEmailPage.tsx @@ -2,17 +2,20 @@ import { useState, useEffect } from "react"; import { withI18n, withI18nProps } from "@lingui/react"; import { Trans, t } from "@lingui/macro"; import AuthClient, { VerifyStatusCode } from "../components/AuthClient"; -import { JustfixUser } from "state-machine"; import Page from "components/Page"; +import { useLocation } from "react-router-dom"; const VerifyEmailPage = withI18n()((props: withI18nProps) => { const { i18n } = props; + const { search } = useLocation(); const [loading, setLoading] = useState(true); const [isVerified, setIsVerified] = useState(false); const [isAlreadyVerified, setIsAlreadyVerified] = useState(false); const [isExpired, setIsExpired] = useState(false); const [isEmailResent, setIsEmailResent] = useState(false); const [unknownError, setUnknownError] = useState(false); + const params = new URLSearchParams(search); + const token = params.get("u") || ""; useEffect(() => { const asyncVerifyEmail = async () => { @@ -34,8 +37,6 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { default: setUnknownError(true); } - console.log(result.statusCode); - console.log(result.statusText); setLoading(false); }); }, []); @@ -65,7 +66,7 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { );