-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Expand file tree
/
Copy pathauthentication-wrapper.tsx
More file actions
154 lines (134 loc) · 5.49 KB
/
Copy pathauthentication-wrapper.tsx
File metadata and controls
154 lines (134 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import type { ReactNode } from "react";
import { observer } from "mobx-react";
import { useSearchParams, usePathname } from "next/navigation";
import useSWR from "swr";
// components
import { LogoSpinner } from "@/components/common/logo-spinner";
// helpers
import { isValidNextPath } from "@plane/utils";
import { EPageTypes } from "@/helpers/authentication.helper";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUser, useUserProfile, useUserSettings } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
type TPageType = EPageTypes;
type TAuthenticationWrapper = {
children: ReactNode;
pageType?: TPageType;
};
// Delegates to the shared isValidNextPath (@plane/utils) instead of a local
// reimplementation. A from-scratch version here previously resolved the
// value against location.origin and required the result to stay
// same-origin — which has its own gap: a next_path like "http:evil.com"
// resolves AS IF relative whenever the input's scheme happens to match the
// real origin's own scheme, e.g. any self-hosted deployment actually
// serving over plain http (verified directly: this bypasses the
// location.origin-based check on an http:// origin, though not on https://,
// since the schemes then differ). isValidNextPath closes this by requiring
// a literal leading "/" (and rejecting "//") before any URL-based
// comparison, so it doesn't depend on which scheme the real origin happens
// to use.
const isValidURL = isValidNextPath;
export const AuthenticationWrapper = observer(function AuthenticationWrapper(props: TAuthenticationWrapper) {
const pathname = usePathname();
const router = useAppRouter();
const searchParams = useSearchParams();
const nextPath = searchParams.get("next_path");
// props
const { children, pageType = EPageTypes.AUTHENTICATED } = props;
// hooks
const { isLoading: isUserLoading, data: currentUser, fetchCurrentUser } = useUser();
const { data: currentUserProfile } = useUserProfile();
const { data: currentUserSettings } = useUserSettings();
const { loader: workspacesLoader, workspaces } = useWorkspace();
const { isLoading: isUserSWRLoading } = useSWR("USER_INFORMATION", async () => await fetchCurrentUser(), {
revalidateOnFocus: false,
shouldRetryOnError: false,
});
const isUserOnboard =
currentUserProfile?.is_onboarded ||
(currentUserProfile?.onboarding_step?.profile_complete &&
currentUserProfile?.onboarding_step?.workspace_create &&
currentUserProfile?.onboarding_step?.workspace_invite &&
currentUserProfile?.onboarding_step?.workspace_join) ||
false;
const getWorkspaceRedirectionUrl = (): string => {
let redirectionRoute = "/create-workspace";
// validating the nextPath from the router query
if (nextPath && isValidURL(nextPath.toString())) {
redirectionRoute = nextPath.toString();
return redirectionRoute;
}
// validate the last and fallback workspace_slug
const currentWorkspaceSlug =
currentUserSettings?.workspace?.last_workspace_slug || currentUserSettings?.workspace?.fallback_workspace_slug;
// validate the current workspace_slug is available in the user's workspace list
const isCurrentWorkspaceValid = Object.values(workspaces || {}).findIndex(
(workspace) => workspace.slug === currentWorkspaceSlug
);
if (isCurrentWorkspaceValid >= 0) redirectionRoute = `/${currentWorkspaceSlug}`;
return redirectionRoute;
};
if ((isUserSWRLoading || isUserLoading || workspacesLoader) && !currentUser?.id)
return (
<div className="relative flex h-screen w-full items-center justify-center">
<LogoSpinner />
</div>
);
if (pageType === EPageTypes.PUBLIC) return <>{children}</>;
if (pageType === EPageTypes.NON_AUTHENTICATED) {
if (!currentUser?.id) return <>{children}</>;
else {
if (currentUserProfile?.id && isUserOnboard) {
const currentRedirectRoute = getWorkspaceRedirectionUrl();
router.push(currentRedirectRoute);
return <></>;
} else {
router.push("/onboarding");
return <></>;
}
}
}
if (pageType === EPageTypes.ONBOARDING) {
if (!currentUser?.id) {
router.push(`/${pathname ? `?next_path=${pathname}` : ``}`);
return <></>;
} else {
if (currentUser && currentUserProfile?.id && isUserOnboard) {
const currentRedirectRoute = getWorkspaceRedirectionUrl();
router.replace(currentRedirectRoute);
return <></>;
} else return <>{children}</>;
}
}
if (pageType === EPageTypes.SET_PASSWORD) {
if (!currentUser?.id) {
router.push(`/${pathname ? `?next_path=${pathname}` : ``}`);
return <></>;
} else {
if (currentUser && !currentUser?.is_password_autoset && currentUserProfile?.id && isUserOnboard) {
const currentRedirectRoute = getWorkspaceRedirectionUrl();
router.push(currentRedirectRoute);
return <></>;
} else return <>{children}</>;
}
}
if (pageType === EPageTypes.AUTHENTICATED) {
if (currentUser?.id) {
if (currentUserProfile && currentUserProfile?.id && isUserOnboard) return <>{children}</>;
else {
router.push(`/onboarding`);
return <></>;
}
} else {
router.push(`/${pathname ? `?next_path=${pathname}` : ``}`);
return <></>;
}
}
return <>{children}</>;
});