newapi-python 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
newapi/__init__.py CHANGED
@@ -72,4 +72,4 @@ __all__ = [
72
72
  "ValidationError",
73
73
  ]
74
74
 
75
- __version__ = "0.2.0"
75
+ __version__ = "0.3.0"
newapi/_client.py CHANGED
@@ -218,6 +218,12 @@ class NewAPI:
218
218
  ) -> int | None:
219
219
  if access_token and session_token:
220
220
  raise ConfigurationError("pass either access_token or session_token, not both")
221
+ if access_token and user_id is None:
222
+ raise ConfigurationError(
223
+ "user_id is required when authenticating with an access_token; "
224
+ "new-api instances reject such requests with 401 "
225
+ "'Unauthorized, New-Api-User header not provided'"
226
+ )
221
227
  if not session_token or user_id is not None:
222
228
  return user_id
223
229
  try:
@@ -342,6 +348,8 @@ class NewAPI:
342
348
  return
343
349
  refresh_token = self._tokens.refresh_token
344
350
  headers: dict[str, str] = {"Origin": self._origin()}
351
+ if self._tokens.user_id is not None:
352
+ headers[_USER_ID_HEADER] = str(self._tokens.user_id)
345
353
  if self._tokens.access_token:
346
354
  headers["Authorization"] = f"{self._tokens.token_type} {self._tokens.access_token}"
347
355
  try:
@@ -508,16 +516,17 @@ class NewAPI:
508
516
  request_headers = {
509
517
  "Accept": "application/json",
510
518
  "Accept-Language": self.language,
511
- "User-Agent": "newapi-python/0.2.0",
519
+ "User-Agent": "newapi-python/0.3.0",
512
520
  }
513
- if authenticated and self._tokens.session_token:
514
- request_headers["Cookie"] = f"{self._session_cookie}={self._tokens.session_token}"
521
+ if authenticated:
515
522
  if self._tokens.user_id is not None:
516
523
  request_headers[_USER_ID_HEADER] = str(self._tokens.user_id)
517
- elif authenticated and self._tokens.access_token:
518
- request_headers["Authorization"] = (
519
- f"{self._tokens.token_type} {self._tokens.access_token}"
520
- )
524
+ if self._tokens.session_token:
525
+ request_headers["Cookie"] = f"{self._session_cookie}={self._tokens.session_token}"
526
+ elif self._tokens.access_token:
527
+ request_headers["Authorization"] = (
528
+ f"{self._tokens.token_type} {self._tokens.access_token}"
529
+ )
521
530
  if refresh_cookie and not self._session_has_refresh_cookie():
522
531
  request_headers["Cookie"] = f"{_REFRESH_COOKIE}={refresh_cookie}"
523
532
  if headers:
@@ -677,12 +686,19 @@ class NewAPI:
677
686
  if isinstance(session, Mapping):
678
687
  raw_session_id = session.get("sid")
679
688
  session_id = raw_session_id if isinstance(raw_session_id, str) else None
689
+ user_id = self._tokens.user_id
690
+ user_data = auth.get("user")
691
+ if isinstance(user_data, Mapping):
692
+ raw_user_id = user_data.get("id")
693
+ if isinstance(raw_user_id, int) and not isinstance(raw_user_id, bool):
694
+ user_id = raw_user_id
680
695
  return SessionTokens(
681
696
  access_token=str(auth["access_token"]),
682
697
  refresh_token=refresh_token,
683
698
  expires_at=expires_at,
684
699
  token_type=str(token_type) if token_type else "Bearer",
685
700
  session_id=session_id,
701
+ user_id=user_id,
686
702
  )
687
703
 
688
704
  def _resolve_quota_per_unit(self) -> float:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: newapi-python
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Python client for the user-facing dashboard API of new-api instances
5
5
  Author: Eight Labs
6
6
  License-Expression: MIT
@@ -55,10 +55,13 @@ client = NewAPI(
55
55
  "https://newapi.example.com",
56
56
  access_token=os.environ["NEWAPI_ACCESS_TOKEN"],
57
57
  refresh_token=os.environ.get("NEWAPI_REFRESH_TOKEN"),
58
+ user_id=int(os.environ["NEWAPI_USER_ID"]),
58
59
  )
59
60
  ```
60
61
 
61
- Pass either the instance origin or its full `/api` URL. Tokens are retained only in memory. If a refresh token is supplied, the client sends it as the `new_api_refresh` cookie, rotates the pair after an authenticated `401`, and refreshes proactively when `expires_at` is known. Users can also generate a long-lived system access token from the dashboard and pass it as `access_token` alone; such tokens cannot refresh browser sessions.
62
+ Pass either the instance origin or its full `/api` URL. Tokens are retained only in memory. If a refresh token is supplied, the client sends it as the `new_api_refresh` cookie, rotates the pair after an authenticated `401`, and refreshes proactively when `expires_at` is known.
63
+
64
+ Users can also generate a long-lived system access token from the dashboard and pass it as `access_token`. Access-token authentication additionally requires `user_id`: new-api instances verify the numeric `New-Api-User` header on every authenticated request and reject requests without it with `401 Unauthorized, New-Api-User header not provided`. The client sends the header automatically from `user_id`, and `login()` and `refresh()` capture the id from the instance's responses. Such tokens cannot refresh browser sessions on their own.
62
65
 
63
66
  The default browser fingerprint is Chrome. Choose another `curl_cffi` fingerprint or configure proxies by supplying your own `curl_cffi.requests.Session`:
64
67
 
@@ -1,12 +1,12 @@
1
- newapi/__init__.py,sha256=akvqqEGOVw0ZIeTFAftG6azoTWbzM8iMDxj5sIJmX74,1310
2
- newapi/_client.py,sha256=O9IbZqqM8HV7I0zslC99M6Mk4PuK0PpLiJHAi1kmHqE,25040
1
+ newapi/__init__.py,sha256=_BZDS3mvNV8s4ckB4x4duVPa5QxruHLHxCeuS5gYc5I,1310
2
+ newapi/_client.py,sha256=ci7AU9R0_IA_xx-Ev01xjv8NMdxgPoJr2dgrMu85zaI,25791
3
3
  newapi/_crypto.py,sha256=ocWpBUsQoZlg_dk1Q_vLVKLGEc6I80RpgYlHmLQXZ-I,3902
4
4
  newapi/_exceptions.py,sha256=YlZVzYf6VUKSN-fVVvFHfunoNJg6LLcuR2IdHNKzaCg,2049
5
5
  newapi/_models.py,sha256=FWUDMqjroiaZLZdj8v_CKcANB6XZZMULrXWdu5mrxE0,6379
6
6
  newapi/_resources.py,sha256=IQ1B3QoufC4p8KpXg6NlU9nmUQgTYmncSIYPLoLKYUc,28476
7
7
  newapi/_session_blob.py,sha256=GIv4k2H0YrPghNGRg8iY2LZ8kgkFLrlMCpvUtIy35kw,3547
8
8
  newapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- newapi_python-0.2.0.dist-info/METADATA,sha256=Ngb5X68FhWICpNHOIq7OaNGhflKG0oHkVnj0_0PGdKE,10937
10
- newapi_python-0.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
11
- newapi_python-0.2.0.dist-info/licenses/LICENSE,sha256=SIZvsDcR9_4_Q4IMiJNqUwZWvdAmwjRHlXIQkXFwCwE,1067
12
- newapi_python-0.2.0.dist-info/RECORD,,
9
+ newapi_python-0.3.0.dist-info/METADATA,sha256=SFbWbiKQppLpy6HXcJ5uTuVVn-KGoV36wDLyjroxlro,11367
10
+ newapi_python-0.3.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
11
+ newapi_python-0.3.0.dist-info/licenses/LICENSE,sha256=SIZvsDcR9_4_Q4IMiJNqUwZWvdAmwjRHlXIQkXFwCwE,1067
12
+ newapi_python-0.3.0.dist-info/RECORD,,