This commit is contained in:
4
src/clients/esia/__init__.py
Normal file
4
src/clients/esia/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from httpx import AsyncClient
|
||||
|
||||
|
||||
class TMKClient(AsyncClient): ...
|
||||
71
src/clients/esia/api.py
Normal file
71
src/clients/esia/api.py
Normal file
@ -0,0 +1,71 @@
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
from logging import getLogger
|
||||
from typing import Any
|
||||
|
||||
import jwt
|
||||
from fastapi import status as st
|
||||
from httpx import AsyncClient
|
||||
|
||||
from apps.esia.scopes import SCOPES
|
||||
from apps.esia.sign import sign_params
|
||||
from core.config import settings
|
||||
from shared import exceptions as e
|
||||
|
||||
from . import schema as s
|
||||
|
||||
|
||||
class ESIA_API(AsyncClient):
|
||||
def __init__(self):
|
||||
self.logger = getLogger(__name__)
|
||||
super().__init__(base_url=settings.ESIA_BASE_URL)
|
||||
|
||||
async def sign_request(self, data: dict[str, Any]):
|
||||
timestamp = datetime.now(UTC).strftime('%Y.%m.%d %H:%M:%S %z').strip()
|
||||
state = str(uuid.uuid4())
|
||||
params = {
|
||||
'client_id': settings.ESIA_CLIENT_ID,
|
||||
'timestamp': timestamp,
|
||||
'state': state,
|
||||
'scope': ' '.join(SCOPES),
|
||||
}
|
||||
params.update(data)
|
||||
params['client_secret'] = sign_params(params)
|
||||
|
||||
return params
|
||||
|
||||
async def access_token(self, code: str):
|
||||
params = {
|
||||
'grant_type': 'authorization_code',
|
||||
'redirect_uri': settings.ESIA_REDIRECT_URI,
|
||||
'code': code,
|
||||
}
|
||||
signed_params = await self.sign_request(params)
|
||||
res = await self.post('/aas/oauth2/te', data=signed_params)
|
||||
|
||||
match res.status_code:
|
||||
case st.HTTP_200_OK:
|
||||
return s.AccessTokenModel.model_validate(res.json())
|
||||
case st.HTTP_400_BAD_REQUEST:
|
||||
return None
|
||||
|
||||
case _:
|
||||
self.logger.error(res.json())
|
||||
raise e.UnknownException
|
||||
|
||||
async def get_user_info(self, access_token: str, id_token: str):
|
||||
IDToken = s.IDTokenModel.model_validate(
|
||||
jwt.decode(id_token, options={'verify_signature': False})
|
||||
)
|
||||
res = await self.get(
|
||||
f'/rs/prns/{IDToken.urn_esia_sbj.oid}',
|
||||
headers={'Authorization': f'Bearer {access_token}'},
|
||||
)
|
||||
|
||||
match res.status_code:
|
||||
case st.HTTP_200_OK:
|
||||
return s.UserInfoModel.model_validate(res.json())
|
||||
|
||||
case _:
|
||||
self.logger.error(res.json())
|
||||
raise e.UnknownException
|
||||
60
src/clients/esia/schema.py
Normal file
60
src/clients/esia/schema.py
Normal file
@ -0,0 +1,60 @@
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field, PositiveInt
|
||||
|
||||
|
||||
class AccessTokenModel(BaseModel):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
state: str
|
||||
id_token: str
|
||||
token_type: Literal['Bearer']
|
||||
expires_in: PositiveInt
|
||||
|
||||
|
||||
class IDTokenACRModel(BaseModel):
|
||||
twoAF: str = Field(alias='2fa')
|
||||
|
||||
|
||||
class IDTokenSBJModel(BaseModel):
|
||||
lvl: str = Field(alias='urn:esia:sbj:lvl')
|
||||
typ: str = Field(alias='urn:esia:sbj:typ')
|
||||
is_tru: bool = Field(alias='urn:esia:sbj:is_tru')
|
||||
oid: int = Field(alias='urn:esia:sbj:oid')
|
||||
name: str = Field(alias='urn:esia:sbj:nam')
|
||||
|
||||
|
||||
class IDTokenModel(BaseModel):
|
||||
aud: str
|
||||
sub: int
|
||||
nbf: int
|
||||
amr: str
|
||||
auth_time: int
|
||||
exp: int
|
||||
iat: int
|
||||
iss: str
|
||||
# acr: IDTokenACRModel
|
||||
urn_esia_amd: str = Field(alias='urn:esia:amd')
|
||||
urn_esia_sid: str = Field(alias='urn:esia:sid')
|
||||
urn_esia_sbj: IDTokenSBJModel = Field(alias='urn:esia:sbj')
|
||||
|
||||
|
||||
class UserInfoModel(BaseModel):
|
||||
stateFacts: list[str]
|
||||
firstName: str
|
||||
lastName: str
|
||||
# middleName: str
|
||||
# birthDate: str
|
||||
# gender: str
|
||||
trusted: bool
|
||||
# citizenship: str
|
||||
snils: str
|
||||
inn: int
|
||||
updatedOn: int
|
||||
rfgUOperatorCheck: bool
|
||||
status: str
|
||||
verifying: bool
|
||||
rIdDoc: int
|
||||
containsUpCfmCode: bool
|
||||
kidAccCreatedByParent: bool
|
||||
eTag: str
|
||||
Reference in New Issue
Block a user