Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker/build_and_push.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,4 @@ ENV LANGFLOW_HOST=0.0.0.0
ENV LANGFLOW_PORT=7860

CMD ["langflow", "run"]

2 changes: 1 addition & 1 deletion src/backend/base/langflow/api/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ async def build_vertices(
try:
vertex_build_response: VertexBuildResponse = await _build_vertex(vertex_id, graph, event_manager)
except asyncio.CancelledError as exc:
await logger.aerror(f"Build cancelled: {exc}")
await logger.ainfo(f"Build cancelled: {exc}")
raise

# send built event or error event
Expand Down
41 changes: 36 additions & 5 deletions src/backend/base/langflow/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,28 @@ async def load_bundles_with_error_handling():
return [], []


def warn_about_future_cors_changes(settings):
"""Warn users about upcoming CORS security changes in version 1.7."""
# Check if using default (backward compatible) settings
using_defaults = settings.cors_origins == "*" and settings.cors_allow_credentials is True

if using_defaults:
logger.warning(
"DEPRECATION NOTICE: Starting in v1.7, CORS will be more restrictive by default. "
"Current behavior allows all origins (*) with credentials enabled. "
"Consider setting LANGFLOW_CORS_ORIGINS for production deployments. "
"See documentation for secure CORS configuration."
)

# Additional warning for potentially insecure configuration
if settings.cors_origins == "*" and settings.cors_allow_credentials:
logger.warning(
"SECURITY NOTICE: Current CORS configuration allows all origins with credentials. "
"In v1.7, credentials will be automatically disabled when using wildcard origins. "
"Specify exact origins in LANGFLOW_CORS_ORIGINS to use credentials securely."
)


def get_lifespan(*, fix_migration=False, version=None):
@asynccontextmanager
async def lifespan(_app: FastAPI):
Expand Down Expand Up @@ -337,14 +359,24 @@ def create_app():
)

setup_sentry(app)
origins = ["*"]

settings = get_settings_service().settings

# Warn about future CORS changes
warn_about_future_cors_changes(settings)

# Configure CORS using settings (with backward compatible defaults)
origins = settings.cors_origins
if isinstance(origins, str) and origins != "*":
origins = [origins]

# Apply current CORS configuration (maintains backward compatibility)
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=settings.cors_allow_credentials,
allow_methods=settings.cors_allow_methods,
allow_headers=settings.cors_allow_headers,
)
app.add_middleware(JavaScriptMIMETypeMiddleware)

Expand Down Expand Up @@ -393,7 +425,6 @@ async def flatten_query_string_lists(request: Request, call_next):

return await call_next(request)

settings = get_settings_service().settings
if prome_port_str := os.environ.get("LANGFLOW_PROMETHEUS_PORT"):
# set here for create_app() entry point
prome_port = int(prome_port_str)
Expand Down
6 changes: 5 additions & 1 deletion src/backend/base/langflow/services/auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,14 +408,18 @@ async def create_refresh_token(refresh_token: str, db: AsyncSession):
user_id: UUID = payload.get("sub") # type: ignore[assignment]
token_type: str = payload.get("type") # type: ignore[assignment]

if user_id is None or token_type == "":
if user_id is None or token_type != "refresh": # noqa: S105
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token")

user_exists = await get_user_by_id(db, user_id)

if user_exists is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token")

# Security: Check if user is still active
if not user_exists.is_active:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User account is inactive")

return await create_user_tokens(user_id, db)

except JWTError as e:
Expand Down
24 changes: 24 additions & 0 deletions src/backend/base/langflow/services/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,18 @@ class Settings(BaseSettings):
backend_only: bool = False
"""If set to True, Langflow will not serve the frontend."""

# CORS Settings
cors_origins: list[str] | str = "*"
"""Allowed origins for CORS. Can be a list of origins or '*' for all origins.
Default is '*' for backward compatibility. In production, specify exact origins."""
cors_allow_credentials: bool = True
"""Whether to allow credentials in CORS requests.
Default is True for backward compatibility. In v1.7, this will be changed to False when using wildcard origins."""
cors_allow_methods: list[str] | str = "*"
"""Allowed HTTP methods for CORS requests."""
cors_allow_headers: list[str] | str = "*"
"""Allowed headers for CORS requests."""

# Telemetry
do_not_track: bool = False
"""If set to True, Langflow will not track telemetry."""
Expand Down Expand Up @@ -290,6 +302,18 @@ class Settings(BaseSettings):
update_starter_projects: bool = True
"""If set to True, Langflow will update starter projects."""

@field_validator("cors_origins", mode="before")
@classmethod
def validate_cors_origins(cls, value):
"""Convert comma-separated string to list if needed."""
if isinstance(value, str) and value != "*":
if "," in value:
# Convert comma-separated string to list
return [origin.strip() for origin in value.split(",")]
# Convert single origin to list for consistency
return [value]
return value

@field_validator("use_noop_database", mode="before")
@classmethod
def set_use_noop_database(cls, value):
Expand Down
Loading
Loading