Add VKID OAuth integration and constants

- Add VKIDModule
- Define vkIdConstants in constants.ts
- Create OAuthRequestDto for VKID
- Create OAuthResponseDto for VKID
This commit is contained in:
2025-01-26 01:14:46 +04:00
parent bf58c2b66a
commit 1b5917f651
9 changed files with 156 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import { UsersModule } from "./users/users.module";
import { ScheduleModule } from "./schedule/schedule.module"; import { ScheduleModule } from "./schedule/schedule.module";
import { CacheModule } from "@nestjs/cache-manager"; import { CacheModule } from "@nestjs/cache-manager";
import { FirebaseAdminModule } from "./firebase-admin/firebase-admin.module"; import { FirebaseAdminModule } from "./firebase-admin/firebase-admin.module";
import { VKIDModule } from "./vkid/vkid.module";
@Module({ @Module({
imports: [ imports: [
@@ -12,6 +13,7 @@ import { FirebaseAdminModule } from "./firebase-admin/firebase-admin.module";
ScheduleModule, ScheduleModule,
CacheModule.register({ ttl: 5 * 60 * 1000, isGlobal: true }), CacheModule.register({ ttl: 5 * 60 * 1000, isGlobal: true }),
FirebaseAdminModule, FirebaseAdminModule,
VKIDModule,
], ],
providers: [], providers: [],
}) })

View File

@@ -3,6 +3,11 @@ import * as process from "node:process";
configDotenv(); configDotenv();
export const vkIdConstants = {
clientId: +process.env.VKID_CLIENT_ID,
redirectUri: process.env.VKID_REDIRECT_URI,
};
export const jwtConstants = { export const jwtConstants = {
secret: process.env.JWT_SECRET, secret: process.env.JWT_SECRET,
}; };

View File

@@ -0,0 +1,12 @@
import { IsString } from "class-validator";
export default class OAuthRequestDto {
@IsString()
code: string;
@IsString()
codeVerifier: string;
@IsString()
deviceId: string;
}

View File

@@ -0,0 +1,11 @@
import { IsString } from "class-validator";
import {
ClassTransformerCtor,
Ctor,
} from "../../utility/class-trasformer/class-transformer-ctor";
@ClassTransformerCtor()
export default class OAuthResponseDto extends Ctor<OAuthResponseDto> {
@IsString()
accessToken: string;
}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { VKIDController } from './vkid.controller';
describe('VkidController', () => {
let controller: VKIDController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [VKIDController],
}).compile();
controller = module.get<VKIDController>(VKIDController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,43 @@
import {
Body,
Controller,
HttpStatus,
NotAcceptableException,
Post,
} from "@nestjs/common";
import { ApiBody, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { VKIDService } from "./vkid.service";
import OAuthRequestDto from "./dto/oauth.request.dto";
import OAuthResponseDto from "./dto/oauth.response.dto";
import { ResultDto } from "../utility/validation/class-validator.interceptor";
@ApiTags("v1/vkid")
@Controller({ path: "vkid", version: "1" })
export class VKIDController {
constructor(private readonly vkidService: VKIDService) {}
@ApiOperation({
summary: "OAuth аутентификация",
description: "Аутентификация пользователя с использованием OAuth",
})
@ApiBody({ type: OAuthRequestDto, description: "Данные для OAuth запроса" })
@ApiResponse({
status: HttpStatus.OK,
type: OAuthResponseDto,
description: "OAuth аутентификация прошла успешно",
})
@ApiResponse({
status: HttpStatus.NOT_ACCEPTABLE,
description: "Ошибка в процессе OAuth",
})
@ResultDto(OAuthResponseDto)
@Post("oauth")
async oauth(
@Body() oAuthRequestDto: OAuthRequestDto,
): Promise<OAuthResponseDto> {
const result = await this.vkidService.oauth(oAuthRequestDto);
if (!result) throw new NotAcceptableException("OAuth process failed");
return result;
}
}

11
src/vkid/vkid.module.ts Normal file
View File

@@ -0,0 +1,11 @@
import { Module } from "@nestjs/common";
import { VKIDService } from "./vkid.service";
import { VKIDController } from "./vkid.controller";
import { UsersModule } from "../users/users.module";
@Module({
imports: [UsersModule],
providers: [VKIDService],
controllers: [VKIDController],
})
export class VKIDModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { VKIDService } from './vkid.service';
describe('VkidService', () => {
let service: VKIDService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [VKIDService],
}).compile();
service = module.get<VKIDService>(VKIDService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

36
src/vkid/vkid.service.ts Normal file
View File

@@ -0,0 +1,36 @@
import { Injectable } from "@nestjs/common";
import OAuthRequestDto from "./dto/oauth.request.dto";
import OAuthResponseDto from "./dto/oauth.response.dto";
import axios from "axios";
import { vkIdConstants } from "../contants";
import { v4 as uuidv4 } from "uuid";
@Injectable()
export class VKIDService {
async oauth(oauthRequestDto: OAuthRequestDto): Promise<OAuthResponseDto> {
const state = uuidv4();
const response = await axios.post(
"https://id.vk.com/oauth2/auth",
"grant_type=authorization_code&" +
`client_id=${vkIdConstants.clientId}&` +
`state=${state}&` +
`code_verifier=${oauthRequestDto.codeVerifier}&` +
`code=${oauthRequestDto.code}&` +
`device_id=${oauthRequestDto.deviceId}&` +
`redirect_uri=${vkIdConstants.redirectUri}`,
);
const accessToken: string = (response.data as { access_token: string })
.access_token;
if (!accessToken) {
console.error(response.data);
return null;
}
return new OAuthResponseDto({
accessToken: accessToken,
});
}
}