Skip to content

Commit 3799026

Browse files
Refactor for federated login to prevent conflicts with internal (#1638)
<!-- Ensure the title clearly reflects what was changed. Provide a clear and concise description of the changes made. The PR should only contain the changes related to the issue, and no other unrelated changes. --> Fixes OPS-2947
1 parent 3a00f9d commit 3799026

File tree

13 files changed

+130
-79
lines changed

13 files changed

+130
-79
lines changed

packages/react-ui/src/app/app.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
DefaultErrorFunction,
44
SetErrorFunction,
55
} from '@sinclair/typebox/errors';
6-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
6+
import { QueryClientProvider } from '@tanstack/react-query';
77

88
import { ThemeProvider } from '@/app/common/providers/theme-provider';
99

@@ -13,9 +13,9 @@ import { InitialDataGuard } from './common/guards/intial-data-guard';
1313
import { Extensions } from './features/extensions';
1414
import './interceptors';
1515
import { useLogoutEventListener } from './lib/navigation-events';
16+
import { queryClient } from './lib/query-client';
1617
import { ApplicationRouter } from './router';
1718

18-
const queryClient = new QueryClient();
1919
let typesFormatsAdded = false;
2020

2121
if (!typesFormatsAdded) {

packages/react-ui/src/app/common/guards/allow-logged-in-user-only-guard.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const AllowOnlyLoggedInUserOnlyGuard = ({
4949
platformHooks.prefetchPlatform();
5050
platformHooks.prefetchNewerVersionInfo(queryClient);
5151

52-
flagsHooks.useFlags();
52+
const { data: flags } = flagsHooks.useFlags();
5353
userSettingsHooks.useUserSettings();
5454
userHooks.useUserMeta();
5555
appConnectionsHooks.useConnectionsMetadata();
@@ -61,6 +61,7 @@ export const AllowOnlyLoggedInUserOnlyGuard = ({
6161
await authenticationSession.logOut({
6262
userInitiated: false,
6363
navigate,
64+
federatedLoginUrl: flags?.FRONTEGG_URL as string | undefined,
6465
});
6566
} catch (e) {
6667
if (isMounted) {
@@ -75,7 +76,14 @@ export const AllowOnlyLoggedInUserOnlyGuard = ({
7576
return () => {
7677
isMounted = false;
7778
};
78-
}, [isLoggedIn, expired, location.pathname, location.search, navigate]);
79+
}, [
80+
isLoggedIn,
81+
expired,
82+
location.pathname,
83+
location.search,
84+
navigate,
85+
flags,
86+
]);
7987

8088
if (!isLoggedIn || expired) {
8189
return <Navigate to="/sign-in" replace />;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type FronteggAuthGuardProps = {
2+
children: React.ReactNode;
3+
};
4+
5+
export const FronteggAuthGuard = ({ children }: FronteggAuthGuardProps) => {
6+
return children;
7+
};

packages/react-ui/src/app/features/navigation/layout/global-layout.tsx

Lines changed: 73 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useCallback, useEffect, useState } from 'react';
88
import { Outlet, useLocation } from 'react-router-dom';
99

1010
import { AllowOnlyLoggedInUserOnlyGuard } from '@/app/common/guards/allow-logged-in-user-only-guard';
11+
import { FronteggAuthGuard } from '@/app/common/guards/frontegg-auth-guard';
1112
import { useResizablePanelGroup } from '@/app/common/hooks/use-resizable-panel-group';
1213
import { RESIZABLE_PANEL_IDS } from '@/app/constants/layout';
1314
import {
@@ -102,76 +103,78 @@ export function GlobalLayout() {
102103
}
103104

104105
return (
105-
<AllowOnlyLoggedInUserOnlyGuard>
106-
<div className="h-screen w-screen overflow-hidden">
107-
<ResizablePanelGroup
108-
direction="horizontal"
109-
id="page-container"
110-
onLayout={onResize}
111-
className="h-full"
112-
>
113-
<LeftSidebarResizablePanel
114-
minSize={
115-
isMinimized
116-
? GLOBAL_SIDEBAR_MINIMIZED_WIDTH
117-
: GLOBAL_SIDEBAR_MIN_SIZE
118-
}
119-
maxSize={
120-
isMinimized
121-
? GLOBAL_SIDEBAR_MINIMIZED_WIDTH
122-
: LEFT_SIDEBAR_MAX_SIZE
123-
}
124-
collapsedSize={
125-
isMinimized
126-
? GLOBAL_SIDEBAR_MINIMIZED_WIDTH
127-
: LEFT_SIDEBAR_MIN_SIZE
128-
}
129-
isDragging={isDragging}
130-
className={cn(
131-
LEFT_SIDEBAR_MIN_EFFECTIVE_WIDTH,
132-
'shadow-sidebar z-[12]',
133-
{
134-
'min-w-[70px] max-w-[70px]': isMinimized,
135-
},
136-
{
137-
'max-w-[400px]': !isMinimized,
138-
},
139-
)}
140-
>
141-
<DashboardSideMenu />
142-
</LeftSidebarResizablePanel>
143-
144-
<ResizableHandle
145-
className="bg-transparent"
146-
disabled={isMinimized}
147-
onDragging={setIsDragging}
148-
style={{
149-
width: '0px',
150-
}}
151-
/>
152-
153-
<AiChatResizablePanel onDragging={setIsDragging} />
154-
155-
<ResizablePanel
156-
id={RESIZABLE_PANEL_IDS.MAIN}
157-
order={3}
158-
className="flex-1 h-full overflow-hidden min-w-[900px] contain-layout"
159-
defaultSize={GLOBAL_MAIN_PANEL_DEFAULT_SIZE}
160-
minSize={GLOBAL_MAIN_PANEL_MIN_SIZE}
106+
<FronteggAuthGuard>
107+
<AllowOnlyLoggedInUserOnlyGuard>
108+
<div className="h-screen w-screen overflow-hidden">
109+
<ResizablePanelGroup
110+
direction="horizontal"
111+
id="page-container"
112+
onLayout={onResize}
113+
className="h-full"
161114
>
162-
<div className="relative h-full w-full">
163-
<AiConfigurationPrompt
164-
className={cn({
165-
'bottom-[60px]':
166-
location.pathname.startsWith('/flows/') ||
167-
location.pathname.startsWith('/runs/'),
168-
})}
169-
/>
170-
<Outlet />
171-
</div>
172-
</ResizablePanel>
173-
</ResizablePanelGroup>
174-
</div>
175-
</AllowOnlyLoggedInUserOnlyGuard>
115+
<LeftSidebarResizablePanel
116+
minSize={
117+
isMinimized
118+
? GLOBAL_SIDEBAR_MINIMIZED_WIDTH
119+
: GLOBAL_SIDEBAR_MIN_SIZE
120+
}
121+
maxSize={
122+
isMinimized
123+
? GLOBAL_SIDEBAR_MINIMIZED_WIDTH
124+
: LEFT_SIDEBAR_MAX_SIZE
125+
}
126+
collapsedSize={
127+
isMinimized
128+
? GLOBAL_SIDEBAR_MINIMIZED_WIDTH
129+
: LEFT_SIDEBAR_MIN_SIZE
130+
}
131+
isDragging={isDragging}
132+
className={cn(
133+
LEFT_SIDEBAR_MIN_EFFECTIVE_WIDTH,
134+
'shadow-sidebar z-[12]',
135+
{
136+
'min-w-[70px] max-w-[70px]': isMinimized,
137+
},
138+
{
139+
'max-w-[400px]': !isMinimized,
140+
},
141+
)}
142+
>
143+
<DashboardSideMenu />
144+
</LeftSidebarResizablePanel>
145+
146+
<ResizableHandle
147+
className="bg-transparent"
148+
disabled={isMinimized}
149+
onDragging={setIsDragging}
150+
style={{
151+
width: '0px',
152+
}}
153+
/>
154+
155+
<AiChatResizablePanel onDragging={setIsDragging} />
156+
157+
<ResizablePanel
158+
id={RESIZABLE_PANEL_IDS.MAIN}
159+
order={3}
160+
className="flex-1 h-full overflow-hidden min-w-[900px] contain-layout"
161+
defaultSize={GLOBAL_MAIN_PANEL_DEFAULT_SIZE}
162+
minSize={GLOBAL_MAIN_PANEL_MIN_SIZE}
163+
>
164+
<div className="relative h-full w-full">
165+
<AiConfigurationPrompt
166+
className={cn({
167+
'bottom-[60px]':
168+
location.pathname.startsWith('/flows/') ||
169+
location.pathname.startsWith('/runs/'),
170+
})}
171+
/>
172+
<Outlet />
173+
</div>
174+
</ResizablePanel>
175+
</ResizablePanelGroup>
176+
</div>
177+
</AllowOnlyLoggedInUserOnlyGuard>
178+
</FronteggAuthGuard>
176179
);
177180
}

packages/react-ui/src/app/interceptors.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { API_URL, isUrlRelative } from '@/app/lib/api';
22
import { authenticationSession } from '@/app/lib/authentication-session';
33
import axios, { AxiosError, HttpStatusCode } from 'axios';
44
import { OPENOPS_CLOUD_USER_INFO_API_URL } from './constants/cloud';
5+
import { QueryKeys } from './constants/query-keys';
6+
import { FlagsMap } from './lib/flags-api';
7+
import { queryClient } from './lib/query-client';
58

69
const unauthenticatedRoutes = [
710
'/v1/authentication/sign-in',
@@ -25,7 +28,7 @@ const needsAuthHeader = (url: string): boolean => {
2528
// Add request interceptor to append Authorization header
2629
axios.interceptors.request.use((config) => {
2730
const token = authenticationSession.getToken();
28-
if (token && needsAuthHeader(config.url!)) {
31+
if (token && config.url && needsAuthHeader(config.url)) {
2932
config.headers.Authorization = `Bearer ${token}`;
3033
}
3134
return config;
@@ -56,10 +59,15 @@ axios.interceptors.response.use(
5659

5760
if (url !== OPENOPS_CLOUD_USER_INFO_API_URL && !isSignInRoute) {
5861
console.warn('JWT expired logging out');
62+
63+
const flags = queryClient.getQueryData<FlagsMap>([QueryKeys.flags]);
5964
authenticationSession.logOut({
6065
userInitiated: false,
66+
federatedLoginUrl: flags?.FRONTEGG_URL as string | undefined,
6167
});
62-
window.location.reload();
68+
if (!flags?.FRONTEGG_URL) {
69+
window.location.reload();
70+
}
6371
}
6472
}
6573
return Promise.reject(error);

packages/react-ui/src/app/lib/authentication-session.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,11 @@ export const authenticationSession = {
7979
async logOut({
8080
userInitiated = false,
8181
navigate,
82+
federatedLoginUrl,
8283
}: {
8384
userInitiated: boolean;
8485
navigate?: NavigateFunction;
86+
federatedLoginUrl?: string;
8587
}) {
8688
await authenticationApi.signOut();
8789
localStorage.removeItem(currentUserKey);
@@ -92,6 +94,12 @@ export const authenticationSession = {
9294
localStorage.setItem(LOGOUT_EVENT_KEY, Date.now().toString());
9395
}
9496

97+
if (federatedLoginUrl) {
98+
// do not use saved path from navigationUtil as we will get validation error from Frontegg
99+
window.location.href = `${federatedLoginUrl}/oauth/logout?post_logout_redirect_uri=${window.location.origin}`;
100+
return;
101+
}
102+
95103
if (
96104
window.location.pathname === '/sign-in' ||
97105
window.location.pathname === '/sign-up'

packages/react-ui/src/app/routes/cloud-connection/frontegg-setup.ts renamed to packages/react-ui/src/app/lib/frontegg-setup.ts

File renamed without changes.
File renamed without changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { QueryClient } from '@tanstack/react-query';
2+
3+
export const queryClient = new QueryClient();

packages/react-ui/src/app/routes/cloud-connection/cloud-connection-page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ import { flagsHooks } from '@/app/common/hooks/flags-hooks';
33
import { ORGIN_PROJECT_ID_KEY, ORGIN_USER_ID_KEY } from '@/app/constants/cloud';
44
import { cloudUserApi } from '@/app/features/cloud/lib/cloud-user-api';
55
import { getProjectIdSearchParam } from '@/app/features/cloud/lib/utils';
6+
import {
7+
additionalFronteggParams,
8+
initializeFrontegg,
9+
} from '@/app/lib/frontegg-setup';
10+
import { getExpirationDate } from '@/app/lib/jwt-utils';
611
import { CloudLoggedInBrief } from '@openops/components/ui';
712
import Cookies from 'js-cookie';
813
import { useEffect, useState } from 'react';
914
import { useNavigate } from 'react-router-dom';
1015
import { useEffectOnce } from 'react-use';
11-
import { additionalFronteggParams, initializeFrontegg } from './frontegg-setup';
12-
import { getExpirationDate } from './jwt-utils';
1316

1417
const CloudConnectionPage = () => {
1518
const navigate = useNavigate();

0 commit comments

Comments
 (0)