Skip to content

Commit 76850e5

Browse files
authored
Fix/fix auth agenthub (#30)
1 parent e9a34ee commit 76850e5

9 files changed

Lines changed: 115 additions & 106 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to `uipath_llm_client` (core package) will be documented in this file.
44

5+
## [1.2.0] - 2026-02-18
6+
7+
### Stable release
8+
9+
### Fix
10+
- Fixed agenhub auth when token already exists
11+
512
## [1.1.1] - 2026-02-12
613

714
### Fix

packages/uipath_langchain_client/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to `uipath_langchain_client` will be documented in this file.
44

5+
## [1.2.0] - 2026-02-18
6+
7+
### Stable release
8+
59
## [1.1.9] - 2026-02-13
610

711
### Docs

packages/uipath_langchain_client/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
requires-python = ">=3.11"
77
dependencies = [
88
"langchain>=1.2.7",
9-
"uipath-llm-client>=1.1.1",
9+
"uipath-llm-client>=1.2.0",
1010
]
1111

1212
[project.optional-dependencies]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__title__ = "UiPath LangChain Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services via LangChain."
3-
__version__ = "1.1.9"
3+
__version__ = "1.2.0"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__titile__ = "UiPath LLM Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services."
3-
__version__ = "1.1.1"
3+
__version__ = "1.2.0"

src/uipath_llm_client/settings/agenthub/auth.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,18 @@ def __init__(
2525
settings: AgentHub settings containing authentication credentials.
2626
"""
2727
self.settings = settings
28-
self.access_token = self._get_access_token()
2928

30-
def _get_access_token(self) -> str:
29+
def get_access_token(self, refresh: bool = False) -> str:
3130
"""Retrieve or refresh the access token."""
32-
self.settings.authenticate()
3331
assert self.settings.access_token is not None
32+
if refresh:
33+
self.settings.authenticate(force=True)
3434
return self.settings.access_token.get_secret_value()
3535

3636
def auth_flow(self, request: Request) -> Generator[Request, Response, None]:
3737
"""HTTPX auth flow that handles token refresh on authentication failures."""
38-
request.headers["Authorization"] = f"Bearer {self.access_token}"
38+
request.headers["Authorization"] = f"Bearer {self.get_access_token()}"
3939
response = yield request
4040
if response.status_code == 401:
41-
self.access_token = self._get_access_token()
42-
request.headers["Authorization"] = f"Bearer {self.access_token}"
41+
request.headers["Authorization"] = f"Bearer {self.get_access_token(refresh=True)}"
4342
yield request

src/uipath_llm_client/settings/agenthub/settings.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def validate_environment(self) -> Self:
5959
self.authenticate()
6060
return self
6161

62-
def check_credentials(self) -> bool:
62+
def credentials_available(self) -> bool:
6363
"""Check if all required credentials are present."""
6464
return (
6565
self.access_token is not None
@@ -68,28 +68,29 @@ def check_credentials(self) -> bool:
6868
and self.organization_id is not None
6969
)
7070

71-
def authenticate(self) -> None:
71+
def load_credentials(self) -> None:
72+
"""Load credentials from environment variables."""
73+
load_dotenv(override=True)
74+
self.access_token = SecretStr(os.getenv("UIPATH_ACCESS_TOKEN", ""))
75+
self.base_url = os.getenv("UIPATH_URL")
76+
self.tenant_id = os.getenv("UIPATH_TENANT_ID")
77+
self.organization_id = os.getenv("UIPATH_ORGANIZATION_ID")
78+
79+
def authenticate(self, force: bool = False) -> None:
7280
"""Authenticate with UiPath using the configured credentials."""
73-
if not self.check_credentials():
74-
auth_service = AuthService(
75-
environment=self.environment,
76-
force=True,
77-
client_id=self.client_id.get_secret_value() if self.client_id is not None else None,
78-
client_secret=self.client_secret.get_secret_value()
79-
if self.client_secret is not None
80-
else None,
81-
base_url=self.base_url,
82-
tenant=self.tenant_id,
83-
scope=self.client_scope,
84-
)
85-
auth_service.authenticate()
86-
load_dotenv(override=True)
87-
self.access_token = SecretStr(os.getenv("UIPATH_ACCESS_TOKEN", ""))
88-
self.base_url = os.getenv("UIPATH_URL")
89-
self.tenant_id = os.getenv("UIPATH_TENANT_ID")
90-
self.organization_id = os.getenv("UIPATH_ORGANIZATION_ID")
91-
if not self.check_credentials():
92-
raise ValueError("Could not authenticate with UiPath")
81+
auth_service = AuthService(
82+
environment=self.environment,
83+
force=force or not self.credentials_available(),
84+
client_id=self.client_id.get_secret_value() if self.client_id is not None else None,
85+
client_secret=self.client_secret.get_secret_value()
86+
if self.client_secret is not None
87+
else None,
88+
base_url=self.base_url,
89+
tenant=self.tenant_id,
90+
scope=self.client_scope,
91+
)
92+
auth_service.authenticate()
93+
self.load_credentials()
9394

9495
@override
9596
def build_base_url(

tests/core/test_base_client.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,12 @@ def test_build_auth_pipeline_returns_auth(self, agenthub_env_vars):
467467
auth = settings.build_auth_pipeline()
468468
assert isinstance(auth, Auth)
469469

470-
def test_check_credentials(self, agenthub_env_vars):
471-
"""Test check_credentials returns True when all credentials present."""
470+
def test_credentials_available(self, agenthub_env_vars):
471+
"""Test credentials_available returns True when all credentials present."""
472472
with patch.dict(os.environ, agenthub_env_vars, clear=True):
473473
with patch("uipath_llm_client.settings.agenthub.settings.AuthService"):
474474
settings = AgentHubSettings()
475-
assert settings.check_credentials() is True
475+
assert settings.credentials_available() is True
476476

477477

478478
# ============================================================================
@@ -505,8 +505,8 @@ def test_auth_flow_refreshes_on_401(self, agenthub_env_vars):
505505
with patch("uipath_llm_client.settings.agenthub.settings.AuthService"):
506506
settings = AgentHubSettings()
507507

508-
# Mock _get_access_token to return a new token on refresh
509-
with patch.object(AgentHubAuth, "_get_access_token", return_value="initial-token"):
508+
# Mock get_access_token to return a new token on refresh
509+
with patch.object(AgentHubAuth, "get_access_token", return_value="initial-token"):
510510
auth = AgentHubAuth(settings=settings)
511511

512512
request = Request("GET", "https://example.com")
@@ -520,10 +520,8 @@ def test_auth_flow_refreshes_on_401(self, agenthub_env_vars):
520520
mock_response = MagicMock(spec=Response)
521521
mock_response.status_code = 401
522522

523-
# Mock the _get_access_token method to return a new token
524-
with patch.object(
525-
AgentHubAuth, "_get_access_token", return_value="refreshed-token"
526-
):
523+
# Mock the get_access_token method to return a new token
524+
with patch.object(AgentHubAuth, "get_access_token", return_value="refreshed-token"):
527525
try:
528526
retry_request = flow.send(mock_response)
529527
assert retry_request.headers["Authorization"] == "Bearer refreshed-token"

0 commit comments

Comments
 (0)