From 564edfc4470b500ef101b84ea1e9e1c078f5dd41 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 3 Jun 2026 14:29:54 +0300 Subject: [PATCH] fix: do not hit sso provider on user missmatch (#3995) --- lnbits/core/views/auth_api.py | 8 +++++++- tests/api/test_auth_api.py | 36 ++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lnbits/core/views/auth_api.py b/lnbits/core/views/auth_api.py index 3d3ad25e2..71a792a93 100644 --- a/lnbits/core/views/auth_api.py +++ b/lnbits/core/views/auth_api.py @@ -36,6 +36,7 @@ from lnbits.decorators import ( check_account_exists, check_admin, check_user_exists, + optional_user_id, ) from lnbits.helpers import ( create_access_token, @@ -320,7 +321,10 @@ async def api_delete_user_api_token( @auth_router.get("/{provider}", description="SSO Provider") async def login_with_sso_provider( - request: Request, provider: str, user_id: str | None = None + request: Request, + provider: str, + user_id: str | None, + auth_user_id: str | None = Depends(optional_user_id), ): provider_sso = _new_sso(provider) if not provider_sso: @@ -328,6 +332,8 @@ async def login_with_sso_provider( HTTPStatus.FORBIDDEN, f"Login by '{provider}' not allowed.", ) + if user_id and user_id != auth_user_id: + raise HTTPException(HTTPStatus.FORBIDDEN, "User ID mismatch.") provider_sso.redirect_uri = str(request.base_url) + f"api/v1/auth/{provider}/token" with provider_sso: diff --git a/tests/api/test_auth_api.py b/tests/api/test_auth_api.py index 6e4b9457f..8993cd8a9 100644 --- a/tests/api/test_auth_api.py +++ b/tests/api/test_auth_api.py @@ -72,9 +72,43 @@ async def test_auth_api_sso_login_and_callback(http_client: AsyncClient, mocker) login_sso = _FakeSSO() mocker.patch("lnbits.core.views.auth_api._new_sso", return_value=login_sso) - response = await http_client.get( + unauthenticated = await http_client.get( f"/api/v1/auth/{provider}", params={"user_id": user.id} ) + assert unauthenticated.status_code == 403 + assert unauthenticated.json()["detail"] == "User ID mismatch." + + other_user = await create_user_account( + Account( + id=uuid4().hex, + username=f"user_{uuid4().hex[:8]}", + email=f"user_{uuid4().hex[:8]}@lnbits.com", + ) + ) + other_login = await http_client.post( + "/api/v1/auth/usr", json={"usr": other_user.id} + ) + http_client.cookies.clear() + assert other_login.status_code == 200 + other_headers = { + "Authorization": f"Bearer {other_login.json()['access_token']}", + } + wrong_user = await http_client.get( + f"/api/v1/auth/{provider}", + params={"user_id": user.id}, + headers=other_headers, + ) + assert wrong_user.status_code == 403 + assert wrong_user.json()["detail"] == "User ID mismatch." + + login = await http_client.post("/api/v1/auth/usr", json={"usr": user.id}) + http_client.cookies.clear() + assert login.status_code == 200 + headers = {"Authorization": f"Bearer {login.json()['access_token']}"} + + response = await http_client.get( + f"/api/v1/auth/{provider}", params={"user_id": user.id}, headers=headers + ) assert response.status_code == 307 assert response.headers["location"] == "https://example.com/sso/login" assert login_sso.redirect_uri == f"{http_client.base_url}/api/v1/auth/github/token"