mirror of
https://github.com/immich-app/immich.git
synced 2025-06-20 17:03:14 +02:00
* chore: add typeorm commands to npm and set default database config values * feat: move to server side authentication tokens * fix: websocket should emit error and disconnect on error thrown by the server * refactor: rename cookie-auth-strategy to user-auth-strategy * feat: user tokens and API keys now use SHA256 hash for performance improvements * test: album e2e test remove unneeded module import * infra: truncate api key table as old keys will no longer work with new hash algorithm * fix(server): e2e tests (#1435) * fix: root module paths * chore: linting * chore: rename user-auth to strategy.ts and make validate return AuthUserDto * fix: we should always send HttpOnly for our auth cookies * chore: remove now unused crypto functions and jwt dependencies * fix: return the extra fields for AuthUserDto in auth service validate --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
import { BadRequestException, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
import { AuthUserDto, ICryptoRepository } from '../auth';
|
|
import { IKeyRepository } from './api-key.repository';
|
|
import { APIKeyCreateDto } from './dto/api-key-create.dto';
|
|
import { APIKeyCreateResponseDto } from './response-dto/api-key-create-response.dto';
|
|
import { APIKeyResponseDto, mapKey } from './response-dto/api-key-response.dto';
|
|
|
|
@Injectable()
|
|
export class APIKeyService {
|
|
constructor(
|
|
@Inject(ICryptoRepository) private crypto: ICryptoRepository,
|
|
@Inject(IKeyRepository) private repository: IKeyRepository,
|
|
) {}
|
|
|
|
async create(authUser: AuthUserDto, dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
|
|
const secret = this.crypto.randomBytes(32).toString('base64').replace(/\W/g, '');
|
|
const entity = await this.repository.create({
|
|
key: this.crypto.hashSha256(secret),
|
|
name: dto.name || 'API Key',
|
|
userId: authUser.id,
|
|
});
|
|
|
|
return { secret, apiKey: mapKey(entity) };
|
|
}
|
|
|
|
async update(authUser: AuthUserDto, id: number, dto: APIKeyCreateDto): Promise<APIKeyResponseDto> {
|
|
const exists = await this.repository.getById(authUser.id, id);
|
|
if (!exists) {
|
|
throw new BadRequestException('API Key not found');
|
|
}
|
|
|
|
return this.repository.update(authUser.id, id, {
|
|
name: dto.name,
|
|
});
|
|
}
|
|
|
|
async delete(authUser: AuthUserDto, id: number): Promise<void> {
|
|
const exists = await this.repository.getById(authUser.id, id);
|
|
if (!exists) {
|
|
throw new BadRequestException('API Key not found');
|
|
}
|
|
|
|
await this.repository.delete(authUser.id, id);
|
|
}
|
|
|
|
async getById(authUser: AuthUserDto, id: number): Promise<APIKeyResponseDto> {
|
|
const key = await this.repository.getById(authUser.id, id);
|
|
if (!key) {
|
|
throw new BadRequestException('API Key not found');
|
|
}
|
|
return mapKey(key);
|
|
}
|
|
|
|
async getAll(authUser: AuthUserDto): Promise<APIKeyResponseDto[]> {
|
|
const keys = await this.repository.getByUserId(authUser.id);
|
|
return keys.map(mapKey);
|
|
}
|
|
|
|
async validate(token: string): Promise<AuthUserDto> {
|
|
const hashedToken = this.crypto.hashSha256(token);
|
|
const keyEntity = await this.repository.getKey(hashedToken);
|
|
if (keyEntity?.user) {
|
|
const user = keyEntity.user;
|
|
|
|
return {
|
|
id: user.id,
|
|
email: user.email,
|
|
isAdmin: user.isAdmin,
|
|
isPublicUser: false,
|
|
isAllowUpload: true,
|
|
};
|
|
}
|
|
|
|
throw new UnauthorizedException('Invalid API Key');
|
|
}
|
|
}
|