bugfix: langflow application losing store api-key on refresh page/backend (#2960)
* changing api key journey * refactor(api_key.py): remove unnecessary code related to config_dir and secret_key_path refactor(login.py): refactor setting api_key cookie to use user's store_api_key refactor(variable/service.py): re-encrypt stored value if secret_key changes to ensure validity * 📝 (api_key.py): Remove unused imports and clean up code for better readability 📝 (login.py): Remove unused imports and clean up code for better readability 📝 (auth/utils.py): Remove unused imports and clean up code for better readability 📝 (store/service.py): Remove unused imports and clean up code for better readability * 🔧 (utils.py): replace hashing logic with random key generation for key length less than 32 bytes to ensure key length is always 32 bytes 📝 (utils.py): update comments for clarity and accuracy regarding key generation and encryption process --------- Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
55693c920d
commit
b6774cf3d8
3 changed files with 44 additions and 2 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, Response
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
|
|
||||||
from langflow.api.v1.schemas import ApiKeyCreateRequest, ApiKeysResponse
|
from langflow.api.v1.schemas import ApiKeyCreateRequest, ApiKeysResponse
|
||||||
|
|
@ -62,17 +62,32 @@ def delete_api_key_route(
|
||||||
@router.post("/store")
|
@router.post("/store")
|
||||||
def save_store_api_key(
|
def save_store_api_key(
|
||||||
api_key_request: ApiKeyCreateRequest,
|
api_key_request: ApiKeyCreateRequest,
|
||||||
|
response: Response,
|
||||||
current_user: User = Depends(auth_utils.get_current_active_user),
|
current_user: User = Depends(auth_utils.get_current_active_user),
|
||||||
db: Session = Depends(get_session),
|
db: Session = Depends(get_session),
|
||||||
settings_service=Depends(get_settings_service),
|
settings_service=Depends(get_settings_service),
|
||||||
):
|
):
|
||||||
|
auth_settings = settings_service.auth_settings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
api_key = api_key_request.api_key
|
api_key = api_key_request.api_key
|
||||||
|
|
||||||
# Encrypt the API key
|
# Encrypt the API key
|
||||||
encrypted = auth_utils.encrypt_api_key(api_key, settings_service=settings_service)
|
encrypted = auth_utils.encrypt_api_key(api_key, settings_service=settings_service)
|
||||||
current_user.store_api_key = encrypted
|
current_user.store_api_key = encrypted
|
||||||
db.add(current_user)
|
db.add(current_user)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
response.set_cookie(
|
||||||
|
"apikey_tkn_lflw",
|
||||||
|
encrypted,
|
||||||
|
httponly=auth_settings.ACCESS_HTTPONLY,
|
||||||
|
samesite=auth_settings.ACCESS_SAME_SITE,
|
||||||
|
secure=auth_settings.ACCESS_SECURE,
|
||||||
|
expires=None, # Set to None to make it a session cookie
|
||||||
|
domain=auth_settings.COOKIE_DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
return {"detail": "API Key saved"}
|
return {"detail": "API Key saved"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
from langflow.services.database.models.user.crud import get_user_by_id
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
|
|
||||||
from langflow.api.v1.schemas import Token
|
from langflow.api.v1.schemas import Token
|
||||||
|
|
@ -57,6 +58,15 @@ async def login_to_get_access_token(
|
||||||
expires=auth_settings.ACCESS_TOKEN_EXPIRE_SECONDS,
|
expires=auth_settings.ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||||
domain=auth_settings.COOKIE_DOMAIN,
|
domain=auth_settings.COOKIE_DOMAIN,
|
||||||
)
|
)
|
||||||
|
response.set_cookie(
|
||||||
|
"apikey_tkn_lflw",
|
||||||
|
str(user.store_api_key),
|
||||||
|
httponly=auth_settings.ACCESS_HTTPONLY,
|
||||||
|
samesite=auth_settings.ACCESS_SAME_SITE,
|
||||||
|
secure=auth_settings.ACCESS_SECURE,
|
||||||
|
expires=None, # Set to None to make it a session cookie
|
||||||
|
domain=auth_settings.COOKIE_DOMAIN,
|
||||||
|
)
|
||||||
variable_service.initialize_user_variables(user.id, db)
|
variable_service.initialize_user_variables(user.id, db)
|
||||||
# Create default folder for user if it doesn't exist
|
# Create default folder for user if it doesn't exist
|
||||||
create_default_folder_if_it_doesnt_exist(db, user.id)
|
create_default_folder_if_it_doesnt_exist(db, user.id)
|
||||||
|
|
@ -74,6 +84,7 @@ async def auto_login(
|
||||||
response: Response, db: Session = Depends(get_session), settings_service=Depends(get_settings_service)
|
response: Response, db: Session = Depends(get_session), settings_service=Depends(get_settings_service)
|
||||||
):
|
):
|
||||||
auth_settings = settings_service.auth_settings
|
auth_settings = settings_service.auth_settings
|
||||||
|
|
||||||
if settings_service.auth_settings.AUTO_LOGIN:
|
if settings_service.auth_settings.AUTO_LOGIN:
|
||||||
user_id, tokens = create_user_longterm_token(db)
|
user_id, tokens = create_user_longterm_token(db)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
|
|
@ -86,6 +97,22 @@ async def auto_login(
|
||||||
domain=auth_settings.COOKIE_DOMAIN,
|
domain=auth_settings.COOKIE_DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
user = get_user_by_id(db, user_id)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
if user.store_api_key is None:
|
||||||
|
user.store_api_key = ""
|
||||||
|
|
||||||
|
response.set_cookie(
|
||||||
|
"apikey_tkn_lflw",
|
||||||
|
str(user.store_api_key), # Ensure it's a string
|
||||||
|
httponly=auth_settings.ACCESS_HTTPONLY,
|
||||||
|
samesite=auth_settings.ACCESS_SAME_SITE,
|
||||||
|
secure=auth_settings.ACCESS_SECURE,
|
||||||
|
expires=None, # Set to None to make it a session cookie
|
||||||
|
domain=auth_settings.COOKIE_DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -140,4 +167,5 @@ async def refresh_token(
|
||||||
async def logout(response: Response):
|
async def logout(response: Response):
|
||||||
response.delete_cookie("refresh_token_lf")
|
response.delete_cookie("refresh_token_lf")
|
||||||
response.delete_cookie("access_token_lf")
|
response.delete_cookie("access_token_lf")
|
||||||
|
response.delete_cookie("apikey_tkn_lflw")
|
||||||
return {"message": "Logout successful"}
|
return {"message": "Logout successful"}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,6 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeApiKey(apikey: string) {
|
function storeApiKey(apikey: string) {
|
||||||
cookies.set(LANGFLOW_API_TOKEN, apikey, { path: "/" });
|
|
||||||
setApiKey(apikey);
|
setApiKey(apikey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue