kobil-auth 3.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.
- kobil_auth-3.0.0/PKG-INFO +39 -0
- kobil_auth-3.0.0/README.md +28 -0
- kobil_auth-3.0.0/kobil/auth/__init__.py +106 -0
- kobil_auth-3.0.0/kobil_auth.egg-info/PKG-INFO +39 -0
- kobil_auth-3.0.0/kobil_auth.egg-info/SOURCES.txt +8 -0
- kobil_auth-3.0.0/kobil_auth.egg-info/dependency_links.txt +1 -0
- kobil_auth-3.0.0/kobil_auth.egg-info/requires.txt +1 -0
- kobil_auth-3.0.0/kobil_auth.egg-info/top_level.txt +1 -0
- kobil_auth-3.0.0/pyproject.toml +20 -0
- kobil_auth-3.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kobil-auth
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: KOBIL OIDC (OpenID Connect) authentication helper for Python.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/uveysservetoglu/kobil-sdks
|
|
7
|
+
Keywords: kobil,oidc,oauth2,authentication,openid-connect
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: kobil-core>=1.0
|
|
11
|
+
|
|
12
|
+
# kobil-auth
|
|
13
|
+
|
|
14
|
+
KOBIL OIDC (OpenID Connect) authentication helper for Python.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install kobil-auth
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from kobil.auth import AuthClient
|
|
22
|
+
|
|
23
|
+
auth = AuthClient(
|
|
24
|
+
authorization_endpoint="https://idp.../auth",
|
|
25
|
+
token_endpoint="https://idp.../token",
|
|
26
|
+
client_id="...",
|
|
27
|
+
client_secret="...",
|
|
28
|
+
scope="openid profile email",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
url = auth.build_authorization_url("https://app.example.com/callback")
|
|
32
|
+
tokens = auth.exchange_code_for_token(code, "https://app.example.com/callback")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Failures raise `kobil.auth.KobilAuthError` (subclass of `kobil.core.KobilError`).
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# kobil-auth
|
|
2
|
+
|
|
3
|
+
KOBIL OIDC (OpenID Connect) authentication helper for Python.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install kobil-auth
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from kobil.auth import AuthClient
|
|
11
|
+
|
|
12
|
+
auth = AuthClient(
|
|
13
|
+
authorization_endpoint="https://idp.../auth",
|
|
14
|
+
token_endpoint="https://idp.../token",
|
|
15
|
+
client_id="...",
|
|
16
|
+
client_secret="...",
|
|
17
|
+
scope="openid profile email",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
url = auth.build_authorization_url("https://app.example.com/callback")
|
|
21
|
+
tokens = auth.exchange_code_for_token(code, "https://app.example.com/callback")
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Failures raise `kobil.auth.KobilAuthError` (subclass of `kobil.core.KobilError`).
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
MIT
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""kobil.auth — KOBIL OIDC authentication helper."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import secrets
|
|
7
|
+
import urllib.error
|
|
8
|
+
import urllib.parse
|
|
9
|
+
import urllib.request
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
from kobil.core import KobilError
|
|
13
|
+
|
|
14
|
+
__all__ = ["AuthClient", "KobilAuthError"]
|
|
15
|
+
|
|
16
|
+
_NONCE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class KobilAuthError(KobilError):
|
|
20
|
+
"""Thrown when an auth operation (token exchange) fails."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AuthClient:
|
|
24
|
+
"""OIDC helper: build the login redirect URL and exchange the code for tokens."""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
*,
|
|
29
|
+
authorization_endpoint: str,
|
|
30
|
+
token_endpoint: str,
|
|
31
|
+
client_id: str,
|
|
32
|
+
client_secret: str,
|
|
33
|
+
scope: str = "openid",
|
|
34
|
+
response_type: str = "code",
|
|
35
|
+
response_mode: str = "query",
|
|
36
|
+
nonce_length: int = 32,
|
|
37
|
+
) -> None:
|
|
38
|
+
for name, value in (
|
|
39
|
+
("authorization_endpoint", authorization_endpoint),
|
|
40
|
+
("token_endpoint", token_endpoint),
|
|
41
|
+
("client_id", client_id),
|
|
42
|
+
("client_secret", client_secret),
|
|
43
|
+
):
|
|
44
|
+
if not value:
|
|
45
|
+
raise KobilAuthError(f"{name} is required.")
|
|
46
|
+
self._authorization_endpoint = authorization_endpoint
|
|
47
|
+
self._token_endpoint = token_endpoint
|
|
48
|
+
self._client_id = client_id
|
|
49
|
+
self._client_secret = client_secret
|
|
50
|
+
self._scope = scope
|
|
51
|
+
self._response_type = response_type
|
|
52
|
+
self._response_mode = response_mode
|
|
53
|
+
self._nonce_length = nonce_length
|
|
54
|
+
|
|
55
|
+
def build_authorization_url(self, redirect_uri: str) -> str:
|
|
56
|
+
if not redirect_uri:
|
|
57
|
+
raise KobilAuthError("redirect_uri cannot be empty.")
|
|
58
|
+
query = urllib.parse.urlencode(
|
|
59
|
+
{
|
|
60
|
+
"client_id": self._client_id,
|
|
61
|
+
"redirect_uri": redirect_uri,
|
|
62
|
+
"scope": self._scope,
|
|
63
|
+
"response_type": self._response_type,
|
|
64
|
+
"response_mode": self._response_mode,
|
|
65
|
+
"nonce": self._nonce(self._nonce_length),
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
return f"{self._authorization_endpoint}?{query}"
|
|
69
|
+
|
|
70
|
+
def exchange_code_for_token(self, code: str, redirect_uri: str) -> Dict[str, Any]:
|
|
71
|
+
if not code:
|
|
72
|
+
raise KobilAuthError("code cannot be empty.")
|
|
73
|
+
if not redirect_uri:
|
|
74
|
+
raise KobilAuthError("redirect_uri cannot be empty.")
|
|
75
|
+
|
|
76
|
+
data = urllib.parse.urlencode(
|
|
77
|
+
{
|
|
78
|
+
"client_id": self._client_id,
|
|
79
|
+
"client_secret": self._client_secret,
|
|
80
|
+
"code": code,
|
|
81
|
+
"redirect_uri": redirect_uri,
|
|
82
|
+
"grant_type": "authorization_code",
|
|
83
|
+
}
|
|
84
|
+
).encode()
|
|
85
|
+
request = urllib.request.Request(
|
|
86
|
+
self._token_endpoint,
|
|
87
|
+
data=data,
|
|
88
|
+
method="POST",
|
|
89
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
90
|
+
)
|
|
91
|
+
try:
|
|
92
|
+
with urllib.request.urlopen(request) as response:
|
|
93
|
+
body = response.read().decode()
|
|
94
|
+
except urllib.error.HTTPError as exc:
|
|
95
|
+
error_body = exc.read().decode(errors="replace")
|
|
96
|
+
raise KobilAuthError(f"Token endpoint returned {exc.code}.", exc.code, error_body) from exc
|
|
97
|
+
except Exception as exc: # noqa: BLE001
|
|
98
|
+
raise KobilAuthError("An error occurred while exchanging code for token.", cause=exc) from exc
|
|
99
|
+
|
|
100
|
+
return json.loads(body)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def _nonce(length: int) -> str:
|
|
104
|
+
if length <= 0:
|
|
105
|
+
raise KobilAuthError("Nonce length must be greater than zero.")
|
|
106
|
+
return "".join(secrets.choice(_NONCE_CHARS) for _ in range(length))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kobil-auth
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: KOBIL OIDC (OpenID Connect) authentication helper for Python.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/uveysservetoglu/kobil-sdks
|
|
7
|
+
Keywords: kobil,oidc,oauth2,authentication,openid-connect
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: kobil-core>=1.0
|
|
11
|
+
|
|
12
|
+
# kobil-auth
|
|
13
|
+
|
|
14
|
+
KOBIL OIDC (OpenID Connect) authentication helper for Python.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install kobil-auth
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from kobil.auth import AuthClient
|
|
22
|
+
|
|
23
|
+
auth = AuthClient(
|
|
24
|
+
authorization_endpoint="https://idp.../auth",
|
|
25
|
+
token_endpoint="https://idp.../token",
|
|
26
|
+
client_id="...",
|
|
27
|
+
client_secret="...",
|
|
28
|
+
scope="openid profile email",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
url = auth.build_authorization_url("https://app.example.com/callback")
|
|
32
|
+
tokens = auth.exchange_code_for_token(code, "https://app.example.com/callback")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Failures raise `kobil.auth.KobilAuthError` (subclass of `kobil.core.KobilError`).
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kobil-core>=1.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kobil
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kobil-auth"
|
|
7
|
+
version = "3.0.0"
|
|
8
|
+
description = "KOBIL OIDC (OpenID Connect) authentication helper for Python."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
keywords = ["kobil", "oidc", "oauth2", "authentication", "openid-connect"]
|
|
13
|
+
dependencies = ["kobil-core>=1.0"]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Repository = "https://github.com/uveysservetoglu/kobil-sdks"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
include = ["kobil*"]
|
|
20
|
+
namespaces = true
|