Skip to content

Commit 5223fae

Browse files
author
Saravana Kumar Rajendran
committed
chore: store Clerk token in cookie
1 parent f65c0bc commit 5223fae

5 files changed

Lines changed: 93 additions & 7 deletions

File tree

docs/docs/Configuration/environment-variables.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ The following table lists the environment variables supported by Langflow.
178178
|----------|--------|---------|-------------|
179179
| <Link id="DO_NOT_TRACK"/>DO_NOT_TRACK | Boolean | `false` | If this option is enabled, Langflow does not track telemetry. |
180180
| <Link id="LANGFLOW_AUTO_LOGIN"/><span class="env-prefix">LANGFLOW_</span>AUTO_LOGIN | Boolean | `true` | Enable automatic login for Langflow. Set to `false` to disable automatic login and require the login form to log into the Langflow UI. Setting to `false` requires [`LANGFLOW_SUPERUSER`](#LANGFLOW_SUPERUSER) and [`LANGFLOW_SUPERUSER_PASSWORD`](environment-variables.md#LANGFLOW_SUPERUSER_PASSWORD) to be set. |
181+
| <Link id="CLERK_AUTH_ENABLED"/>CLERK_AUTH_ENABLED | Boolean | `false` | When enabled, Langflow uses Clerk for authentication. After signing in or signing up with Clerk, the frontend registers the user with the backend and then logs in via `/api/v1/login` to sync cookies. The `access_token_lf` cookie stores the Clerk session token. |
182+
| <Link id="CLERK_PUBLISHABLE_KEY"/>CLERK_PUBLISHABLE_KEY | String | Not set | Publishable key required when `CLERK_AUTH_ENABLED` is `true`. |
181183
| <Link id="LANGFLOW_AUTO_SAVING"/><span class="env-prefix">LANGFLOW_</span>AUTO_SAVING | Boolean | `true` | Enable flow auto-saving.<br/>See [`--auto-saving` option](./configuration-cli.md#run-auto-saving). |
182184
| <Link id="LANGFLOW_AUTO_SAVING_INTERVAL"/><span class="env-prefix">LANGFLOW_</span>AUTO_SAVING_INTERVAL | Integer | `1000` | Set the interval for flow auto-saving in milliseconds.<br/>See [`--auto-saving-interval` option](./configuration-cli.md#run-auto-saving-interval). |
183185
| <Link id="LANGFLOW_BACKEND_ONLY"/><span class="env-prefix">LANGFLOW_</span>BACKEND_ONLY | Boolean | `false` | Only run Langflow's backend server (no frontend).<br/>See [`--backend-only` option](./configuration-cli.md#run-backend-only). |
Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,44 @@
1-
import { useAuth } from "@clerk/clerk-react";
2-
import { useEffect } from "react";
1+
import { useAuth, useUser } from "@clerk/clerk-react";
2+
import { useEffect, useContext } from "react";
33
import { Cookies } from "react-cookie";
44
import useAuthStore from "@/stores/authStore";
55
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
6+
import { AuthContext } from "@/contexts/authContext";
7+
import { ensureLangflowUser, backendLogin } from "./langflow-sync";
68

79
export default function ClerkAuthAdapter() {
810
const { getToken, isSignedIn, sessionId } = useAuth();
11+
const { user } = useUser();
12+
const { login } = useContext(AuthContext);
913

1014
useEffect(() => {
1115
const cookies = new Cookies();
1216
async function syncToken() {
1317
if (isSignedIn) {
1418
const token = await getToken();
1519
if (token) {
16-
cookies.set(LANGFLOW_ACCESS_TOKEN, token, { path: "/" });
17-
useAuthStore.getState().setAccessToken(token);
18-
useAuthStore.getState().setIsAuthenticated(true);
20+
const username =
21+
user?.username ||
22+
user?.primaryEmailAddress?.emailAddress ||
23+
user?.id ||
24+
"clerk_user";
25+
try {
26+
await ensureLangflowUser(token, username);
27+
const data = await backendLogin(username);
28+
// use the Clerk token for frontend state but still
29+
// rely on /login to set refresh and API key cookies
30+
login(token, "login", data.refresh_token);
31+
} catch {
32+
// ignore errors and continue login
33+
}
1934
}
2035
} else {
2136
cookies.remove(LANGFLOW_ACCESS_TOKEN, { path: "/" });
2237
useAuthStore.getState().logout();
2338
}
2439
}
2540
syncToken();
26-
}, [isSignedIn, getToken, sessionId]);
41+
}, [isSignedIn, getToken, sessionId, user, login]);
2742

2843
return null;
2944
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { api } from "@/controllers/API/api";
2+
import { getURL } from "@/controllers/API/helpers/constants";
3+
import { CLERK_DUMMY_PASSWORD } from "@/constants/clerk";
4+
5+
export async function ensureLangflowUser(token: string, username: string) {
6+
try {
7+
await api.get(`${getURL("USERS")}/whoami`, {
8+
headers: { Authorization: `Bearer ${token}` },
9+
});
10+
} catch (err: any) {
11+
if (err?.response?.status === 404) {
12+
await api.post(
13+
`${getURL("USERS")}/`,
14+
{ username, password: CLERK_DUMMY_PASSWORD },
15+
{ headers: { Authorization: `Bearer ${token}` } },
16+
);
17+
} else {
18+
throw err;
19+
}
20+
}
21+
}
22+
23+
export async function backendLogin(username: string) {
24+
const res = await api.post(
25+
`${getURL("LOGIN")}`,
26+
new URLSearchParams({
27+
username,
28+
password: CLERK_DUMMY_PASSWORD,
29+
}).toString(),
30+
{
31+
headers: {
32+
"Content-Type": "application/x-www-form-urlencoded",
33+
},
34+
},
35+
);
36+
return res.data;
37+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export const IS_CLERK_AUTH =
22
String(process.env.CLERK_AUTH_ENABLED).toLowerCase() === "true";
33
export const CLERK_PUBLISHABLE_KEY = process.env.CLERK_PUBLISHABLE_KEY || "";
4+
5+
// Dummy password used when registering Clerk users in the backend
6+
export const CLERK_DUMMY_PASSWORD = "clerk_dummy_password";
Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,33 @@
1-
import { SignUp } from "@clerk/clerk-react";
1+
import { SignUp, useAuth, useUser, useClerk } from "@clerk/clerk-react";
2+
import { useEffect, useState } from "react";
3+
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
4+
import { ensureLangflowUser } from "@/clerk/langflow-sync";
5+
26
export default function ClerkSignUpPage() {
7+
const { isSignedIn, getToken } = useAuth();
8+
const { user } = useUser();
9+
const { signOut } = useClerk();
10+
const navigate = useCustomNavigate();
11+
const [processed, setProcessed] = useState(false);
12+
13+
useEffect(() => {
14+
async function handleSignup() {
15+
if (isSignedIn && user && !processed) {
16+
setProcessed(true);
17+
const token = await getToken();
18+
if (token) {
19+
const username =
20+
user.username ||
21+
user.primaryEmailAddress?.emailAddress ||
22+
user.id;
23+
await ensureLangflowUser(token, username);
24+
}
25+
await signOut();
26+
navigate("/login");
27+
}
28+
}
29+
handleSignup();
30+
}, [isSignedIn, user, getToken, signOut, navigate, processed]);
31+
332
return <SignUp />;
433
}

0 commit comments

Comments
 (0)