Skip to content

Commit

Permalink
Merge pull request #1345 from flanksource/fix-clerk-kratos-switching-…
Browse files Browse the repository at this point in the history
…not-working

fix: fix switch between clerk and kratos not working
  • Loading branch information
mainawycliffe authored Aug 28, 2023
2 parents 91ae60f + a5724bd commit 02250b0
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 71 deletions.
52 changes: 8 additions & 44 deletions src/components/Authentication/AuthProviderWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { useOrganization } from "@clerk/nextjs";
import { useQuery } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { User, whoami } from "../../api/services/users";
import { AuthContext } from "../../context";
import ErrorPage from "../Errors/ErrorPage";
import FullPageSkeletonLoader from "../SkeletonLoader/FullPageSkeletonLoader";
import InstanceCreationInProgress from "./Clerk/InstanceCreationInProgress";
import ClerkAuthContextProvider from "./Clerk/ClerkAuthContextProvider";
import KratosAuthContextProvider from "./Kratos/KratosAuthContextProvider";
import useDetermineAuthSystem from "./useDetermineAuthSystem";

type AuthProviderWrapperProps = {
children: React.ReactNode;
Expand All @@ -14,43 +9,12 @@ type AuthProviderWrapperProps = {
export default function AuthProviderWrapper({
children
}: AuthProviderWrapperProps) {
// when organization is switched, we need to re-fetch the user and the UI
const { organization } = useOrganization();
const authSystem = useDetermineAuthSystem();

const {
data: user,
isLoading,
error
} = useQuery<User, AxiosError>(
["user", "whoami", organization],
() => whoami(),
{
refetchOnWindowFocus: false,
refetchInterval: 0,
refetchOnReconnect: false
}
);

if (isLoading && !user) {
return <FullPageSkeletonLoader />;
}

// if the organization backend is not yet created, we need to wait for it to
if (
error &&
(error.response?.status?.toString().startsWith("5") ||
error?.response?.status === 404)
) {
return <InstanceCreationInProgress />;
}

if (error && !user) {
return <ErrorPage error={error} />;
// we need access to Clerk's organization context
if (authSystem === "clerk") {
return <ClerkAuthContextProvider>{children}</ClerkAuthContextProvider>;
}

return (
<AuthContext.Provider value={{ user, setUser: () => {} }}>
{children}
</AuthContext.Provider>
);
return <KratosAuthContextProvider>{children}</KratosAuthContextProvider>;
}
56 changes: 56 additions & 0 deletions src/components/Authentication/Clerk/ClerkAuthContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useOrganization } from "@clerk/nextjs";
import { useQuery } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { User, whoami } from "../../../api/services/users";
import { AuthContext } from "../../../context";
import ErrorPage from "../../Errors/ErrorPage";
import FullPageSkeletonLoader from "../../SkeletonLoader/FullPageSkeletonLoader";
import InstanceCreationInProgress from "./InstanceCreationInProgress";

type AuthProviderWrapperProps = {
children: React.ReactNode;
};

export default function ClerkAuthContextProvider({
children
}: AuthProviderWrapperProps) {
// when organization is switched, we need to re-fetch the user and the UI
const { organization } = useOrganization();

const {
data: user,
isLoading,
error
} = useQuery<User, AxiosError>(
["user", "whoami", organization],
() => whoami(),
{
refetchOnWindowFocus: false,
refetchInterval: 0,
refetchOnReconnect: false
}
);

if (isLoading && !user) {
return <FullPageSkeletonLoader />;
}

// if the organization backend is not yet created, we need to wait for it to
if (
error &&
(error.response?.status?.toString().startsWith("5") ||
error?.response?.status === 404)
) {
return <InstanceCreationInProgress />;
}

if (error && !user) {
return <ErrorPage error={error} />;
}

return (
<AuthContext.Provider value={{ user, setUser: () => {} }}>
{children}
</AuthContext.Provider>
);
}
41 changes: 14 additions & 27 deletions src/components/Authentication/Kratos/KratosAuthContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,35 @@
import { useQuery } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { useState } from "react";
import { getUser } from "../../../api/auth";
import { User } from "../../../api/services/users";
import { User, whoami } from "../../../api/services/users";
import { AuthContext } from "../../../context";
import { isAuthEnabled } from "../../../context/Environment";
import ErrorPage from "../../Errors/ErrorPage";
import FullPageSkeletonLoader from "../../SkeletonLoader/FullPageSkeletonLoader";
import useCurrentKratosUser from "./useCurrentKratosUserID";

type Props = {
children: React.ReactNode;
};

export default function KratosAuthContextProvider({ children }: Props) {
const [user, setUser] = useState<User | null>(null);
const {
data: user,
isLoading,
error
} = useQuery<User, AxiosError>(["user", "whoami"], () => whoami(), {
refetchOnWindowFocus: false,
refetchInterval: 0,
refetchOnReconnect: false
});

const userId = useCurrentKratosUser();

const { isLoading, error } = useQuery<User | null, AxiosError>(
["getUser", !isAuthEnabled()],
() => getUser(userId!),
{
onSuccess: (data) => {
setUser(data ?? null);
},
onError: (err) => {
console.error("Error fetching user", err);
console.error(err);
},
enabled: !!userId
}
);
if (isLoading && !user) {
return <FullPageSkeletonLoader />;
}

if (error && !user) {
return <ErrorPage error={error} />;
}

if (!user || isLoading) {
return <FullPageSkeletonLoader />;
}

return (
<AuthContext.Provider value={{ user, setUser }}>
<AuthContext.Provider value={{ user, setUser: () => {} }}>
{children}
</AuthContext.Provider>
);
Expand Down
94 changes: 94 additions & 0 deletions src/components/Forms/Formik/FormikAuthFields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { useFormikContext } from "formik";
import { get } from "lodash";
import { useState, useEffect, useCallback } from "react";
import FormikCheckboxFieldsGroup from "./FormikCheckboxFieldsGroup";
import FormikEnvVarConfigsFields from "./FormikConfigEnvVarFields";
import { Switch } from "../../Switch";

type FormikAuthFieldsProps = {
name: string;
fields: {
name: string;
label: string;
}[];
label?: string;
};

export default function FormikAuthFields({
name,
fields,
label = "Authentication"
}: FormikAuthFieldsProps) {
const { setFieldValue, values } = useFormikContext<Record<string, any>>();

const [selectedMethod, setSelectedMethod] = useState<"None" | string>(() => {
fields.forEach((field) => {
if (get(values, `${name}.${field.name}`)) {
return field.name;
}
});
return "None";
});

useEffect(() => {
fields.forEach((field) => {
if (get(values, `${name}.${field.name}`)) {
setSelectedMethod(field.name);
}
});
}, [fields, name, values]);

const setAuthenticationMethodFormValue = useCallback(
(method: "None" | string) => {
// reset all fields
fields.forEach((field) => {
setFieldValue(`${name}.${field.name}`, undefined);
});

// set the correct method
fields.forEach((field) => {
if (field.name === method) {
setFieldValue(`${name}.${field.name}`, true);
}
});
},
[fields, name, setFieldValue]
);

return (
<div className="flex flex-col space-y-2">
<label className="font-semibold text-sm">{label}</label>
<div className="flex flex-row w-full">
<Switch
options={fields.map((field) => field.label)}
defaultValue="None"
value={selectedMethod}
onChange={(v) => {
setSelectedMethod(v);
setAuthenticationMethodFormValue(v);
}}
/>
</div>
{selectedMethod !== "None" && (
<div className="flex flex-col p-2">
<FormikCheckboxFieldsGroup
name={`${name}.authentication.username`}
label="Username"
>
<FormikEnvVarConfigsFields
name={`${name}.authentication.username`}
/>
</FormikCheckboxFieldsGroup>
<FormikCheckboxFieldsGroup
name={`${name}.authentication.password`}
label="Password"
>
<FormikEnvVarConfigsFields
name={`${name}.authentication.password`}
/>
</FormikCheckboxFieldsGroup>
</div>
)}
</div>
);
}

0 comments on commit 02250b0

Please sign in to comment.