From 2988d5e429f869bd14acf70e3517597fac56c664 Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Fri, 17 May 2024 14:27:49 +0530 Subject: [PATCH] [WEB-1319] chore: handled redirection when user is not logged in (#4497) * chore: handled redirection when user is not logged in * dev: handle url redirection in space app * dev: remove user from redis on successful code matching --- .../provider/credentials/magic_code.py | 2 + .../plane/authentication/views/space/email.py | 72 ++++--------------- .../authentication/views/space/github.py | 29 ++------ .../authentication/views/space/google.py | 31 ++------ .../plane/authentication/views/space/magic.py | 44 +++--------- .../views/space/password_management.py | 23 ++---- space/app/page.tsx | 18 ++--- .../account/auth-forms/auth-root.tsx | 3 + .../account/auth-forms/password.tsx | 5 +- .../account/auth-forms/unique-code.tsx | 4 +- .../account/oauth/oauth-options.tsx | 4 +- .../comment/comment-reactions.tsx | 19 ++++- .../peek-overview/issue-emoji-reactions.tsx | 20 +++++- .../peek-overview/issue-vote-reactions.tsx | 20 +++++- 14 files changed, 119 insertions(+), 175 deletions(-) diff --git a/apiserver/plane/authentication/provider/credentials/magic_code.py b/apiserver/plane/authentication/provider/credentials/magic_code.py index aa66b42f1..f21a65a89 100644 --- a/apiserver/plane/authentication/provider/credentials/magic_code.py +++ b/apiserver/plane/authentication/provider/credentials/magic_code.py @@ -125,6 +125,8 @@ class MagicCodeProvider(CredentialAdapter): }, } ) + # Delete the token from redis if the code match is successful + ri.delete(self.key) return else: raise AuthenticationException( diff --git a/apiserver/plane/authentication/views/space/email.py b/apiserver/plane/authentication/views/space/email.py index 73690e2fa..7a5613a75 100644 --- a/apiserver/plane/authentication/views/space/email.py +++ b/apiserver/plane/authentication/views/space/email.py @@ -1,5 +1,5 @@ # Python imports -from urllib.parse import urlencode, urljoin +from urllib.parse import urlencode # Django imports from django.core.exceptions import ValidationError @@ -36,10 +36,7 @@ class SignInAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # set the referer as session to redirect after login @@ -58,10 +55,7 @@ class SignInAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # Validate email @@ -77,10 +71,7 @@ class SignInAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # Existing User @@ -95,10 +86,7 @@ class SignInAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) if not existing_user.is_active: @@ -111,10 +99,7 @@ class SignInAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: @@ -125,19 +110,13 @@ class SignInAuthSpaceEndpoint(View): # Login the user and record his device info user_login(request=request, user=user, is_space=True) # redirect to next path - url = urljoin( - base_host(request=request, is_space=True), - str(next_path) if next_path else "", - ) + url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}" return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) @@ -158,10 +137,7 @@ class SignUpAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) email = request.POST.get("email", False) @@ -179,10 +155,7 @@ class SignUpAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # Validate the email email = email.strip().lower() @@ -198,10 +171,7 @@ class SignUpAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # Existing User @@ -218,10 +188,7 @@ class SignUpAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) exc = AuthenticationException( @@ -232,10 +199,7 @@ class SignUpAuthSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: @@ -246,17 +210,11 @@ class SignUpAuthSpaceEndpoint(View): # Login the user and record his device info user_login(request=request, user=user, is_space=True) # redirect to referer path - url = urljoin( - base_host(request=request, is_space=True), - str(next_path) if next_path else "", - ) + url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}" return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/space/github.py b/apiserver/plane/authentication/views/space/github.py index 8430cbdfb..711f7eaa7 100644 --- a/apiserver/plane/authentication/views/space/github.py +++ b/apiserver/plane/authentication/views/space/github.py @@ -1,6 +1,6 @@ # Python imports import uuid -from urllib.parse import urlencode, urljoin +from urllib.parse import urlencode # Django import from django.http import HttpResponseRedirect @@ -38,10 +38,7 @@ class GitHubOauthInitiateSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: @@ -54,10 +51,7 @@ class GitHubOauthInitiateSpaceEndpoint(View): params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) @@ -79,10 +73,7 @@ class GitHubCallbackSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) if not code: @@ -95,10 +86,7 @@ class GitHubCallbackSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: @@ -111,14 +99,11 @@ class GitHubCallbackSpaceEndpoint(View): user_login(request=request, user=user, is_space=True) # Process workspace and project invitations # redirect to referer path - url = urljoin(base_host, str(next_path) if next_path else "") + url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}" return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/space/google.py b/apiserver/plane/authentication/views/space/google.py index 502f146c3..38a2b910a 100644 --- a/apiserver/plane/authentication/views/space/google.py +++ b/apiserver/plane/authentication/views/space/google.py @@ -1,6 +1,6 @@ # Python imports import uuid -from urllib.parse import urlencode, urljoin +from urllib.parse import urlencode # Django import from django.http import HttpResponseRedirect @@ -36,10 +36,7 @@ class GoogleOauthInitiateSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: @@ -52,10 +49,7 @@ class GoogleOauthInitiateSpaceEndpoint(View): params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) @@ -76,10 +70,7 @@ class GoogleCallbackSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) if not code: exc = AuthenticationException( @@ -91,10 +82,7 @@ class GoogleCallbackSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = next_path - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: provider = GoogleOAuthProvider( @@ -105,16 +93,11 @@ class GoogleCallbackSpaceEndpoint(View): # Login the user and record his device info user_login(request=request, user=user, is_space=True) # redirect to referer path - url = urljoin( - base_host, str(next_path) if next_path else "/spaces" - ) + url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}" return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/space/magic.py b/apiserver/plane/authentication/views/space/magic.py index dd5a10d84..0e859d44d 100644 --- a/apiserver/plane/authentication/views/space/magic.py +++ b/apiserver/plane/authentication/views/space/magic.py @@ -1,5 +1,5 @@ # Python imports -from urllib.parse import urlencode, urljoin +from urllib.parse import urlencode # Django imports from django.core.validators import validate_email @@ -84,10 +84,7 @@ class MagicSignInSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) existing_user = User.objects.filter(email=email).first() @@ -100,10 +97,7 @@ class MagicSignInSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # Active User @@ -117,10 +111,7 @@ class MagicSignInSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: provider = MagicCodeProvider( @@ -136,17 +127,14 @@ class MagicSignInSpaceEndpoint(View): else: # Get the redirection path path = str(next_path) if next_path else "" - url = urljoin(base_host(request=request, is_space=True), path) + url = f"{base_host(request=request, is_space=True)}{path}" return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) @@ -169,10 +157,7 @@ class MagicSignUpSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) # Existing User existing_user = User.objects.filter(email=email).first() @@ -185,10 +170,7 @@ class MagicSignUpSpaceEndpoint(View): params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: @@ -199,18 +181,12 @@ class MagicSignUpSpaceEndpoint(View): # Login the user and record his device info user_login(request=request, user=user, is_space=True) # redirect to referer path - url = urljoin( - base_host(request=request, is_space=True), - str(next_path) if next_path else "spaces", - ) + url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}" return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/space/password_management.py b/apiserver/plane/authentication/views/space/password_management.py index 5263c8956..fa20fa618 100644 --- a/apiserver/plane/authentication/views/space/password_management.py +++ b/apiserver/plane/authentication/views/space/password_management.py @@ -1,6 +1,6 @@ # Python imports import os -from urllib.parse import urlencode, urljoin +from urllib.parse import urlencode # Third party imports from rest_framework import status @@ -145,10 +145,7 @@ class ResetPasswordSpaceEndpoint(View): error_message="INVALID_PASSWORD_TOKEN", ) params = exc.get_error_dict() - url = urljoin( - base_host(request=request, is_space=True), - "accounts/reset-password?" + urlencode(params), - ) + url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(params)}" return HttpResponseRedirect(url) password = request.POST.get("password", False) @@ -158,10 +155,7 @@ class ResetPasswordSpaceEndpoint(View): error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"], error_message="INVALID_PASSWORD", ) - url = urljoin( - base_host(request=request, is_space=True), - "?" + urlencode(exc.get_error_dict()), - ) + url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}" return HttpResponseRedirect(url) # Check the password complexity @@ -171,11 +165,7 @@ class ResetPasswordSpaceEndpoint(View): error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"], error_message="INVALID_PASSWORD", ) - url = urljoin( - base_host(request=request, is_space=True), - "accounts/reset-password?" - + urlencode(exc.get_error_dict()), - ) + url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}" return HttpResponseRedirect(url) # set_password also hashes the password that the user will get @@ -193,8 +183,5 @@ class ResetPasswordSpaceEndpoint(View): ], error_message="EXPIRED_PASSWORD_TOKEN", ) - url = urljoin( - base_host(request=request, is_space=True), - "accounts/reset-password?" + urlencode(exc.get_error_dict()), - ) + url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}" return HttpResponseRedirect(url) diff --git a/space/app/page.tsx b/space/app/page.tsx index 1dbadcf60..e1522b14b 100644 --- a/space/app/page.tsx +++ b/space/app/page.tsx @@ -1,4 +1,5 @@ "use client"; + import { observer } from "mobx-react-lite"; import useSWR from "swr"; // components @@ -9,17 +10,18 @@ import { AuthView } from "@/components/views"; import { useUser } from "@/hooks/store"; function HomePage() { - const { fetchCurrentUser, isAuthenticated, isLoading } = useUser(); + const { data: currentUser, fetchCurrentUser, isAuthenticated, isLoading } = useUser(); - useSWR("CURRENT_USER", () => fetchCurrentUser(), { errorRetryCount: 0 }); + useSWR("CURRENT_USER", () => fetchCurrentUser(), { + errorRetryCount: 0, + revalidateIfStale: false, + revalidateOnFocus: false, + refreshWhenHidden: false, + }); - if (isLoading) { - return ; - } + if (isLoading) return ; - if (isAuthenticated) { - return ; - } + if (currentUser && isAuthenticated) return ; return ; } diff --git a/space/components/account/auth-forms/auth-root.tsx b/space/components/account/auth-forms/auth-root.tsx index 0d1a13eb0..273e5bbd5 100644 --- a/space/components/account/auth-forms/auth-root.tsx +++ b/space/components/account/auth-forms/auth-root.tsx @@ -35,6 +35,7 @@ export const AuthRoot: FC = observer(() => { const searchParams = useSearchParams(); const emailParam = searchParams.get("email") || undefined; const error_code = searchParams.get("error_code") || undefined; + const nextPath = searchParams.get("next_path") || undefined; // states const [authMode, setAuthMode] = useState(EAuthModes.SIGN_UP); const [authStep, setAuthStep] = useState(EAuthSteps.EMAIL); @@ -141,6 +142,7 @@ export const AuthRoot: FC = observer(() => { { setEmail(""); setAuthStep(EAuthSteps.EMAIL); @@ -154,6 +156,7 @@ export const AuthRoot: FC = observer(() => { isPasswordAutoset={isPasswordAutoset} isSMTPConfigured={isSMTPConfigured} email={email} + nextPath={nextPath} handleEmailClear={() => { setEmail(""); setAuthStep(EAuthSteps.EMAIL); diff --git a/space/components/account/auth-forms/password.tsx b/space/components/account/auth-forms/password.tsx index 9ec549bc2..76a355827 100644 --- a/space/components/account/auth-forms/password.tsx +++ b/space/components/account/auth-forms/password.tsx @@ -19,6 +19,7 @@ type Props = { isPasswordAutoset: boolean; isSMTPConfigured: boolean; mode: EAuthModes; + nextPath: string | undefined; handleEmailClear: () => void; handleAuthStep: (step: EAuthSteps) => void; }; @@ -37,7 +38,7 @@ const defaultValues: TPasswordFormValues = { const authService = new AuthService(); export const AuthPasswordForm: React.FC = observer((props: Props) => { - const { email, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props; + const { email, nextPath, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props; // states const [csrfToken, setCsrfToken] = useState(undefined); const [passwordFormData, setPasswordFormData] = useState({ ...defaultValues, email }); @@ -65,6 +66,7 @@ export const AuthPasswordForm: React.FC = observer((props: Props) => { }; const passwordSupport = passwordFormData.password.length > 0 && + mode === EAuthModes.SIGN_UP && (getPasswordStrength(passwordFormData.password) < 3 || isPasswordInputFocused) && ( ); @@ -92,6 +94,7 @@ export const AuthPasswordForm: React.FC = observer((props: Props) => { > +