From 13e4937925b504ac1dd553abfcf81f70d4fdaa0a Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 18 Feb 2026 06:27:27 +0300 Subject: [PATCH] feat: add telegram verification --- src/modules/auth/auth.controller.ts | 47 ++++++++++++++++++- src/modules/auth/auth.grpc.ts | 9 ++++ src/modules/auth/dto/rq/index.ts | 1 + .../auth/dto/rq/telegram-verify.dto.ts | 7 +++ yarn.lock | 6 +-- 5 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 src/modules/auth/dto/rq/telegram-verify.dto.ts diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 2dbbf92..87d0522 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,11 +1,12 @@ import { Body, Controller, + Get, HttpCode, HttpStatus, Post, Req, - Res + Res, UnauthorizedException } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { ApiOperation } from '@nestjs/swagger' @@ -13,7 +14,11 @@ import type { Request, Response } from 'express' import { lastValueFrom } from 'rxjs' import { AuthClientGrpc } from './auth.grpc' -import { SendOtpRequestDto, VerifyOtpRequestDto } from './dto' +import { + SendOtpRequestDto, + TelegramVerifyDto, + VerifyOtpRequestDto +} from './dto' @Controller('auth') export class AuthController { @@ -94,4 +99,42 @@ export class AuthController { res.clearCookie('refreshToken') return { ok: true } } + + @Get('telegram/init') + @HttpCode(HttpStatus.OK) + public telegramInit() { + return this.client.telegramInit() + } + + @Post('telegram/verify') + @HttpCode(HttpStatus.OK) + public async telegramVerify( + @Body() dto: TelegramVerifyDto, + @Res({ passthrough: true }) res: Response + ) { + const query = JSON.parse(atob(dto.tgAuthResult)) as { + [key: string]: string + } + const result = await lastValueFrom(this.client.telegramVerify({ query })) + + if (result.url) { + return result + } + + if (result.accessToken && result.refreshToken) { + const { accessToken, refreshToken } = result + + res.cookie('refreshToken', refreshToken, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + domain: this.config.getOrThrow('COOKIES_DOMAIN'), + sameSite: 'lax', + maxAge: 30 * 24 * 60 * 60 * 1000 + }) + + return { accessToken } + } + + throw new UnauthorizedException('Invalid telegram login response') + } } diff --git a/src/modules/auth/auth.grpc.ts b/src/modules/auth/auth.grpc.ts index 142ceac..79272fc 100644 --- a/src/modules/auth/auth.grpc.ts +++ b/src/modules/auth/auth.grpc.ts @@ -4,6 +4,7 @@ import { AuthServiceClient, RefreshRequest, SendOtpRequest, + TelegramVerifyRequest, VerifyOtpRequest } from '@teacinema/contracts/gen/auth' @@ -28,4 +29,12 @@ export class AuthClientGrpc implements OnModuleInit { public refresh(request: RefreshRequest) { return this.authService.refresh(request) } + + public telegramInit() { + return this.authService.telegramInit({}) + } + + public telegramVerify(request: TelegramVerifyRequest) { + return this.authService.telegramVerify(request) + } } diff --git a/src/modules/auth/dto/rq/index.ts b/src/modules/auth/dto/rq/index.ts index 7acab38..000008f 100644 --- a/src/modules/auth/dto/rq/index.ts +++ b/src/modules/auth/dto/rq/index.ts @@ -1,2 +1,3 @@ export * from './send-otp.request.dto' export * from './verify-otp.request.dto' +export * from './telegram-verify.dto' diff --git a/src/modules/auth/dto/rq/telegram-verify.dto.ts b/src/modules/auth/dto/rq/telegram-verify.dto.ts new file mode 100644 index 0000000..7786775 --- /dev/null +++ b/src/modules/auth/dto/rq/telegram-verify.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator' + +export class TelegramVerifyDto { + @IsString() + @IsNotEmpty() + tgAuthResult: string +} diff --git a/yarn.lock b/yarn.lock index f1a335b..77c56a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1221,9 +1221,9 @@ integrity sha512-q3DURJbSk3k8MNWFIYaSM4LEcBgPbWa+HJmBz/nzYT4kuYitJVSXxpZ97kr0Ea+81AZwAU3JhQKlkK0SBWUi0A== "@teacinema/contracts@^1.0.0": - version "1.0.7" - resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.0.7/contracts-1.0.7.tgz#2c75a3118e4126d30f5c8193c4518bac3f1812ff" - integrity sha512-lE14PO/yBphYCu0BGFGyGAyvJ74v+ACz34MXA3fFo/PwThPVpudBReSG6oRyUACRybkN882GKXFU6LrJXvGbrQ== + version "1.0.8" + resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.0.8/contracts-1.0.8.tgz#4e31c3a370b0808629d9c93900c5f27958ef82e6" + integrity sha512-xyPyifbVwWelgo4MsvhhRl//To+b0Tnp8DtANCzwLBUhwPj3ZG19QgfPB7Ih7PsBU5TD6F80aNlITDxJbFgkFQ== dependencies: "@nestjs/microservices" "^11.1.12" protoc "33.4.0"