1111from starlette .status import HTTP_401_UNAUTHORIZED
1212
1313from langflow .logging .logger import logger
14- from langflow .services .database .models .user import User , UserCreate
14+ from langflow .services .database .models .user import User
1515from langflow .services .database .models .user .crud import get_user_by_id
1616from langflow .services .deps import get_settings_service
1717
2020
2121_jwks_cache : dict [str , dict [str , Any ]] = {}
2222
23- # APIs that require Clerk token decoding in middleware
24- PROTECTED_PATHS = ["/api/v1/users/" ,"/api/v1/login" ]
25-
2623
2724async def _get_jwks (issuer : str ) -> dict [str , Any ]:
2825 """Retrieve and cache JWKS for a Clerk issuer."""
@@ -64,6 +61,7 @@ async def verify_clerk_token(token: str) -> dict[str, Any]:
6461 algorithms = [unverified_header .get ("alg" , "RS256" )],
6562 audience = unverified_claims .get ("aud" ),
6663 issuer = issuer ,
64+ # options={"verify_signature": False, "verify_aud": False, "verify_exp": False},
6765 )
6866 # ✅ Add deterministic UUID to the payload
6967 clerk_id = payload .get ("sub" )
@@ -72,54 +70,54 @@ async def verify_clerk_token(token: str) -> dict[str, Any]:
7270 raise JWTError (msg )
7371 payload ["uuid" ] = str (uuid .uuid5 (uuid .NAMESPACE_DNS , str (clerk_id )))
7472
73+ org = payload .get ("o" )
74+ if isinstance (org , dict ) and "id" in org :
75+ payload ["org_id" ] = org ["id" ]
76+ elif "org_id" in payload :
77+ # Some Clerk tokens expose the organisation id directly
78+ payload ["org_id" ] = payload ["org_id" ]
79+ else :
80+ msg = "Missing organization info in Clerk token payload"
81+ raise JWTError (msg )
82+ logger .info (f"[ClerkAuthAdapter] Verified Clerk token for org_id: { payload } " )
7583 except JWTError as exc :
7684 msg = "Invalid token"
7785 raise ValueError (msg ) from exc
7886 return payload
7987
8088
81- async def process_new_user_with_clerk (_user : UserCreate , new_user : User ):
82- settings = get_settings_service ().auth_settings
83- # ✅ If Clerk is enabled, pull UUID from enriched auth_header_ctx payload
84- if settings .CLERK_AUTH_ENABLED :
85- payload = auth_header_ctx .get ()
86- if not payload :
87- raise HTTPException (status_code = 401 , detail = "Missing Clerk payload" )
88- clerk_uuid = payload .get ("uuid" )
89- if not clerk_uuid :
90- raise HTTPException (status_code = 401 , detail = "Missing Clerk UUID" )
91- new_user .id = UUID (clerk_uuid )
92- logger .info (f"[process_new_user_with_clerk] Assigned Clerk UUID { new_user .id } to new user object" )
93-
94- async def get_user_from_clerk_payload (token : str , db : AsyncSession ) -> User :
95- """Retrieve the current user using the payload from ``verify_clerk_token``."""
89+ def get_user_id_from_clerk_payload () -> UUID :
90+ """Extract the Clerk user UUID from the request context."""
91+ payload = auth_header_ctx .get ()
92+ if not payload :
93+ raise HTTPException (status_code = 401 , detail = "Missing Clerk payload" )
94+ clerk_uuid = payload .get ("uuid" )
95+ if not clerk_uuid :
96+ raise HTTPException (status_code = 401 , detail = "Missing Clerk UUID" )
9697 try :
97- payload = await verify_clerk_token (token )
98- except Exception as exc :
99- raise HTTPException (
100- status_code = status .HTTP_401_UNAUTHORIZED ,
101- detail = "Authentication failed" ,
102- headers = {"WWW-Authenticate" : "Bearer" },
103- ) from exc
104-
105- uuid_str = payload .get ("uuid" )
106- logger .info (f"uuid_str: { uuid_str } " )
107- if not uuid_str :
108- raise HTTPException (
109- status_code = status .HTTP_401_UNAUTHORIZED ,
110- detail = "Missing Clerk UUID" ,
111- headers = {"WWW-Authenticate" : "Bearer" },
112- )
113-
114- try :
115- user_id = UUID (uuid_str )
98+ return UUID (clerk_uuid )
11699 except ValueError as err :
117100 raise HTTPException (
118- status_code = status . HTTP_401_UNAUTHORIZED ,
101+ status_code = 401 ,
119102 detail = "Invalid Clerk UUID format" ,
120103 headers = {"WWW-Authenticate" : "Bearer" },
121104 ) from err
122105
106+
107+ async def process_new_user_with_clerk (new_user : User ):
108+ settings = get_settings_service ().auth_settings
109+ # ✅ If Clerk is enabled, pull UUID from enriched auth_header_ctx payload
110+ if settings .CLERK_AUTH_ENABLED :
111+ user_id = get_user_id_from_clerk_payload ()
112+ new_user .id = user_id
113+ logger .info (f"[process_new_user_with_clerk] Assigned Clerk UUID { new_user .id } to new user object" )
114+
115+
116+ async def get_user_from_clerk_payload (db : AsyncSession ) -> User :
117+ """Retrieve the current user using the payload stored in the request context."""
118+ user_id = get_user_id_from_clerk_payload ()
119+ logger .debug (f"uuid_str: { user_id } " )
120+
123121 user = await get_user_by_id (db , user_id )
124122 logger .info (f"Retrieved user: { user } " )
125123 if user is None :
@@ -142,36 +140,31 @@ async def get_user_from_clerk_payload(token: str, db: AsyncSession) -> User:
142140
143141
144142async def clerk_token_middleware (request : Request , call_next ):
145- """Middleware to decode Clerk token for specific paths ."""
143+ """Middleware to decode Clerk token when present ."""
146144 settings = get_settings_service ()
145+ if not settings .auth_settings .CLERK_AUTH_ENABLED :
146+ return await call_next (request )
147147
148- ctx_token : Token | None = None
149- if settings .auth_settings .CLERK_AUTH_ENABLED and request .url .path in PROTECTED_PATHS :
150- auth_header = request .headers .get ("Authorization" )
151-
152- if not auth_header or not auth_header .startswith ("Bearer " ):
153- logger .warning ("Missing or malformed Authorization header for Clerk protected route." )
154- return JSONResponse (
155- status_code = HTTP_401_UNAUTHORIZED ,
156- content = {"detail" : "Authorization header with valid Bearer token required" },
157- )
158-
159- if auth_header and auth_header .startswith ("Bearer " ):
160- token = auth_header [len ("Bearer " ) :]
161- try :
162- payload = await verify_clerk_token (token )
163- ctx_token = auth_header_ctx .set (payload )
164- except Exception as exc : # noqa: BLE001
165- logger .warning (f"Failed to verify Clerk token: { exc } " )
166- return JSONResponse (
167- status_code = HTTP_401_UNAUTHORIZED ,
168- content = {"detail" : "Invalid Clerk token" }
169- )
148+ auth_header = request .headers .get ("Authorization" )
149+ logger .info (f"Authorization header present: { auth_header } " )
150+ if not auth_header or not auth_header .startswith ("Bearer " ):
151+ return await call_next (request )
170152
153+ ctx_token : Token | None = None
154+ token = auth_header [len ("Bearer " ) :]
171155 try :
172- return await call_next (request )
156+ payload = await verify_clerk_token (token )
157+ ctx_token = auth_header_ctx .set (payload )
158+ response = await call_next (request )
159+ except Exception as exc : # noqa: BLE001
160+ logger .warning (f"Failed to verify Clerk token: { exc } " )
161+ return JSONResponse (
162+ status_code = HTTP_401_UNAUTHORIZED ,
163+ content = {"detail" : "Invalid Clerk token" },
164+ )
173165 finally :
174166 if ctx_token is not None :
175167 auth_header_ctx .reset (ctx_token )
176168 else :
177169 auth_header_ctx .set (None )
170+ return response
0 commit comments