Skip to content

Commit 2a81f60

Browse files
author
Saravana Kumar Rajendran
authored
restore env example and clean doc (#17)
1 parent 19a55ed commit 2a81f60

17 files changed

Lines changed: 211 additions & 215 deletions

File tree

docs/docs/Integrations/clerk-token-refresh.md renamed to docs/docs/Integrations/clerk-auth.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
---
2-
title: Clerk Token Refresh Behavior
3-
slug: /clerk-token-refresh
2+
title: Clerk Authentication
3+
slug: /clerk-auth
44
---
55

6+
67
This page summarizes how Langflow handles access token refreshes in different authentication modes and what state changes occur when using Clerk.
78

89
## Refresh scenarios when Clerk authentication is disabled

src/frontend/src/AppWithProvider.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/frontend/src/clerk/auth.tsx

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import React, { ReactNode, useEffect, useContext, useState } from "react";
2+
import App from "../customization/custom-App";
3+
import { ClerkProvider, useAuth, useUser, useClerk } from "@clerk/clerk-react";
4+
import { Cookies } from "react-cookie";
5+
import useAuthStore from "@/stores/authStore";
6+
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
7+
import { AuthContext } from "@/contexts/authContext";
8+
import { api } from "@/controllers/API/api";
9+
import { getURL } from "@/controllers/API/helpers/constants";
10+
import { useLogout as useLogoutMutation } from "@/controllers/API/queries/auth";
11+
12+
// Clerk constants
13+
export const IS_CLERK_AUTH =
14+
String(process.env.CLERK_AUTH_ENABLED).toLowerCase() === "true";
15+
export const CLERK_PUBLISHABLE_KEY = process.env.CLERK_PUBLISHABLE_KEY || "";
16+
export const CLERK_DUMMY_PASSWORD = "clerk_dummy_password";
17+
18+
// Backend synchronization helpers
19+
export async function ensureLangflowUser(token: string, username: string) {
20+
try {
21+
await api.get(`${getURL("USERS")}/whoami`, {
22+
headers: { Authorization: `Bearer ${token}` },
23+
});
24+
} catch (err: any) {
25+
if (err?.response?.status === 404) {
26+
await api.post(
27+
`${getURL("USERS")}/`,
28+
{ username, password: CLERK_DUMMY_PASSWORD },
29+
{ headers: { Authorization: `Bearer ${token}` } },
30+
);
31+
} else {
32+
throw err;
33+
}
34+
}
35+
}
36+
37+
export async function backendLogin(username: string) {
38+
const res = await api.post(
39+
`${getURL("LOGIN")}`,
40+
new URLSearchParams({
41+
username,
42+
password: CLERK_DUMMY_PASSWORD,
43+
}).toString(),
44+
{
45+
headers: {
46+
"Content-Type": "application/x-www-form-urlencoded",
47+
},
48+
},
49+
);
50+
return res.data;
51+
}
52+
53+
// Component that syncs Clerk session with backend
54+
export function ClerkAuthAdapter() {
55+
const { getToken, isSignedIn, sessionId } = useAuth();
56+
const { user } = useUser();
57+
const { login } = useContext(AuthContext);
58+
59+
useEffect(() => {
60+
const cookies = new Cookies();
61+
async function syncToken() {
62+
if (isSignedIn) {
63+
const token = await getToken();
64+
if (token) {
65+
const username =
66+
user?.username ||
67+
user?.primaryEmailAddress?.emailAddress ||
68+
user?.id ||
69+
"clerk_user";
70+
try {
71+
await ensureLangflowUser(token, username);
72+
const data = await backendLogin(username);
73+
login(token, "login", data.refresh_token);
74+
} catch {
75+
// ignore errors and continue login
76+
}
77+
}
78+
} else {
79+
cookies.remove(LANGFLOW_ACCESS_TOKEN, { path: "/" });
80+
useAuthStore.getState().logout();
81+
}
82+
}
83+
syncToken();
84+
}, [isSignedIn, getToken, sessionId, user, login]);
85+
86+
return null;
87+
}
88+
89+
// Provider that wraps the app with Clerk when enabled
90+
export function ClerkAuthProvider({ children }: { children: ReactNode }) {
91+
return (
92+
<ClerkProvider publishableKey={CLERK_PUBLISHABLE_KEY}>
93+
<ClerkAuthAdapter />
94+
{children}
95+
</ClerkProvider>
96+
);
97+
}
98+
99+
// Logout hook that also signs out from Clerk
100+
export function useLogout(options?: Parameters<typeof useLogoutMutation>[0]) {
101+
const { mutate, mutateAsync, ...rest } = useLogoutMutation(options);
102+
const { signOut } = IS_CLERK_AUTH ? useClerk() : { signOut: async () => {} };
103+
104+
const clerkSignOut = async () => {
105+
if (IS_CLERK_AUTH) {
106+
try {
107+
await signOut();
108+
} catch (err) {
109+
console.error("Clerk signOut failed:", err);
110+
}
111+
}
112+
};
113+
114+
const wrappedMutate: typeof mutate = (...args) => {
115+
clerkSignOut().finally(() => mutate(...args));
116+
};
117+
118+
const wrappedMutateAsync: typeof mutateAsync = async (...args) => {
119+
await clerkSignOut();
120+
return mutateAsync(...args);
121+
};
122+
123+
return { mutate: wrappedMutate, mutateAsync: wrappedMutateAsync, clerkSignOut, ...rest };
124+
}
125+
126+
// App wrapper that conditionally enables Clerk
127+
export function AppWithProvider() {
128+
return IS_CLERK_AUTH ? (
129+
<ClerkAuthProvider>
130+
<App />
131+
</ClerkAuthProvider>
132+
) : (
133+
<App />
134+
);
135+
}
136+
137+
// Mock mutation used when Clerk auth is enabled
138+
export const mockClerkMutation = {
139+
mutate: () => {},
140+
mutateAsync: async () => undefined,
141+
isError: false,
142+
isIdle: true,
143+
isPending: false,
144+
isSuccess: true,
145+
reset: () => {},
146+
status: "success",
147+
variables: undefined,
148+
data: undefined,
149+
error: null,
150+
} as any;
151+
152+
export default AppWithProvider;

src/frontend/src/clerk/clerk-auth-adapter.tsx

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/frontend/src/clerk/clerk-logout.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/frontend/src/clerk/clerk-provider.tsx

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/frontend/src/clerk/langflow-sync.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { lazy } from "react";
2+
import { SignIn, SignUp, useAuth, useUser, useClerk } from "@clerk/clerk-react";
3+
import { useEffect, useState } from "react";
4+
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
5+
import {
6+
IS_CLERK_AUTH,
7+
ensureLangflowUser,
8+
} from "./auth";
9+
10+
// Clerk login page component
11+
export function ClerkLoginPage() {
12+
return <SignIn />;
13+
}
14+
15+
// Clerk sign-up page component
16+
export function ClerkSignUpPage() {
17+
const { isSignedIn, getToken } = useAuth();
18+
const { user } = useUser();
19+
const { signOut } = useClerk();
20+
const navigate = useCustomNavigate();
21+
const [processed, setProcessed] = useState(false);
22+
23+
useEffect(() => {
24+
async function handleSignup() {
25+
if (isSignedIn && user && !processed) {
26+
setProcessed(true);
27+
const token = await getToken();
28+
if (token) {
29+
const username =
30+
user.username || user.primaryEmailAddress?.emailAddress || user.id;
31+
await ensureLangflowUser(token, username);
32+
}
33+
await signOut();
34+
navigate("/login");
35+
}
36+
}
37+
handleSignup();
38+
}, [isSignedIn, user, getToken, signOut, navigate, processed]);
39+
40+
return <SignUp />;
41+
}
42+
43+
// Original pages
44+
import OriginalLoginPage from "../pages/LoginPage";
45+
import OriginalSignUp from "../pages/SignUpPage";
46+
const OriginalLoginAdminPage = lazy(() => import("../pages/AdminPage/LoginPage"));
47+
48+
export const LoginPage = IS_CLERK_AUTH ? ClerkLoginPage : OriginalLoginPage;
49+
export const SignUpPage = IS_CLERK_AUTH ? ClerkSignUpPage : OriginalSignUp;
50+
export const LoginAdminPage = IS_CLERK_AUTH ? ClerkLoginPage : OriginalLoginAdminPage;
51+
52+
export const SignUp = SignUpPage; // maintain previous named export

src/frontend/src/components/core/appHeaderComponent/components/AccountMenu/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useLogout } from "@/clerk/clerk-logout";
1+
import { useLogout } from "@/clerk/auth";
22
import { ForwardedIconComponent } from "@/components/common/genericIconComponent";
33
import {
44
DATASTAX_DOCS_URL,

src/frontend/src/constants/clerk.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)