kobil-auth 1.2.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.
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: kobil-auth
3
+ Version: 1.2.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: 1.2.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,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ kobil/auth/__init__.py
4
+ kobil_auth.egg-info/PKG-INFO
5
+ kobil_auth.egg-info/SOURCES.txt
6
+ kobil_auth.egg-info/dependency_links.txt
7
+ kobil_auth.egg-info/requires.txt
8
+ kobil_auth.egg-info/top_level.txt
@@ -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 = "1.2.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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+