sso-nebus 0.1.2__tar.gz → 0.1.4__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sso-nebus
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python клиент для взаимодействия с MS Auth Service API
5
5
  License: LICENSE
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sso-nebus"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Python клиент для взаимодействия с MS Auth Service API"
5
5
  authors = [
6
6
  {name = "Артем Костюченко",email = "kostyuchenko.work@gmail.com"}
@@ -1,12 +1,33 @@
1
1
  """Клиент для микросервисного взаимодействия (Client Credentials)"""
2
2
 
3
- from typing import Optional
3
+ import base64
4
+ import json
5
+ from typing import Any, Dict, Optional
4
6
 
5
7
  from .base import BaseClient
6
8
  from .models import TokenResponse, UserInfo
7
9
  from .exceptions import TokenError
8
10
 
9
11
 
12
+ def _decode_jwt_payload(token: str) -> Dict[str, Any]:
13
+ """
14
+ Декодирует payload JWT без проверки подписи (только чтение полей).
15
+ Токен получен от SSO, используется для извлечения claim service_name.
16
+ """
17
+ try:
18
+ parts = token.split(".")
19
+ if len(parts) != 3:
20
+ return {}
21
+ payload_b64 = parts[1]
22
+ padding = 4 - len(payload_b64) % 4
23
+ if padding != 4:
24
+ payload_b64 += "=" * padding
25
+ payload_bytes = base64.urlsafe_b64decode(payload_b64)
26
+ return json.loads(payload_bytes.decode("utf-8"))
27
+ except Exception:
28
+ return {}
29
+
30
+
10
31
  class ServiceClient(BaseClient):
11
32
  """Клиент для микросервисного взаимодействия с Client Credentials Grant"""
12
33
 
@@ -59,7 +80,7 @@ class ServiceClient(BaseClient):
59
80
  if scope:
60
81
  form_data["scope"] = scope
61
82
 
62
- data = await self.post("auth/token", form_data=form_data)
83
+ data = await self.post("token", form_data=form_data)
63
84
  token_response = TokenResponse(**data)
64
85
 
65
86
  # Сохраняем токен
@@ -83,7 +104,7 @@ class ServiceClient(BaseClient):
83
104
  raise TokenError(
84
105
  "Access token не найден. Вызовите get_access_token() сначала.")
85
106
 
86
- data = await self.get("auth/me", access_token=access_token)
107
+ data = await self.get("me", access_token=access_token)
87
108
  return UserInfo(**data)
88
109
 
89
110
  def set_access_token(self, access_token: str):
@@ -107,6 +128,35 @@ class ServiceClient(BaseClient):
107
128
  """Получить текущий access token"""
108
129
  return self._access_token
109
130
 
131
+ def get_token_payload(self, access_token: Optional[str] = None) -> Dict[str, Any]:
132
+ """
133
+ Получить payload текущего JWT токена (без проверки подписи).
134
+ Удобно для чтения полей, вшитых SSO (например service_name).
135
+
136
+ Args:
137
+ access_token: Токен (если не указан, используется сохранённый)
138
+
139
+ Returns:
140
+ Словарь с полями payload или пустой словарь при ошибке
141
+ """
142
+ token = access_token or self._access_token
143
+ if not token:
144
+ return {}
145
+ return _decode_jwt_payload(token)
146
+
147
+ def get_service_name(self, access_token: Optional[str] = None) -> Optional[str]:
148
+ """
149
+ Получить название сервиса из JWT токена (поле service_name, вшитое SSO).
150
+
151
+ Args:
152
+ access_token: Токен (если не указан, используется сохранённый)
153
+
154
+ Returns:
155
+ Название сервиса или None, если токена нет или поле отсутствует
156
+ """
157
+ payload = self.get_token_payload(access_token)
158
+ return payload.get("service_name")
159
+
110
160
  async def request_with_auth(
111
161
  self,
112
162
  method: str,
File without changes
File without changes
File without changes
File without changes