22import hmac
33import logging
44from typing import Any
5- from urllib .parse import quote , unquote
5+ from urllib .parse import unquote
66
77import jwt
88from event_schema .auth import UserLogin
99from fastapi import Depends
1010from fastapi .background import BackgroundTasks
1111from fastapi_sqlalchemy import db
12- from pydantic import BaseModel , Field
12+ from pydantic import BaseModel
1313
1414from auth_backend .auth_method import AuthPluginMeta , OauthMeta , Session
1515from auth_backend .exceptions import AlreadyExists , OauthAuthFailed
2626
2727class TelegramSettings (Settings ):
2828 TELEGRAM_REDIRECT_URL : str = "https://app.test.profcomff.com/auth"
29- TELEGRAM_BOT_TOKEN : str | None = None
29+ TELEGRAM_BOT_TOKEN : str | None = None # Сделал так для тестов, однако токен нужен для работы авторизации!
3030
3131
3232class TelegramAuth (OauthMeta ):
33+ """Вход в приложение 'Твой ФФ' через Telegram Login Widget."""
34+
3335 prefix = '/telegram'
3436 tags = ['Telegram' ]
3537 settings = TelegramSettings ()
3638
37- class OauthResponseSchema (BaseModel ):
38- id_token : str | None = Field (default = None , help = "Telegram JWT token identifier" )
39- id : str | None = None
40- first_name : str | None = None
39+ class TGAuthResponseSchema (BaseModel ):
40+ id : str
41+ first_name : str
4142 last_name : str | None = None
4243 username : str | None = None
4344 photo_url : str | None = None
44- auth_date : str | None = None
45- hash : str | None = None
46- scopes : list [Scope ] | None = None
47- session_name : str | None = None
45+ auth_date : str
46+ hash : str
47+ scopes : list [Scope ] | None = None # Телеграм не передает это поле. Осталось "исторически".
48+ session_name : str | None = None # Телеграм не передает это поле. Осталось "исторически".
4849
4950 @classmethod
5051 async def _register (
5152 cls ,
52- user_inp : OauthResponseSchema ,
53+ user_inp : TGAuthResponseSchema ,
5354 background_tasks : BackgroundTasks ,
5455 user_session : UserSession = Depends (UnionAuth (auto_error = True , scopes = [], allow_none = True )),
5556 ) -> Session :
57+ """Добавление метода аутентификации через (виджет) Телеграма."""
5658 old_user = None
5759 new_user = {}
58- telegram_user_id = None
59- userinfo = None
60-
61- if user_inp .id_token is None :
62- userinfo = await cls ._check (user_inp )
63- telegram_user_id = user_inp .id
64- logger .debug (userinfo )
65- else :
66- userinfo = jwt .decode (user_inp .id_token , cls .settings .ENCRYPTION_KEY , algorithms = ["HS256" ])
67- telegram_user_id = userinfo ['id' ]
68- logger .debug (userinfo )
6960
70- user = await cls ._get_user ('user_id' , telegram_user_id , db_session = db .session )
61+ # Проверяем получение корректных данных
62+ userinfo = await cls ._check (user_inp )
63+ tg_user_id = userinfo ['id' ]
7164
65+ user = await cls ._get_user ('user_id' , tg_user_id , db_session = db .session )
7266 if user is not None :
7367 raise AlreadyExists (User , user .id )
68+
7469 if user_session is None :
75- user = await cls ._create_user (db_session = db .session ) if user_session is None else user_session . user
70+ user = await cls ._create_user (db_session = db .session )
7671 else :
7772 user = user_session .user
7873 old_user = {'user_id' : user .id }
7974 new_user ["user_id" ] = user .id
80- tg_id = cls .create_auth_method_param ('user_id' , telegram_user_id , user .id , db_session = db .session )
81- new_user [cls .get_name ()] = {"user_id" : tg_id .value }
75+ tg_auth = cls .create_auth_method_param ('user_id' , tg_user_id , user .id , db_session = db .session )
76+ new_user [cls .get_name ()] = {"user_id" : tg_auth .value }
8277 userdata = await TelegramAuth ._convert_data_to_userdata_format (userinfo )
8378 background_tasks .add_task (
8479 get_kafka_producer ().produce ,
@@ -95,10 +90,10 @@ async def _register(
9590 )
9691
9792 @classmethod
98- async def _login (cls , user_inp : OauthResponseSchema , background_tasks : BackgroundTasks ) -> Session :
99- """Вход в пользователя с помощью аккаунта https://lk.msu.ru
93+ async def _login (cls , user_inp : TGAuthResponseSchema , background_tasks : BackgroundTasks ) -> Session :
94+ """Вход в пользователя с помощью аккаунта ТГ.
10095
101- Производит вход, если находит пользователя по уникаотному идендификатору . Если аккаунт не
96+ Производит вход, если находит пользователя по id (из Телеграма) . Если аккаунт не
10297 найден, возвращает ошибка.
10398 """
10499
@@ -129,23 +124,26 @@ async def _login(cls, user_inp: OauthResponseSchema, background_tasks: Backgroun
129124
130125 @classmethod
131126 async def _redirect_url (cls ):
132- """URL на который происходит редирект после завершения входа на стороне провайдера"""
127+ """URL на который происходит редирект после завершения входа на стороне провайдера.
128+
129+ В данном случае не предполагается к использованию, т.к. данный URL вшит в виджет.
130+ """
133131 return OauthMeta .UrlSchema (url = cls .settings .TELEGRAM_REDIRECT_URL )
134132
135133 @classmethod
136134 async def _auth_url (cls ):
137- """URL на который происходит редирект из приложения для авторизации на стороне провайдера"""
135+ """URL на который происходит редирект из приложения, чтобы авторизоваться на стороне провайдера.
138136
139- return OauthMeta . UrlSchema (
140- url = f"https://oauth.telegram.org/auth?bot_id= { cls . settings . TELEGRAM_BOT_TOKEN . split ( ':' )[ 0 ] } &origin= { quote ( cls . settings . TELEGRAM_REDIRECT_URL ) } &return_to= { quote ( cls . settings . TELEGRAM_REDIRECT_URL ) } "
141- )
137+ В данном случае не предполагается, т.к. URL вшит в виджет. Отдается атрибут src виджета.
138+ "" "
139+ return OauthMeta . UrlSchema ( url = 'https://telegram.org/js/telegram-widget.js?22' )
142140
143141 @classmethod
144142 async def _check (cls , user_inp ):
145- ''' Проверка данных пользователя
143+ """ Проверка данных пользователя.
146144
147145 https://core.telegram.org/widgets/login#checking-authorization
148- '''
146+ """
149147 data_check = {
150148 'id' : user_inp .id ,
151149 'first_name' : user_inp .first_name ,
@@ -170,6 +168,7 @@ async def _check(cls, user_inp):
170168
171169 @classmethod
172170 async def _convert_data_to_userdata_format (cls , data : dict [str , Any ]) -> UserLogin :
171+ """Конвертация данных в формат для userdata-api."""
173172 first_name , last_name = '' , ''
174173 if 'first_name' in data .keys () and data ['first_name' ] is not None :
175174 first_name = data ['first_name' ]
0 commit comments