2424logger = logging .getLogger (__name__ )
2525
2626
27- class TelegramSettings (Settings ):
27+ class TelegramSettings (Settings ): # TODO: переписать раздел про ТГ в README.md
2828 TELEGRAM_REDIRECT_URL : str = "https://app.test.profcomff.com/auth"
29- TELEGRAM_BOT_TOKEN : str | None = None
29+ TELEGRAM_BOT_TOKEN : str
3030
3131
3232class TelegramAuth (OauthMeta ):
33+ """Вход в приложение 'Твой ФФ' через Telegram Login Widget."""
3334 prefix = '/telegram'
3435 tags = ['Telegram' ]
3536 settings = TelegramSettings ()
3637
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
38+ class TGAuthResponseSchema (BaseModel ):
39+ id : str
40+ first_name : str
4141 last_name : str | None = None
4242 username : str | None = None
4343 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
44+ auth_date : str
45+ hash : str
46+ scopes : list [Scope ] | None = None # Телеграм не передает это поле. Осталось "исторически".
47+ session_name : str | None = None # Телеграм не передает это поле. Осталось "исторически".
4848
4949 @classmethod
5050 async def _register (
5151 cls ,
52- user_inp : OauthResponseSchema ,
52+ user_inp : TGAuthResponseSchema ,
5353 background_tasks : BackgroundTasks ,
5454 user_session : UserSession = Depends (UnionAuth (auto_error = True , scopes = [], allow_none = True )),
5555 ) -> Session :
56+ """Добавление метода аутентификации через (виджет) Телеграма."""
5657 old_user = None
5758 new_user = {}
58- telegram_user_id = None
59- userinfo = None
6059
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 )
69-
70- user = await cls ._get_user ('user_id' , telegram_user_id , db_session = db .session )
60+ # Проверяем получение корректных данных
61+ userinfo = await cls ._check (user_inp )
62+ tg_user_id = userinfo ['id' ]
7163
64+ user = await cls ._get_user ('user_id' , tg_user_id , db_session = db .session )
7265 if user is not None :
7366 raise AlreadyExists (User , user .id )
67+
7468 if user_session is None :
75- user = await cls ._create_user (db_session = db .session ) if user_session is None else user_session . user
69+ user = await cls ._create_user (db_session = db .session )
7670 else :
7771 user = user_session .user
7872 old_user = {'user_id' : user .id }
7973 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 }
74+ tg_auth = cls .create_auth_method_param ('user_id' , tg_user_id , user .id , db_session = db .session )
75+ new_user [cls .get_name ()] = {"user_id" : tg_auth .value }
8276 userdata = await TelegramAuth ._convert_data_to_userdata_format (userinfo )
8377 background_tasks .add_task (
8478 get_kafka_producer ().produce ,
@@ -93,12 +87,12 @@ async def _register(
9387 db_session = db .session ,
9488 session_name = user_inp .session_name ,
9589 )
96-
90+
9791 @classmethod
98- async def _login (cls , user_inp : OauthResponseSchema , background_tasks : BackgroundTasks ) -> Session :
99- """Вход в пользователя с помощью аккаунта https://lk.msu.ru
92+ async def _login (cls , user_inp : TGAuthResponseSchema , background_tasks : BackgroundTasks ) -> Session :
93+ """Вход в пользователя с помощью аккаунта ТГ.
10094
101- Производит вход, если находит пользователя по уникаотному идендификатору . Если аккаунт не
95+ Производит вход, если находит пользователя по id (из Телеграма) . Если аккаунт не
10296 найден, возвращает ошибка.
10397 """
10498
@@ -128,24 +122,23 @@ async def _login(cls, user_inp: OauthResponseSchema, background_tasks: Backgroun
128122 )
129123
130124 @classmethod
131- async def _redirect_url (cls ):
125+ async def _redirect_url (cls ): # А это вообще нужно, если мы используем виджет с атрибутом redirect_url (а не callback)?
132126 """URL на который происходит редирект после завершения входа на стороне провайдера"""
133127 return OauthMeta .UrlSchema (url = cls .settings .TELEGRAM_REDIRECT_URL )
134-
128+
135129 @classmethod
136- async def _auth_url (cls ):
137- """URL на который происходит редирект из приложения для авторизации на стороне провайдера"""
138-
130+ async def _auth_url (cls ): # А это вообще нужно, если в виджете ТГ уже прописан атрибут src и там скрипт?!
131+ """URL на который происходит редирект из приложения, чтобы авторизоваться на стороне провайдера."""
139132 return OauthMeta .UrlSchema (
140133 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 )} "
141134 )
142-
135+
143136 @classmethod
144137 async def _check (cls , user_inp ):
145- ''' Проверка данных пользователя
138+ """ Проверка данных пользователя.
146139
147140 https://core.telegram.org/widgets/login#checking-authorization
148- '''
141+ """
149142 data_check = {
150143 'id' : user_inp .id ,
151144 'first_name' : user_inp .first_name ,
@@ -167,9 +160,10 @@ async def _check(cls, user_inp):
167160 return data_check
168161 else :
169162 raise OauthAuthFailed ('Invalid user data from Telegram' , 'Неправильные учетные данные' )
170-
163+
171164 @classmethod
172165 async def _convert_data_to_userdata_format (cls , data : dict [str , Any ]) -> UserLogin :
166+ """Конвертация данных в формат для userdata-api."""
173167 first_name , last_name = '' , ''
174168 if 'first_name' in data .keys () and data ['first_name' ] is not None :
175169 first_name = data ['first_name' ]
@@ -185,111 +179,3 @@ async def _convert_data_to_userdata_format(cls, data: dict[str, Any]) -> UserLog
185179 ]
186180 result = {"items" : items , "source" : cls .get_name ()}
187181 return cls .userdata_process_empty_strings (UserLogin .model_validate (result ))
188-
189-
190- class NewTelegramSettings (Settings ):
191- TELEGRAM_REDIRECT_URL : str = "https://app.test.profcomff.com/auth"
192- TELEGRAM_BOT_TOKEN : str | None = None # TODO: добавить сюда токен бота для тестов. В целом, брать его из .env.
193-
194-
195- class NewTelegramAuth (OauthMeta ):
196- """Вход в приложение 'Твой ФФ' через Telegram Login Widget."""
197- prefix = '/telegram'
198- tags = ['Telegram' ]
199- settings = NewTelegramSettings ()
200-
201- class TGAuthResponseSchema (BaseModel ):
202- # id_token: str | None = Field(default=None, help="Telegram JWT token identifier")
203- id : str
204- first_name : str
205- last_name : str | None = None
206- username : str | None = None
207- photo_url : str | None = None
208- auth_date : str
209- hash : str
210- # scopes: list[Scope] | None = None
211- # session_name: str | None = None
212-
213- @classmethod
214- async def _register (
215- cls ,
216- user_inp : TGAuthResponseSchema ,
217- background_tasks : BackgroundTasks ,
218- user_session : UserSession = Depends (UnionAuth (auto_error = True , scopes = [], allow_none = True )),
219- ) -> Session :
220- old_user = None
221- new_user = {}
222-
223- # Проверяем получение корректных данных
224- userinfo = await cls ._check (user_inp )
225- tg_user_id = userinfo ['id' ]
226-
227- # if user_inp.id_token is None:
228- # userinfo = await cls._check(user_inp)
229- # telegram_user_id = user_inp.id
230- # logger.debug(userinfo)
231- # else:
232- # userinfo = jwt.decode(user_inp.id_token, cls.settings.ENCRYPTION_KEY, algorithms=["HS256"])
233- # telegram_user_id = userinfo['id']
234- # logger.debug(userinfo)
235-
236- # Проверяем, что этот тг-аккаунт уже не привязан к существующему пользователю! # TODO: уточнить, что это правда так работает...
237- user = await cls ._get_user ('user_id' , tg_user_id , db_session = db .session )
238- if user is not None :
239- raise AlreadyExists (User , user .id )
240-
241- if user_session is None :
242- user = await cls ._create_user (db_session = db .session ) # if user_session is None else user_session.user
243- else :
244- user = user_session .user
245- old_user = {'user_id' : user .id }
246- new_user ["user_id" ] = user .id
247- tg_auth = cls .create_auth_method_param ('user_id' , tg_user_id , user .id , db_session = db .session )
248- new_user [cls .get_name ()] = {"user_id" : tg_auth .value }
249- userdata = await TelegramAuth ._convert_data_to_userdata_format (userinfo )
250- background_tasks .add_task (
251- get_kafka_producer ().produce ,
252- cls .settings .KAFKA_USER_LOGIN_TOPIC_NAME ,
253- TelegramAuth .generate_kafka_key (user .id ),
254- userdata ,
255- )
256- await AuthPluginMeta .user_updated (new_user , old_user )
257- return await cls ._create_session (
258- user , # TODO: спросить, зачем (в google.py) отправляется новая сессия с user. user обновляется к этому моменту? Даже если меняется, то зачем именно здесь пользователю новая сессия?
259- user_inp .scopes ,
260- db_session = db .session ,
261- session_name = user_inp .session_name ,
262- )
263-
264- @classmethod
265- async def _redirect_url (cls ):
266- """URL на который происходит редирект после завершения входа на стороне провайдера"""
267- return OauthMeta .UrlSchema (url = cls .settings .TELEGRAM_REDIRECT_URL )
268-
269- @classmethod
270- async def _check (cls , user_inp ): # TODO: перепроверить, что написанное реально поддерживает проверку по доке тг!
271- '''Проверка данных пользователя
272-
273- https://core.telegram.org/widgets/login#checking-authorization
274- '''
275- data_check = {
276- 'id' : user_inp .id ,
277- 'first_name' : user_inp .first_name ,
278- 'last_name' : user_inp .last_name ,
279- 'username' : user_inp .username ,
280- 'photo_url' : user_inp .photo_url ,
281- 'auth_date' : user_inp .auth_date ,
282- }
283- check_hash = user_inp .hash
284- data_check_string = ''
285- for k , v in sorted (data_check .items ()):
286- if v is None :
287- continue
288- data_check_string += f'{ unquote (k )} ={ unquote (v )} \n '
289- data_check_string = data_check_string .rstrip ('\n ' )
290- secret_key = hashlib .sha256 (str .encode (cls .settings .TELEGRAM_BOT_TOKEN )).digest ()
291- signing = hmac .new (secret_key , msg = str .encode (data_check_string ), digestmod = hashlib .sha256 ).hexdigest ()
292- if signing == check_hash :
293- return data_check
294- else :
295- raise OauthAuthFailed ('Invalid user data from Telegram' , 'Неправильные учетные данные' )
0 commit comments