diff --git a/src/components/App/useLoginWithKubeconfigID.ts b/src/components/App/useLoginWithKubeconfigID.ts index bf2c5875e5..6e436d789f 100644 --- a/src/components/App/useLoginWithKubeconfigID.ts +++ b/src/components/App/useLoginWithKubeconfigID.ts @@ -1,7 +1,7 @@ import { addByContext } from 'components/Clusters/shared'; import { ClustersState, clustersAtom } from 'state/clustersAtom'; import { SetStateAction, useAtom, useAtomValue } from 'jotai'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { NavigateFunction, useNavigate, useSearchParams } from 'react-router'; import { useTranslation } from 'react-i18next'; import jsyaml from 'js-yaml'; @@ -26,7 +26,7 @@ import { import { useNotification } from 'shared/contexts/NotificationContext'; import { manualKubeConfigIdAtom, - ManualKubeConfigIdType, + ManualKubeConfigIdController, } from 'state/manualKubeConfigIdAtom'; export interface KubeconfigIdFeature extends ConfigFeature { @@ -66,7 +66,7 @@ export async function loadKubeconfigById( return payload; } -const addClusters = async ( +const addClusters = ( kubeconfig: ValidKubeconfig, clusters: ClustersState, clusterInfo: useClustersInfoType, @@ -74,12 +74,7 @@ const addClusters = async ( t: TFunction, notification?: any, navigate?: NavigateFunction, - manualKubeConfigId?: { - manualKubeConfigId?: ManualKubeConfigIdType; - setManualKubeConfigId?: ( - update: SetStateAction, - ) => void; - }, + manualKubeConfigId?: ManualKubeConfigIdController, ) => { const isOnlyOneCluster = kubeconfig.contexts.length === 1; const currentContext = kubeconfig['current-context']; @@ -133,12 +128,7 @@ const loadKubeconfigIdCluster = async ( clusterInfo: useClustersInfoType, t: TFunction, setContextsState?: (update: SetStateAction) => void, - manualKubeConfigId?: { - manualKubeConfigId?: ManualKubeConfigIdType; - setManualKubeConfigId?: ( - update: SetStateAction, - ) => void; - }, + manualKubeConfigId?: ManualKubeConfigIdController, ) => { try { const kubeconfig = await loadKubeconfigById( @@ -201,6 +191,7 @@ export function useLoginWithKubeconfigID() { const { setCurrentCluster } = clusterInfo; const [handledKubeconfigId, setHandledKubeconfigId] = useState('not started'); + const lastProcessedKubeconfigIdRef = useRef(null); useEffect(() => { if (contextsState?.chosenContext) { @@ -252,21 +243,26 @@ export function useLoginWithKubeconfigID() { useEffect(() => { const dependenciesReady = !!configuration?.features && !!clusters; const flowStarted = handledKubeconfigId !== 'not started'; + const kubeconfigId = search.get('kubeconfigID'); - if (search.get('kubeconfigID') && flowStarted) { - setCurrentCluster(undefined); + if (kubeconfigId && flowStarted) { + if (kubeconfigId !== lastProcessedKubeconfigIdRef.current) { + setCurrentCluster(undefined); + setHandledKubeconfigId('not started'); + } + return; } if (!dependenciesReady || flowStarted) { return; } - const kubeconfigId = search.get('kubeconfigID'); if (!kubeconfigId || !kubeconfigIdFeature?.isEnabled) { setHandledKubeconfigId('done'); return; } + lastProcessedKubeconfigIdRef.current = kubeconfigId; setHandledKubeconfigId('loading'); loadKubeconfigIdCluster( kubeconfigId, @@ -282,7 +278,6 @@ export function useLoginWithKubeconfigID() { } }); }, [ - contextsState, search, clusters, kubeconfigIdFeature, diff --git a/src/components/Clusters/shared.ts b/src/components/Clusters/shared.ts index cad11439a7..91c33a49f5 100644 --- a/src/components/Clusters/shared.ts +++ b/src/components/Clusters/shared.ts @@ -11,9 +11,12 @@ import { useClustersInfoType } from 'state/utils/getClustersInfo'; import { tryParseOIDCparams } from './components/oidc-params'; import { hasNonOidcAuth, createUserManager } from 'state/authDataAtom'; import { useNavigate } from 'react-router'; -import { SetStateAction, useSetAtom } from 'jotai'; +import { useSetAtom } from 'jotai'; import { removePreviousPath } from 'state/useAfterInitHook'; -import { ManualKubeConfigIdType } from 'state/manualKubeConfigIdAtom'; +import { + ManualKubeConfigIdController, + ManualKubeConfigIdType, +} from 'state/manualKubeConfigIdAtom'; import { parseOIDCparams } from 'components/Clusters/components/oidc-params'; export type Users = Array<{ @@ -163,12 +166,7 @@ export const addByContext = ( config: any; }, clustersInfo: useClustersInfoType, - manualKubeConfigId?: { - manualKubeConfigId?: ManualKubeConfigIdType; - setManualKubeConfigId?: ( - update: SetStateAction, - ) => void; - }, + manualKubeConfigId?: ManualKubeConfigIdController, ) => { let kubeconfig = userKubeconfig as ValidKubeconfig; const findUser = () => diff --git a/src/state/authDataAtom.ts b/src/state/authDataAtom.ts index 98e4a82cda..e21a17fb3d 100644 --- a/src/state/authDataAtom.ts +++ b/src/state/authDataAtom.ts @@ -170,7 +170,12 @@ export function useAuthHandler() { const onAfterLogin = () => { setIsLoading(false); - if (!getPreviousPath() || getPreviousPath() === '/clusters') { + // Only auto-navigate after an OIDC redirect (which always lands on '/'). + const isOidcCallbackPath = window.location.pathname === '/'; + if ( + isOidcCallbackPath && + (!getPreviousPath() || getPreviousPath() === '/clusters') + ) { if (cluster.currentContext.namespace) { navigate( `/cluster/${encodeURIComponent(cluster.name)}/namespaces/${ diff --git a/src/state/clusterAtom.ts b/src/state/clusterAtom.ts index bfd49a7223..b7f084ea8c 100644 --- a/src/state/clusterAtom.ts +++ b/src/state/clusterAtom.ts @@ -36,6 +36,12 @@ const getClusters = () => { }; const getInitialClusterState = (): ActiveClusterState => { + // Don't restore a previous cluster when a kubeconfigID is present — it + // would race with and override the kubeconfigID flow. + if (new URLSearchParams(window.location.search).get('kubeconfigID')) { + return null; + } + const clusters = getClusters(); const clusterName = localStorage.getItem(CLUSTER_NAME_STORAGE_KEY); diff --git a/src/state/manualKubeConfigIdAtom.ts b/src/state/manualKubeConfigIdAtom.ts index 27405e6bda..c5014e2f9f 100644 --- a/src/state/manualKubeConfigIdAtom.ts +++ b/src/state/manualKubeConfigIdAtom.ts @@ -1,10 +1,17 @@ -import { atom } from 'jotai'; +import { atom, SetStateAction } from 'jotai'; export type ManualKubeConfigIdType = { formOpen: boolean; auth: any; }; +export type ManualKubeConfigIdController = { + manualKubeConfigId?: ManualKubeConfigIdType; + setManualKubeConfigId?: ( + update: SetStateAction, + ) => void; +}; + const defaultValue = { formOpen: false, auth: null }; export const manualKubeConfigIdAtom =