From 2bbf688613f4632b68cbc0eaa88cfdfa5c741941 Mon Sep 17 00:00:00 2001 From: Yassin Kortam Date: Wed, 10 Jun 2026 17:22:20 -0700 Subject: [PATCH] build(auth_v2): add Authlib and scim2-models for the auth_v2 module Pull in the OSS libraries the standards-based auth module orchestrates: Authlib for the OIDC login flow and scim2-models for SCIM 2.0, and switch PyJWT to the [crypto] extra so JWKS-backed RS256 verification is explicit (cryptography was already a proxy dependency). scim2-models ships py.typed but its generic, alias-driven models trip mypy's call-arg check though they work at runtime, so treat the library as untyped at the boundary in both litellm/mypy.ini (used by CI) and the root pyproject mypy config. --- litellm/mypy.ini | 6 +++++- pyproject.toml | 14 +++++++++++++- uv.lock | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/litellm/mypy.ini b/litellm/mypy.ini index 4702b591124..bdd60084f70 100644 --- a/litellm/mypy.ini +++ b/litellm/mypy.ini @@ -16,4 +16,8 @@ ignore_errors = True [mypy-fastuuid.*] ignore_missing_imports = True -ignore_errors = True \ No newline at end of file +ignore_errors = True + +[mypy-scim2_models.*] +follow_imports = skip +follow_imports_for_stubs = True \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b9d76379faf..610305e1905 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,9 @@ proxy = [ "orjson>=3.11.6,<4.0", "apscheduler>=3.11.2,<4.0", "fastapi-sso>=0.19.0,<1.0", - "PyJWT>=2.13.0,<3.0", + "PyJWT[crypto]>=2.13.0,<3.0", + "Authlib>=1.6.0,<2.0", + "scim2-models>=0.6.0,<1.0", "python-multipart>=0.0.27,<1.0", "cryptography>=46.0.7,<47.0", "pynacl>=1.6.2,<2.0", @@ -277,6 +279,16 @@ version_files = [ [tool.mypy] plugins = "pydantic.mypy" +# scim2-models ships py.typed, but its generic, alias-driven SCIM models report +# phantom call-arg errors under mypy though they work at runtime. Treat the +# library as untyped at the boundary; litellm/auth_v2 is its only consumer. +# CI runs mypy from litellm/ against litellm/mypy.ini, which carries the same +# override; this block keeps root-level mypy runs consistent. +[[tool.mypy.overrides]] +module = "scim2_models.*" +follow_imports = "skip" +follow_imports_for_stubs = true + [tool.pytest.ini_options] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "session" diff --git a/uv.lock b/uv.lock index 1100db783d3..e1b84f4e629 100644 --- a/uv.lock +++ b/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-06-05T23:18:37.734017Z" +exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. exclude-newer-span = "P3D" [manifest] @@ -423,6 +423,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/1f/aa74b23b6eea4cf9b79ace914df59123c4c8e7e4bd32dd22d09c126422d9/aurelio_sdk-0.0.19-py3-none-any.whl", hash = "sha256:390c0212b59ce99116df8722d3badced88c5ef0bb742a6222d479ceed0ed3948", size = 17322, upload-time = "2025-03-24T14:37:31.305Z" }, ] +[[package]] +name = "authlib" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "joserfc" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" }, +] + [[package]] name = "azure-ai-contentsafety" version = "1.0.0" @@ -2735,6 +2748,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] +[[package]] +name = "joserfc" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/c3/2f590052b55cbdd0ace470ee7ee1f685f6882051be93a9374891005623e2/joserfc-1.7.0.tar.gz", hash = "sha256:4aced6ab0c47846f0a531402aec2419a874b91e918df9c4c9da8a82fb559d6c4", size = 232967, upload-time = "2026-06-02T09:59:34.506Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/83/b6b62a66a06ce872d9429a5eb5ee20b2002fd9c331b953c94381c1f7c9f9/joserfc-1.7.0-py3-none-any.whl", hash = "sha256:17e5d7a5a35e65442b05efc435a3d5d46696ffa2c8a2ed0eea6f63fc268e3224", size = 70387, upload-time = "2026-06-02T09:59:33.264Z" }, +] + [[package]] name = "jsonlines" version = "4.0.0" @@ -3323,6 +3348,7 @@ mlflow = [ ] proxy = [ { name = "apscheduler" }, + { name = "authlib" }, { name = "azure-identity" }, { name = "azure-storage-blob" }, { name = "backoff" }, @@ -3338,7 +3364,7 @@ proxy = [ { name = "orjson" }, { name = "polars" }, { name = "pydantic-settings" }, - { name = "pyjwt" }, + { name = "pyjwt", extra = ["crypto"] }, { name = "pynacl" }, { name = "pyroscope-io", marker = "sys_platform != 'win32'" }, { name = "python-multipart" }, @@ -3346,6 +3372,7 @@ proxy = [ { name = "restrictedpython" }, { name = "rich" }, { name = "rq" }, + { name = "scim2-models" }, { name = "soundfile" }, { name = "starlette" }, { name = "uvicorn" }, @@ -3477,6 +3504,7 @@ requires-dist = [ { name = "apscheduler", marker = "extra == 'proxy'", specifier = ">=3.11.2,<4.0" }, { name = "audioread", marker = "extra == 'stt-nvidia-riva'", specifier = ">=3.0.1" }, { name = "aurelio-sdk", marker = "python_full_version < '3.14' and extra == 'semantic-router'", specifier = ">=0.0.19,<1.0" }, + { name = "authlib", marker = "extra == 'proxy'", specifier = ">=1.6.0,<2.0" }, { name = "azure-ai-contentsafety", marker = "extra == 'proxy-runtime'", specifier = ">=1.0.0,<2.0" }, { name = "azure-identity", marker = "extra == 'extra-proxy'", specifier = ">=1.25.2,<2.0" }, { name = "azure-identity", marker = "extra == 'proxy'", specifier = ">=1.25.2,<2.0" }, @@ -3527,7 +3555,7 @@ requires-dist = [ { name = "prometheus-client", marker = "extra == 'proxy-runtime'", specifier = ">=0.20.0,<1.0" }, { name = "pydantic", specifier = ">=2.10.0,<3.0.0" }, { name = "pydantic-settings", marker = "extra == 'proxy'", specifier = ">=2.14.1,<3.0" }, - { name = "pyjwt", marker = "extra == 'proxy'", specifier = ">=2.13.0,<3.0" }, + { name = "pyjwt", extras = ["crypto"], marker = "extra == 'proxy'", specifier = ">=2.13.0,<3.0" }, { name = "pynacl", marker = "extra == 'proxy'", specifier = ">=1.6.2,<2.0" }, { name = "pypdf", marker = "python_full_version < '3.14' and extra == 'proxy-runtime'", specifier = ">=6.10.2,<7.0" }, { name = "pyroscope-io", marker = "sys_platform != 'win32' and extra == 'proxy'", specifier = ">=0.8.16,<1.0" }, @@ -3542,6 +3570,7 @@ requires-dist = [ { name = "rich", marker = "extra == 'cli'", specifier = ">=13.9.4,<14.0" }, { name = "rich", marker = "extra == 'proxy'", specifier = ">=13.9.4,<14.0" }, { name = "rq", marker = "extra == 'proxy'", specifier = ">=2.7.0,<3.0" }, + { name = "scim2-models", marker = "extra == 'proxy'", specifier = ">=0.6.0,<1.0" }, { name = "semantic-router", marker = "python_full_version < '3.14' and extra == 'semantic-router'", specifier = ">=0.1.15,<1.0" }, { name = "sentry-sdk", marker = "extra == 'proxy-runtime'", specifier = ">=2.21.0,<3.0" }, { name = "soundfile", marker = "extra == 'proxy'", specifier = ">=0.12.1,<1.0" }, @@ -6894,6 +6923,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, ] +[[package]] +name = "scim2-models" +version = "0.6.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic", extra = ["email"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/67/121a60b8433839fb19a9986e8d9f5416941e42ea6827bd7e520783477cca/scim2_models-0.6.12.tar.gz", hash = "sha256:3a57c5ec10dc1007e5f4391d3bddeaca974be8d55e9ede4a1de20019ad3a920b", size = 48631, upload-time = "2026-04-13T12:37:11.888Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/b0/04d4b4e10ce59e813a3000d3ccb97537e88ab0341aacebc51abee50ed637/scim2_models-0.6.12-py3-none-any.whl", hash = "sha256:8ccc8139c61f84a29a1c54d8eda3f8fd121534cdd292166be1e8289d6f05902a", size = 61012, upload-time = "2026-04-13T12:37:10.322Z" }, +] + [[package]] name = "scipy" version = "1.15.3"