- Added a new `api` service for the NestJS backend, including health checks and dependencies on PostgreSQL, Redis, and MinIO. - Introduced PostgreSQL and Redis services with health checks and configurations for data persistence. - Added MinIO for S3-compatible object storage and a one-shot service to initialize required buckets. - Updated the Nginx configuration to proxy requests to the new backend API and MinIO storage. - Enhanced the Dockerfile to support the new API environment variables and configurations. - Updated the `package.json` and `package-lock.json` to include new dependencies for QR code generation and other utilities. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
/* eslint-disable unicorn/prevent-abbreviations */
|
|
import { INestApplication } from '@nestjs/common';
|
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
import * as request from 'supertest';
|
|
import { createHash } from 'node:crypto';
|
|
import {
|
|
finalizeEvent,
|
|
generateSecretKey,
|
|
getPublicKey,
|
|
type UnsignedEvent,
|
|
} from 'nostr-tools';
|
|
import { NostrAuthModule } from '../src/nostr-auth/nostr-auth.module';
|
|
|
|
const hashPayload = (payload: string) =>
|
|
createHash('sha256').update(payload).digest('hex');
|
|
|
|
describe('NostrAuth (e2e)', () => {
|
|
let app: INestApplication;
|
|
|
|
const secretKey = generateSecretKey();
|
|
const pubkey = getPublicKey(secretKey);
|
|
const host = 'nostr.test';
|
|
const path = '/nostr-auth/echo';
|
|
const url = `http://${host}${path}`;
|
|
|
|
const buildAuthHeader = (unsignedEvent: UnsignedEvent): string => {
|
|
const event = finalizeEvent(unsignedEvent, secretKey);
|
|
return `Nostr ${Buffer.from(JSON.stringify(event)).toString('base64')}`;
|
|
};
|
|
|
|
beforeAll(async () => {
|
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
imports: [NostrAuthModule],
|
|
}).compile();
|
|
|
|
app = moduleFixture.createNestApplication();
|
|
await app.init();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
it('accepts a valid nostr-signed request', async () => {
|
|
const body = { ping: 'pong' };
|
|
const payload = JSON.stringify(body);
|
|
|
|
const authHeader = buildAuthHeader({
|
|
pubkey,
|
|
kind: 27_235,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags: [
|
|
['u', url],
|
|
['method', 'POST'],
|
|
['payload', hashPayload(payload)],
|
|
],
|
|
content: '',
|
|
});
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(path)
|
|
.set('host', host)
|
|
.set('x-forwarded-proto', 'http')
|
|
.set('authorization', authHeader)
|
|
.send(body)
|
|
.expect(201);
|
|
|
|
expect(response.body.pubkey).toBe(pubkey);
|
|
});
|
|
|
|
it('rejects a tampered payload hash', async () => {
|
|
const body = { ping: 'pong' };
|
|
const payload = JSON.stringify(body);
|
|
|
|
const authHeader = buildAuthHeader({
|
|
pubkey,
|
|
kind: 27_235,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags: [
|
|
['u', url],
|
|
['method', 'POST'],
|
|
['payload', hashPayload(`${payload}tampered`)],
|
|
],
|
|
content: '',
|
|
});
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.post(path)
|
|
.set('host', host)
|
|
.set('x-forwarded-proto', 'http')
|
|
.set('authorization', authHeader)
|
|
.send(body)
|
|
.expect(401);
|
|
|
|
expect(response.body.code).toBe('PAYLOAD_MISMATCH');
|
|
});
|
|
});
|