59 lines
1.5 KiB
TypeScript
59 lines
1.5 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import {
|
|
createCipheriv,
|
|
createDecipheriv,
|
|
createHash,
|
|
randomBytes,
|
|
} from 'crypto';
|
|
|
|
@Injectable()
|
|
export class TokenCryptoService {
|
|
private readonly key: Buffer;
|
|
|
|
constructor(configService: ConfigService) {
|
|
const configuredKey = configService.get<string>('APP_ENCRYPTION_KEY');
|
|
if (!configuredKey) {
|
|
throw new Error(
|
|
'Missing required environment variable: APP_ENCRYPTION_KEY',
|
|
);
|
|
}
|
|
this.key = createHash('sha256').update(configuredKey).digest();
|
|
}
|
|
|
|
encrypt(value: string): string {
|
|
const iv = randomBytes(12);
|
|
const cipher = createCipheriv('aes-256-gcm', this.key, iv);
|
|
const encrypted = Buffer.concat([
|
|
cipher.update(value, 'utf8'),
|
|
cipher.final(),
|
|
]);
|
|
const authTag = cipher.getAuthTag();
|
|
|
|
return [
|
|
iv.toString('base64url'),
|
|
authTag.toString('base64url'),
|
|
encrypted.toString('base64url'),
|
|
].join('.');
|
|
}
|
|
|
|
decrypt(value: string): string {
|
|
const [ivValue, authTagValue, encryptedValue] = value.split('.');
|
|
if (!ivValue || !authTagValue || !encryptedValue) {
|
|
throw new Error('Invalid encrypted token format');
|
|
}
|
|
|
|
const decipher = createDecipheriv(
|
|
'aes-256-gcm',
|
|
this.key,
|
|
Buffer.from(ivValue, 'base64url'),
|
|
);
|
|
decipher.setAuthTag(Buffer.from(authTagValue, 'base64url'));
|
|
|
|
return Buffer.concat([
|
|
decipher.update(Buffer.from(encryptedValue, 'base64url')),
|
|
decipher.final(),
|
|
]).toString('utf8');
|
|
}
|
|
}
|