diff --git a/apps/api/src/api-keys/api-keys.module.ts b/apps/api/src/api-keys/api-keys.module.ts index 2dc618d..6ed56cd 100644 --- a/apps/api/src/api-keys/api-keys.module.ts +++ b/apps/api/src/api-keys/api-keys.module.ts @@ -7,5 +7,6 @@ import { ApiKeysService } from './api-keys.service'; controllers: [ApiKeysController], providers: [ApiKeysService], imports: [UsersModule], + exports: [ApiKeysService], }) export class ApiKeysModule {} diff --git a/apps/api/src/api-keys/api-keys.service.spec.ts b/apps/api/src/api-keys/api-keys.service.spec.ts index de32780..8f25a38 100644 --- a/apps/api/src/api-keys/api-keys.service.spec.ts +++ b/apps/api/src/api-keys/api-keys.service.spec.ts @@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Types } from 'mongoose'; import { UserRepository } from '../models/users/users.repository'; import { ApiKeysService } from './api-keys.service'; +import { profile } from 'node:console'; describe('ApiKeysService', () => { let service: ApiKeysService; @@ -76,4 +77,46 @@ describe('ApiKeysService', () => { const [user] = dto.apiKey.split('.'); expect(user).toEqual(userId.toString()); }); + + describe('Validate key', () => { + it('should get user on valid key', async () => { + const userId = new Types.ObjectId(); + mockUserRepository.findById.mockResolvedValue({ + profile: { + username: 'test', + }, + auth: { + password: 'password', + apiKey: `${userId}.current-key`, + }, + }); + + const user = await service.validateKey(`${userId}.current-key`); + expect(user.username).toBe('test'); + }); + + it('should not get user on invalid key', async () => { + const userId = new Types.ObjectId(); + mockUserRepository.findById.mockResolvedValue({ + profile: { + username: 'test', + }, + auth: { + password: 'password', + apiKey: `${userId}.current-key`, + }, + }); + + const user = await service.validateKey(`${userId}.invalid-key`); + expect(user).toBeNull(); + }); + + it('should not get invalid user', async () => { + mockUserRepository.findById.mockResolvedValue(null); + + const user = await service.validateKey(`${new Types.ObjectId()}.key`); + + expect(user).toBeNull(); + }); + }); }); diff --git a/apps/api/src/api-keys/api-keys.service.ts b/apps/api/src/api-keys/api-keys.service.ts index 9bfbaa3..8d8495f 100644 --- a/apps/api/src/api-keys/api-keys.service.ts +++ b/apps/api/src/api-keys/api-keys.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Types } from 'mongoose'; import * as crypto from 'node:crypto'; import { ApiKeyDto } from 'shared-types'; +import { UserProfile } from '../models/users/schemas/user-profile.schema'; import { UserRepository } from '../models/users/users.repository'; @Injectable() @@ -30,6 +31,19 @@ export class ApiKeysService { return { user: userId.toString(), apiKey }; } + async validateKey(input: string): Promise { + const [userIdString] = input.split('.'); + if (!userIdString) return null; + const userId = new Types.ObjectId(userIdString); + + const user = await this.userRepository.findById(userId); + if (!user) return null; + + const apiKey = user.auth.apiKey; + const keyValid = apiKey == input; + return keyValid ? user.profile : null; + } + private generateApiKey(userId: Types.ObjectId): string { const randomHash = crypto.randomBytes(32).toString('hex'); return `${userId}.${randomHash}`;