langserve
langserve copied to clipboard
Security Auth API Key headers compatibility with playground
When adding middleware for auth api-key header security on the langserve routes, the /docs page doesn't recognize the security so there is no option to add the header and the /playground pages don't have an option of adding the header so it doesn't work.
Is there any additional documentation on how to setup auth security on these routes and still be able to use the playground?
For /docs see this solution: https://github.com/tiangolo/fastapi/issues/364
ccing @dqbd for playground
Can someone explain how this can be done? I have added authorization to all the endpoints except for playground endpoint but the problem is the API calls in the playground frontend app need to send the authorization header. How can that be achieved?
+1
Can someone explain how this can be done? I have added authorization to all the endpoints except for playground endpoint but the problem is the API calls in the playground frontend app need to send the authorization header. How can that be achieved?
This would be of much help. Thanks for posting the question.
@dqbd any input on this?
@dqbd appreciate if we can get some guidance on this.
Same issue here, it'd like to use the playground with a service where the langserve routes require an Authentication header, so I'm looking for a way to configure the playground to get a header from the page or somewhere and include it in requests.
This would be a very useful feature; any production environment likely needs some form of authentication header. We are having to resort to curl statements for testing.
As a temporary workaround you can use secure http cookie authentication with some templated html around all routes similar to what is in the following server code with an environment variable set for the API Key (LANGSERVE_API_KEY). This will give you a sort of dashboard for navigation after login:
import os
from fastapi import FastAPI, Form, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from app.example_chain import example_chain
from langserve import add_routes
LANGSERVE_API_KEY = os.getenv("LANGSERVE_API_KEY")
if not LANGSERVE_API_KEY:
raise ValueError("LANGSERVE_API_KEY is not set")
def get_login_html_content(hasError: bool = False):
error_message = "<p>Invalid API Key</p>" if hasError else ""
return f"""
<html>
<head>
<title>Login</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="api_key" placeholder="Enter API Key" required>
<button type="submit">Submit</button>
</form>
{error_message}
</body>
</html>
"""
app = FastAPI(
title="Example LangChain Server",
version="1.0",
description="Example AI Microservice",
)
@app.middleware("http")
async def cookie_auth_middleware(request: Request, call_next):
path = request.url.path
# Check if the path is one of the unprotected routes
if path not in ["/", "/login"]:
# Extract the cookie and check it
api_key = request.cookies.get("LANGSERVE_API_KEY")
if api_key != LANGSERVE_API_KEY:
return RedirectResponse(url="/login", status_code=303)
response = await call_next(request)
return response
@app.get("/", response_class=RedirectResponse)
async def redirect_root_to_login():
return RedirectResponse("/login")
@app.get("/login", response_class=HTMLResponse)
def login_form():
return HTMLResponse(content=get_login_html_content())
@app.post("/login")
def login(api_key: str = Form(...)):
if api_key == LANGSERVE_API_KEY:
response = RedirectResponse(url="/dashboard", status_code=303)
response.set_cookie(
key="LANGSERVE_API_KEY",
value=api_key,
httponly=True,
secure=True,
samesite="strict",
)
return response
return HTMLResponse(content=get_login_html_content(hasError=True))
routes = [
{
"path": "/example_path",
"chain": example_chain,
"name": "Example",
}
]
routes_html = "\n".join(
[f'<a href="{route["path"]}/playground">{route["name"]}</a>' for route in routes]
)
@app.get("/dashboard", response_class=HTMLResponse)
def login_form():
return HTMLResponse(
content=f"""
<html>
<head>
<title>Dashboard</title>
<style>
a {{
display: block;
}}
</style>
</head>
<body>
<h3>Dashboard</h3>
<a href="/docs">Docs</a>
<h3>Playgrounds</h3>
{routes_html}
</body>
</html>
"""
)
for route in routes:
add_routes(
app,
route["chain"],
path=route["path"],
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)