Skip to content

Add optional Clerk authentication - #13

Merged
SaravanakumarR2018 merged 21 commits into
mainfrom
9r8oht-codex/integrate-clerk-authentication-flow
Aug 5, 2025
Merged

Add optional Clerk authentication#13
SaravanakumarR2018 merged 21 commits into
mainfrom
9r8oht-codex/integrate-clerk-authentication-flow

Conversation

@SaravanakumarR2018

Copy link
Copy Markdown
Owner

Summary

  • add a helper to swap login pages when Clerk is enabled
  • wrap the app in ClerkAuthProvider when IS_CLERK_AUTH is true
  • expose Clerk feature flags via new constants
  • supply .env.clerk.example for Clerk configuration
  • remove Clerk settings from .env.example

Testing

  • npm run type-check (fails: Cannot find module 'react')
  • make tests_frontend (fails: playwright installation prompt)

https://chatgpt.com/codex/tasks/task_e_6868e3a90fdc8326a9ff235d844cc057

@autofix-troubleshooter

Copy link
Copy Markdown

Hi! I'm the autofix logoautofix.ci troubleshooter bot.

It looks like you correctly set up a CI job that uses the autofix.ci GitHub Action, but the autofix.ci GitHub App has not been installed for this repository. This means that autofix.ci unfortunately does not have the permissions to fix this pull request. If you are the repository owner, please install the app and then restart the CI workflow! 😃

Saravana Kumar Rajendran and others added 7 commits July 5, 2025 21:47
* refactor: update auth and login pages to signup users

* updated imports in clerk_utils.py

* modified clerk_auth and login-page.tsx

* updated context-wrapper

* updated context-wrapper

* modified use-post-refresh-access.ts

* add new file for clerk constants

* modified autologin for clerk auth

* add token refresh effect in auth.tsx
@Kabilan-16

Kabilan-16 commented Jul 17, 2025

Copy link
Copy Markdown
Collaborator

✅ Clerk Authentication Flow Validation

1. Sign Up with Clerk

When a new user completes the Clerk sign-up flow:

  • GET /api/v1/users/whoami returns 401 (no backend user exists yet).

  • The frontend calls ensureLangflowUser, triggering:

    POST /api/v1/users/

    This creates the user with:

    id: 977d9470-911f-59ec-a1b7-45f50b1756c5
    
  • After user creation:

    • Clerk signs the user out.
    • The app redirects to /login.
  • Frontend updates:

    • authStore.isAuthenticated = false
  • Browser cookies are cleared:

    • access_token_lf, refresh_token_lf, etc.

2. Sign In with Clerk

Once the user authenticates via Clerk:

  • The frontend receives the Clerk token and sets it in authStore.accessToken.

  • GET /api/v1/users/whoami confirms the user exists.

  • The frontend calls:

    POST /api/v1/login

    to establish a Langflow session.

  • Frontend state:

    • authStore.isAuthenticated = true
    • authStore.userData is populated:
    {
      "id": "977d9470-911f-59ec-a1b7-45f50b1756c5",
      "username": "bharanitharan964@gmail.com",
      "is_active": true,
      "is_superuser": false,
      "last_login_at": "2025-07-17T13:37:38.838718"
    }
  • Backend (clerk_utils) confirms:

    uuid_str: 977d9470-911f-59ec-a1b7-45f50b1756c5
    username: bharanitharan964@gmail.com
    is_active: True
    

3. Page Reload While Signed In

After signing in with Clerk and reloading the browser:

  • Clerk session persists and triggers a token refresh.

  • ClerkAuthAdapter detects the new token and updates authStore.accessToken.

  • GET /api/v1/users/whoami returns user info.

  • POST /api/v1/login syncs the backend session.

  • authStore.isAuthenticated = true

  • authStore.userData is restored:

    {
      "id": "977d9470-911f-59ec-a1b7-45f50b1756c5",
      "username": "bharanitharan964@gmail.com",
      "is_active": true,
      "is_superuser": false,
      "last_login_at": "2025-07-17T13:44:51.498293"
    }
  • Backend clerk_utils consistently returns the same user with the expected UUID.


4. 🔄 Automatic Token Refresh (Clerk)

Clerk automatically refreshes session tokens every 5–60 minutes. When this happens:

  • access_token_lf and refresh_token_lf are updated.
  • ClerkAuthAdapter detects the change and updates authStore.accessToken.
  • authStore.isAuthenticated remains true.
  • Backend successfully authenticates using the new Clerk token.

Confirmed by:

GET /api/v1/users/whoami → 200 OK

And logs like:

[AuthStore] setAccessToken: <new JWT>
[AuthStore] setUserData: { id: "977d9470-911f-59ec-a1b7-45f50b1756c5", username: "bharanitharan964@gmail.com", ... }

5. Automatic Token Refresh (App)

Clerk automatically refreshes session tokens every 5–60 minutes. When this happens:

  • access_token_lf and refresh_token_lf are updated.
  • ClerkAuthAdapter detects the change and updates authStore.accessToken.
  • authStore.isAuthenticated remains true.
  • Backend successfully authenticates using the new Clerk token.

Confirmed by:

GET /api/v1/users/whoami → 200 OK

And logs like:

[AuthStore] setAccessToken: <new JWT>
[AuthStore] setUserData: { id: "dcb3a31d-9189-560a-ac09-a8b6fb294255", username: "kabik5095@gmail.com", ... }

6. Sign out

Use the logout option

  • All authentication cookies removed
  • authStore.isAuthenticated = false
  • authStore.accessToken = null

All Clerk-related cookies (access_token, refresh_token, __session) should be gone.

Screenshot (44)

7. Access protected page after logout

  • After signing out, navigate to any authenticated route.(ex:localhost:3000/flows)
  • No cookies present
  • authStore.isAuthenticated: false

Confirmed by:
Page redirects to /login

8. Sign out then sign in again

  • Log out and immediately authenticate once more with Clerk.
  • Same cookies as initial sign‑in
  • Same as initial sign‑in

Confirmed by:

GET /api/v1/users/whoami → 200 OK

frontend:

{
    "id": "dcb3a31d-9189-560a-ac09-a8b6fb294255",
    "username": "kabik5095@gmail.com",
    "profile_image": null,
    "store_api_key": null,
    "is_active": true,
    "is_superuser": false,
    "create_at": "2025-07-17T14:02:20.167081",
    "updated_at": "2025-07-17T14:52:39.338703",
    "last_login_at": "2025-07-17T14:52:39.330954",
    "optins": {
        "github_starred": false,
        "dialog_dismissed": false,
        "discord_clicked": false
    }
}
  • Backend clerk_utils consistently returns the same user with the expected UUID.
 clerk_utils - Retrieved     clerk_utils.py:122
                             is_active=True create_at=datetime.datetime(2025,7, 17, 14, 2, 20, 167081)                                                                        last_login_at=datetime.datetime(2025, 7, 17, 14, 52, 39,                       
                             330954) optins={'github_starred': False, 'dialog_dismissed':                   
                             False, 'discord_clicked': False}                                               
                             id=UUID('dcb3a31d-9189-560a-ac09-a8b6fb294255')                                
                             username='kabik5095@gmail.com' 

Comment thread src/frontend/src/routes.tsx Outdated
Comment thread src/frontend/src/clerk/login-pages.tsx Outdated
Comment thread src/backend/base/langflow/services/auth/clerk_utils.py Outdated
Comment thread src/frontend/src/clerk/constants.ts Outdated
@@ -0,0 +1,6 @@
// src/clerk/constants.ts

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move all these constants into

src/frontend/src/clerk/auth.tsx file and import its reference from the above same file

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified sir

Comment thread src/frontend/src/controllers/API/queries/auth/use-get-autologin.ts
Comment thread src/frontend/src/clerk/auth.tsx Outdated
} catch (err: any) {
const status = err?.response?.status;
console.warn(`[ensureLangflowUser] whoami failed (${status})`);
if (status === 401) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of 401 - use a HTTP ERROR CODE ENUM

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified sir

Comment thread src/frontend/src/clerk/auth.tsx Outdated
justCreated: boolean;
user: Users | null;
}> {
console.log("[ensureLangflowUser] START");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this log

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified sir

Comment thread src/frontend/src/clerk/auth.tsx Outdated
return;
}else{
console.log("[ClerkAuthAdapter] Clerk token changed, syncing...");
cookie.set(LANGFLOW_ACCESS_TOKEN, token, { path: "/" });

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of these two lines

        cookie.set(LANGFLOW_ACCESS_TOKEN, token, { path: "/" });
        useAuthStore.getState().setAccessToken?.(token); // if you have this

can we use the login function?

login(token,<other parameters>)?

@Bharani0012 Bharani0012 Jul 18, 2025

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sir, those were added for refresh logic, since we now have separate useEffect we dont need that if else block.

Comment thread src/frontend/src/clerk/auth.tsx
Comment thread src/frontend/src/clerk/auth.tsx Outdated
prevTokenRef.current = token;
const current = cookie.get(LANGFLOW_ACCESS_TOKEN);
if (token !== current) {
cookie.set(LANGFLOW_ACCESS_TOKEN, token, { path: "/" });

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the login function?

login(token, <other parameters>)

Instead of

 cookie.set(LANGFLOW_ACCESS_TOKEN, token, { path: "/" });
useAuthStore.getState().setAccessToken?.(token);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified sir

Repository owner deleted a comment from SaravanakumarR2018 Jul 23, 2025
@Kabilan-16
Kabilan-16 force-pushed the 9r8oht-codex/integrate-clerk-authentication-flow branch 2 times, most recently from e67d350 to 11c0fdb Compare July 24, 2025 10:53
* added protected paths in login api

* added bearer token in header

* ruff check fix
Comment thread src/frontend/src/clerk/auth.tsx Outdated
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
import { Cookies } from "react-cookie";

console.log(useAuthStore.getState().isAuthenticated, "useAuthStore.isAuthenticated");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kabilan-16 I still see this log?

Comment thread src/frontend/src/clerk/login-pages.tsx
Comment thread src/frontend/src/routes.tsx Outdated
import MessagesPage from "./pages/SettingsPage/pages/messagesPage";
import ShortcutsPage from "./pages/SettingsPage/pages/ShortcutsPage";
import ViewPage from "./pages/ViewPage";
import { LoginPage, SignUpPage, LoginAdminPage } from "./clerk/login-pages";

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import as

import { LoginPage, SignUp, LoginAdminPage } from "./clerk/login-pages";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified

Comment thread src/frontend/src/routes.tsx Outdated
element={
<ProtectedLoginRoute>
<SignUp />
<SignUpPage />

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this change anymore

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified sir

Comment thread src/frontend/src/clerk/auth.tsx
Comment thread src/frontend/src/clerk/login-pages.tsx Outdated
@@ -0,0 +1,70 @@
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { SignIn, SignUp, useAuth, useUser , useClerk, SignedOut} from "@clerk/clerk-react";

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apply this diff

@@ function ClerkSignUpPage() {
   useEffect(() => {
     async function handleSignup() {
       if (isSignedIn && user && !processed) {
-        console.log("[ClerkSignUpPage] User is signed in, processing sign up...");
+        console.debug("[ClerkSignUpPage] User is signed in, processing sign up...");
         setProcessed(true);
         const token = await getToken();
         if (token) {
           const username =
             user.username || user.primaryEmailAddress?.emailAddress || user.id;
-          console.log(`[ClerkSignUpPage] Creating Langflow user for: ${username}`);
+          console.debug(`[ClerkSignUpPage] Creating Langflow user for: ${username}`);
           await ensureLangflowUser(token, username);
         } else {
-          console.log("[ClerkSignUpPage] No token received from Clerk.");
+          console.warn("[ClerkSignUpPage] No token received from Clerk.");
         }
-        console.log("[ClerkSignUpPage] Signing out user after sign up.");
+        console.debug("[ClerkSignUpPage] Signing out user after sign up.");
         await logout();
-        console.log("[ClerkSignUpPage] Redirecting to /login after sign up.");
+        console.debug("[ClerkSignUpPage] Redirecting to /login after sign up.");
         navigate("/login");
       }
     }

if (!isLoginPage) {
const status = error.response?.status;
if (status === 400 && IS_CLERK_AUTH) {
console.log("[AutoLogin] Clerk login - skipping logout on 400");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it console.debug

path="signup"
element={
<ProtectedLoginRoute>
<SignUp />

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not have this change

Kabilan-16 and others added 3 commits July 25, 2025 15:32
* update make file to inject build args in docker

* update docker file to load env

* modified constant.ts

* Update Makefile and Dockerfile to replace LANGFLOW_AUTO_LOGIN with VITE_AUTO_LOGIN

* modified constant.ts
@SaravanakumarR2018
SaravanakumarR2018 merged commit 0709eaf into main Aug 5, 2025
12 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants