Skip to content

Commit 977842b

Browse files
committed
use basic auth instead of post
1 parent b690f40 commit 977842b

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

pyiceberg/catalog/rest/auth.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717

1818
import base64
1919
import importlib
20+
import logging
2021
import threading
2122
import time
22-
import logging
2323
from abc import ABC, abstractmethod
24+
from functools import cached_property
2425
from typing import Any, Dict, List, Optional, Type
2526

2627
import requests
@@ -158,16 +159,19 @@ def __init__(
158159
self._expires_at = 0
159160
self._lock = threading.Lock()
160161

162+
@cached_property
163+
def _client_secret_header(self) -> str:
164+
creds = f"{self.client_id}:{self.client_secret}"
165+
creds_bytes = creds.encode("utf-8")
166+
b64_creds = base64.b64encode(creds_bytes).decode("utf-8")
167+
return f"Basic {b64_creds}"
168+
161169
def _refresh_token(self) -> None:
162-
data = {
163-
"grant_type": "client_credentials",
164-
"client_id": self.client_id,
165-
"client_secret": self.client_secret,
166-
}
170+
data = {}
167171
if self.scope:
168172
data["scope"] = self.scope
169173

170-
response = requests.post(self.token_url, data=data)
174+
response = requests.post(self.token_url, data=data, headers={"Authorization": self._client_secret_header})
171175
response.raise_for_status()
172176
result = response.json()
173177

@@ -177,11 +181,11 @@ def _refresh_token(self) -> None:
177181
raise ValueError(
178182
"The expiration time of the Token must be provided by the Server in the Access Token Response in `expires_in` field, or by the PyIceberg Client."
179183
)
180-
self._expires_at = time.time() + expires_in - self.refresh_margin
184+
self._expires_at = time.monotonic() + expires_in - self.refresh_margin
181185

182186
def get_token(self) -> str:
183187
with self._lock:
184-
if not self._token or time.time() >= self._expires_at:
188+
if not self._token or time.monotonic() >= self._expires_at:
185189
self._refresh_token()
186190
if self._token is None:
187191
raise ValueError("Authorization token is None after refresh")
@@ -211,6 +215,8 @@ def __init__(
211215

212216
def auth_header(self) -> str:
213217
return f"Bearer {self.token_provider.get_token()}"
218+
219+
214220
class GoogleAuthManager(AuthManager):
215221
"""An auth manager that is responsible for handling Google credentials."""
216222

tests/catalog/test_rest.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from unittest import mock
2222

2323
import pytest
24+
from requests.exceptions import HTTPError
2425
from requests_mock import Mocker
2526

2627
import pyiceberg
@@ -1680,6 +1681,29 @@ def test_rest_catalog_with_oauth2_auth_type(requests_mock: Mocker) -> None:
16801681
assert catalog.uri == TEST_URI
16811682

16821683

1684+
def test_rest_catalog_oauth2_non_200_token_response(requests_mock: Mocker) -> None:
1685+
requests_mock.post(
1686+
f"{TEST_URI}oauth2/token",
1687+
json={"error": "invalid_client"},
1688+
status_code=401,
1689+
)
1690+
catalog_properties = {
1691+
"uri": TEST_URI,
1692+
"auth": {
1693+
"type": "oauth2",
1694+
"oauth2": {
1695+
"client_id": "bad_client_id",
1696+
"client_secret": "bad_client_secret",
1697+
"token_url": f"{TEST_URI}oauth2/token",
1698+
"scope": "read",
1699+
},
1700+
},
1701+
}
1702+
1703+
with pytest.raises(HTTPError):
1704+
RestCatalog("rest", **catalog_properties) # type: ignore
1705+
1706+
16831707
EXAMPLE_ENV = {"PYICEBERG_CATALOG__PRODUCTION__URI": TEST_URI}
16841708

16851709

0 commit comments

Comments
 (0)