@@ -185,3 +185,111 @@ async def _convert_data_to_userdata_format(cls, data: dict[str, Any]) -> UserLog
185185 ]
186186 result = {"items" : items , "source" : cls .get_name ()}
187187 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