feat(server): Add JWT validation (#7642)
* add auth middleware * update readnme * Update rnd/autogpt_server/pyproject.toml Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com> * address newline feedback * update poetry lock --------- Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com>pull/7665/head
parent
ac45b7cae9
commit
b23bd9c479
|
@ -0,0 +1,3 @@
|
||||||
|
# AutoGPT Libs
|
||||||
|
|
||||||
|
This is a new project to store shared functionality across different services in NextGen AutoGPT (e.g. authentication)
|
|
@ -0,0 +1,16 @@
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
JWT_SECRET_KEY: str = os.getenv("SUPABASE_JWT_SECRET", "")
|
||||||
|
ENABLE_AUTH: bool = os.getenv("ENABLE_AUTH", "false").lower() == "true"
|
||||||
|
JWT_ALGORITHM: str = "HS256"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_configured(self) -> bool:
|
||||||
|
return bool(self.JWT_SECRET_KEY)
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
|
@ -0,0 +1,20 @@
|
||||||
|
import jwt
|
||||||
|
from typing import Dict, Any
|
||||||
|
from .config import settings
|
||||||
|
|
||||||
|
|
||||||
|
def parse_jwt_token(token: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Parse and validate a JWT token.
|
||||||
|
|
||||||
|
:param token: The token to parse
|
||||||
|
:return: The decoded payload
|
||||||
|
:raises ValueError: If the token is invalid or expired
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
|
||||||
|
return payload
|
||||||
|
except jwt.ExpiredSignatureError:
|
||||||
|
raise ValueError("Token has expired")
|
||||||
|
except jwt.InvalidTokenError as e:
|
||||||
|
raise ValueError(f"Invalid token: {str(e)}")
|
|
@ -0,0 +1,26 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import Request, HTTPException, Depends
|
||||||
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
|
from .jwt_utils import parse_jwt_token
|
||||||
|
from .config import settings
|
||||||
|
|
||||||
|
security = HTTPBearer()
|
||||||
|
async def auth_middleware(request: Request):
|
||||||
|
if not settings.ENABLE_AUTH:
|
||||||
|
# If authentication is disabled, allow the request to proceed
|
||||||
|
return {}
|
||||||
|
|
||||||
|
security = HTTPBearer()
|
||||||
|
credentials = await security(request)
|
||||||
|
|
||||||
|
if not credentials:
|
||||||
|
raise HTTPException(status_code=401, detail="Authorization header is missing")
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = parse_jwt_token(credentials.credentials)
|
||||||
|
request.state.user = payload
|
||||||
|
logging.info("Token decoded successfully")
|
||||||
|
except ValueError as e:
|
||||||
|
raise HTTPException(status_code=401, detail=str(e))
|
||||||
|
return payload
|
|
@ -0,0 +1,37 @@
|
||||||
|
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyjwt"
|
||||||
|
version = "2.8.0"
|
||||||
|
description = "JSON Web Token implementation in Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"},
|
||||||
|
{file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
crypto = ["cryptography (>=3.4.0)"]
|
||||||
|
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||||
|
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||||
|
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dotenv"
|
||||||
|
version = "1.0.1"
|
||||||
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
||||||
|
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
cli = ["click (>=5.0)"]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.0"
|
||||||
|
python-versions = ">=3.10,<4.0"
|
||||||
|
content-hash = "7030c59d6f7c40f49ee64eb60dccc8640b35a276617f9351fb2b93d382c7113d"
|
|
@ -0,0 +1,17 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "autogpt-libs"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Shared libraries across NextGen AutoGPT"
|
||||||
|
authors = ["Aarushi <aarushik93@gmail.com>"]
|
||||||
|
readme = "README.md"
|
||||||
|
packages = [{ include = "autogpt_libs" }]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = ">=3.10,<4.0"
|
||||||
|
pyjwt = "^2.8.0"
|
||||||
|
python-dotenv = "^1.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
|
@ -4,3 +4,4 @@ DB_NAME=agpt_local
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:${DB_PORT}/${DB_NAME}"
|
DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:${DB_PORT}/${DB_NAME}"
|
||||||
PRISMA_SCHEMA="postgres/schema.prisma"
|
PRISMA_SCHEMA="postgres/schema.prisma"
|
||||||
|
ENABLE_AUTH="false"
|
||||||
|
|
|
@ -5,9 +5,11 @@ from contextlib import asynccontextmanager
|
||||||
from typing import Annotated, Any, Dict
|
from typing import Annotated, Any, Dict
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
from autogpt_libs.auth.middleware import auth_middleware
|
||||||
from fastapi import (
|
from fastapi import (
|
||||||
APIRouter,
|
APIRouter,
|
||||||
Body,
|
Body,
|
||||||
|
Depends,
|
||||||
FastAPI,
|
FastAPI,
|
||||||
HTTPException,
|
HTTPException,
|
||||||
WebSocket,
|
WebSocket,
|
||||||
|
@ -80,6 +82,8 @@ class AgentServer(AppService):
|
||||||
|
|
||||||
# Define the API routes
|
# Define the API routes
|
||||||
router = APIRouter(prefix="/api")
|
router = APIRouter(prefix="/api")
|
||||||
|
router.dependencies.append(Depends(auth_middleware))
|
||||||
|
|
||||||
router.add_api_route(
|
router.add_api_route(
|
||||||
path="/blocks",
|
path="/blocks",
|
||||||
endpoint=self.get_graph_blocks, # type: ignore
|
endpoint=self.get_graph_blocks, # type: ignore
|
||||||
|
|
|
@ -25,7 +25,7 @@ requests = "*"
|
||||||
sentry-sdk = "^1.40.4"
|
sentry-sdk = "^1.40.4"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
benchmark = ["agbenchmark"]
|
benchmark = ["agbenchmark @ file:///Users/aarushi/autogpt/AutoGPT/benchmark"]
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "directory"
|
type = "directory"
|
||||||
|
@ -329,12 +329,29 @@ watchdog = "4.0.0"
|
||||||
webdriver-manager = "^4.0.1"
|
webdriver-manager = "^4.0.1"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
benchmark = ["agbenchmark"]
|
benchmark = ["agbenchmark @ file:///Users/aarushi/autogpt/AutoGPT/benchmark"]
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "directory"
|
type = "directory"
|
||||||
url = "../../forge"
|
url = "../../forge"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autogpt-libs"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Shared libraries across NextGen AutoGPT"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.10,<4.0"
|
||||||
|
files = []
|
||||||
|
develop = true
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pyjwt = "^2.8.0"
|
||||||
|
python-dotenv = "^1.0.1"
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "directory"
|
||||||
|
url = "../autogpt_libs"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backoff"
|
name = "backoff"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
|
@ -2383,6 +2400,9 @@ files = [
|
||||||
{file = "lief-0.14.1-cp312-cp312-manylinux_2_28_x86_64.manylinux_2_27_x86_64.whl", hash = "sha256:497b88f9c9aaae999766ba188744ee35c5f38b4b64016f7dbb7037e9bf325382"},
|
{file = "lief-0.14.1-cp312-cp312-manylinux_2_28_x86_64.manylinux_2_27_x86_64.whl", hash = "sha256:497b88f9c9aaae999766ba188744ee35c5f38b4b64016f7dbb7037e9bf325382"},
|
||||||
{file = "lief-0.14.1-cp312-cp312-win32.whl", hash = "sha256:08bad88083f696915f8dcda4042a3bfc514e17462924ec8984085838b2261921"},
|
{file = "lief-0.14.1-cp312-cp312-win32.whl", hash = "sha256:08bad88083f696915f8dcda4042a3bfc514e17462924ec8984085838b2261921"},
|
||||||
{file = "lief-0.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:e131d6158a085f8a72124136816fefc29405c725cd3695ce22a904e471f0f815"},
|
{file = "lief-0.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:e131d6158a085f8a72124136816fefc29405c725cd3695ce22a904e471f0f815"},
|
||||||
|
{file = "lief-0.14.1-cp313-cp313-manylinux_2_28_x86_64.manylinux_2_27_x86_64.whl", hash = "sha256:f9ff9a6959fb6d0e553cca41cd1027b609d27c5073e98d9fad8b774fbb5746c2"},
|
||||||
|
{file = "lief-0.14.1-cp313-cp313-win32.whl", hash = "sha256:95f295a7cc68f4e14ce7ea4ff8082a04f5313c2e5e63cc2bbe9d059190b7e4d5"},
|
||||||
|
{file = "lief-0.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:cdc1123c2e27970f8c8353505fd578e634ab33193c8d1dff36dc159e25599a40"},
|
||||||
{file = "lief-0.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df650fa05ca131e4dfeb42c77985e1eb239730af9944bc0aadb1dfac8576e0e8"},
|
{file = "lief-0.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df650fa05ca131e4dfeb42c77985e1eb239730af9944bc0aadb1dfac8576e0e8"},
|
||||||
{file = "lief-0.14.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b4e76eeb48ca2925c6ca6034d408582615f2faa855f9bb11482e7acbdecc4803"},
|
{file = "lief-0.14.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b4e76eeb48ca2925c6ca6034d408582615f2faa855f9bb11482e7acbdecc4803"},
|
||||||
{file = "lief-0.14.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:016e4fac91303466024154dd3c4b599e8b7c52882f72038b62a2be386d98c8f9"},
|
{file = "lief-0.14.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:016e4fac91303466024154dd3c4b599e8b7c52882f72038b62a2be386d98c8f9"},
|
||||||
|
@ -4170,6 +4190,23 @@ files = [
|
||||||
[package.extras]
|
[package.extras]
|
||||||
windows-terminal = ["colorama (>=0.4.6)"]
|
windows-terminal = ["colorama (>=0.4.6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyjwt"
|
||||||
|
version = "2.8.0"
|
||||||
|
description = "JSON Web Token implementation in Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"},
|
||||||
|
{file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
crypto = ["cryptography (>=3.4.0)"]
|
||||||
|
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||||
|
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||||
|
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pylatexenc"
|
name = "pylatexenc"
|
||||||
version = "2.10"
|
version = "2.10"
|
||||||
|
@ -6362,4 +6399,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "9013ff78344cb68878809bd7220453879c32c9291e39d99321dbcc9a7359855c"
|
content-hash = "b9a68db94e5721bfc916e583626e6da66a3469451d179bf9ee117d0a31b865f2"
|
||||||
|
|
|
@ -40,6 +40,7 @@ feedparser = "^6.0.11"
|
||||||
python-dotenv = "^1.0.1"
|
python-dotenv = "^1.0.1"
|
||||||
expiringdict = "^1.2.2"
|
expiringdict = "^1.2.2"
|
||||||
|
|
||||||
|
autogpt-libs = { path = "../autogpt_libs", develop = true }
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
cx-freeze = { git = "https://github.com/ntindle/cx_Freeze.git", rev = "main", develop = true }
|
cx-freeze = { git = "https://github.com/ntindle/cx_Freeze.git", rev = "main", develop = true }
|
||||||
poethepoet = "^0.26.1"
|
poethepoet = "^0.26.1"
|
||||||
|
|
Loading…
Reference in New Issue