oauth2-cli-auth 1.6.1__tar.gz → 2.0.0__tar.gz

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.
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oauth2-cli-auth
3
- Version: 1.6.1
3
+ Version: 2.0.0
4
4
  Summary: Authenticate against OAuth2 Provider in Python CLIs
5
5
  License: MIT
6
6
  Author: Timo Reymann
7
7
  Author-email: mail@timo-reymann.de
8
- Requires-Python: >=3.9
8
+ Requires-Python: >=3.10
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Environment :: Console
11
11
  Classifier: Environment :: MacOS X
@@ -15,7 +15,6 @@ Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Operating System :: POSIX
16
16
  Classifier: Operating System :: Unix
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
@@ -3,5 +3,5 @@ Authenticate against OAuth2 Provider in Python CLIs
3
3
  """
4
4
  from oauth2_cli_auth.__version__ import __version__
5
5
  from oauth2_cli_auth.http_server import OAuthCallbackHttpServer
6
- from oauth2_cli_auth.code_grant import OAuth2ClientInfo, exchange_code_for_access_token, get_auth_url, open_browser, load_oidc_config
6
+ from oauth2_cli_auth.code_grant import OAuth2ClientInfo, refresh_access_token, exchange_code_for_access_token, exchange_code_for_response, get_auth_url, open_browser, load_oidc_config
7
7
  from oauth2_cli_auth.simplified_flow import get_access_token_with_browser_open
@@ -0,0 +1 @@
1
+ __version__ = "2.0.0"
@@ -2,6 +2,7 @@ import json
2
2
  import time
3
3
  import urllib
4
4
  from urllib.error import URLError
5
+ from typing import Union
5
6
 
6
7
 
7
8
  def _urlopen_with_backoff(url, max_retries=3, base_delay=1, timeout=15):
@@ -19,7 +20,7 @@ def _urlopen_with_backoff(url, max_retries=3, base_delay=1, timeout=15):
19
20
  raise URLError(f"Failed to open URL after {max_retries} retries")
20
21
 
21
22
 
22
- def _load_json(url_or_request: str | urllib.request.Request) -> dict:
23
+ def _load_json(url_or_request: Union[str, urllib.request.Request]) -> dict:
23
24
  with _urlopen_with_backoff(url_or_request) as response:
24
25
  response_data = response.read().decode('utf-8')
25
26
  json_response = json.loads(response_data)
@@ -7,6 +7,7 @@ import urllib.request
7
7
  import webbrowser
8
8
  from collections.abc import Callable
9
9
  from dataclasses import dataclass
10
+ from typing import Optional
10
11
 
11
12
  from oauth2_cli_auth._urllib_util import _load_json
12
13
 
@@ -25,11 +26,14 @@ class OAuth2ClientInfo:
25
26
  client_id: str
26
27
  """Id of the client to request for"""
27
28
 
29
+ client_secret: str | None
30
+ """Secret of the client to request for"""
31
+
28
32
  scopes: list[str]
29
33
  """List of scopes to request"""
30
34
 
31
35
  @staticmethod
32
- def from_oidc_endpoint(oidc_config_endpoint: str, client_id: str, scopes: list[str]) -> "OAuth2ClientInfo":
36
+ def from_oidc_endpoint(oidc_config_endpoint: str, client_id: str, scopes: list[str] = [], client_secret: str | None = None) -> "OAuth2ClientInfo":
33
37
  """
34
38
  Create client information object from well known endpoint in format as specified in
35
39
  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
@@ -44,6 +48,7 @@ class OAuth2ClientInfo:
44
48
  authorization_url=config.get("authorization_endpoint"),
45
49
  token_url=config.get("token_endpoint"),
46
50
  client_id=client_id,
51
+ client_secret=client_secret,
47
52
  scopes=scopes,
48
53
  )
49
54
 
@@ -58,7 +63,7 @@ def load_oidc_config(odic_well_known_endpoint: str) -> dict:
58
63
  return config
59
64
 
60
65
 
61
- def open_browser(url: str, print_open_browser_instruction: Callable[[str], None] | None = print) -> None:
66
+ def open_browser(url: str, print_open_browser_instruction: Optional[Callable[[str], None]] = print) -> None:
62
67
  """
63
68
  Open browser using webbrowser module and show message about URL open
64
69
 
@@ -101,7 +106,7 @@ def exchange_code_for_response(
101
106
  """
102
107
  headers = {
103
108
  "Content-Type": "application/x-www-form-urlencoded",
104
- "Authorization": "Basic " + base64.b64encode(f"{client_info.client_id}:".encode()).decode(),
109
+ "Authorization": "Basic " + base64.b64encode(f"{client_info.client_id}:{client_info.client_secret if client_info.client_secret is not None else ''}".encode()).decode(),
105
110
  }
106
111
 
107
112
  data = {
@@ -116,6 +121,32 @@ def exchange_code_for_response(
116
121
 
117
122
  return json_response
118
123
 
124
+ def refresh_access_token(
125
+ client_info: OAuth2ClientInfo,
126
+ refresh_token: str,
127
+ ) -> dict:
128
+ """
129
+ Refresh an access token using the endpoints from client info
130
+
131
+ :param client_info: Info about oauth2 client
132
+ :param refresh_token: Refresh token to use
133
+ :return: Response from OAuth2 endpoint
134
+ """
135
+ headers = {
136
+ "Content-Type": "application/x-www-form-urlencoded",
137
+ "Authorization": "Basic " + base64.b64encode(f"{client_info.client_id}:{client_info.client_secret if client_info.client_secret is not None else ''}".encode()).decode(),
138
+ }
139
+
140
+ data = {
141
+ "refresh_token": refresh_token,
142
+ "grant_type": "refresh_token",
143
+ }
144
+ encoded_data = urllib.parse.urlencode(data).encode('utf-8')
145
+
146
+ request = urllib.request.Request(client_info.token_url, data=encoded_data, headers=headers)
147
+ json_response = _load_json(request)
148
+
149
+ return json_response
119
150
 
120
151
  def exchange_code_for_access_token(
121
152
  client_info: OAuth2ClientInfo,
@@ -234,9 +234,9 @@ class OAuthCallbackHttpServer(HTTPServer):
234
234
  def __init__(self, port):
235
235
  super().__init__(("", port), OAuthRedirectHandler)
236
236
 
237
- self._code: str | None = None
237
+ self._code: Optional[str] = None
238
238
 
239
- def get_code(self) -> str | None:
239
+ def get_code(self) -> Optional[str]:
240
240
  """
241
241
  This method should only be called after the request was done and might be None when no token is given.
242
242
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "oauth2-cli-auth"
3
- version = "1.6.1"
3
+ version = "2.0.0"
4
4
  description = "Authenticate against OAuth2 Provider in Python CLIs"
5
5
  authors = ["Timo Reymann <mail@timo-reymann.de>"]
6
6
  license = "MIT"
@@ -26,7 +26,7 @@ include = [
26
26
  "LICENSE"
27
27
  ]
28
28
  [tool.poetry.dependencies]
29
- python = ">=3.9"
29
+ python = ">=3.10"
30
30
 
31
31
  [tool.poetry.urls]
32
32
  "Bug Tracker" = "https://github.com/timo-reymann/python-oauth2-cli-auth/issues"
@@ -39,7 +39,7 @@ priority="explicit"
39
39
  [tool.poetry.group.dev.dependencies]
40
40
  pytest = "^8.0.0"
41
41
  coverage = "^7.3.2"
42
- setuptools = "^78.0.0"
42
+ setuptools = "^80.0.0"
43
43
  pydoctor = "^24.0.0"
44
44
 
45
45
  [tool.coverage.run]
@@ -1 +0,0 @@
1
- __version__ = "1.6.1"
File without changes