A NestJS module for Matrix Federation API authentication.
It provides helpers to sign outbound federation requests and verify inbound signatures using Matrix homeserver signing keys.
- Load homeserver signing key (
homeserver.signing.key) from file - Generate public/private key pairs for federation
signRequest→ sign outbound federation requests with Ed25519verifySignature→ validate inbound requests against the origin’s published keysMatrixFederationGuardfor inbound verification
npm install nestjs-matrix-federationThe signing key file must be in the same format as Synapse:
ed25519 <key_id> <base64_seed>
import { Module } from '@nestjs/common';
import { MatrixFederationModule } from 'nestjs-matrix-federation';
@Module({
imports: [
MatrixFederationModule.forRoot({
serverName: 'my.example.com',
signingKeyPath: '/data/homeserver.signing.key',
dns: {
override: {
homeserver: [
{ name: 'my.example.com', value: 'https://override.example.com' }
],
},
},
}),
],
})
export class AppModule {}import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { MatrixFederationService } from 'nestjs-matrix-federation';
@Injectable()
export class ExampleService {
constructor(
private readonly http: HttpService,
private readonly matrix: MatrixFederationService,
) {}
async send() {
const { auth } = this.matrix.signRequest({
method: 'PUT',
uri: '/_matrix/federation/v1/send/12345',
destination: 'remote.example.com',
body: { hello: 'world' },
});
await this.http.put(
'https://remote.example.com/_matrix/federation/v1/send/12345',
{ hello: 'world' },
{ headers: { Authorization: auth } },
).toPromise();
}
}When another homeserver calls your federation API (e.g. /_matrix/federation/v1/send/txn),
you need to verify the Authorization header before processing.
import { Controller, Post, Req, Res } from '@nestjs/common';
import { MatrixFederationService } from 'nestjs-matrix-federation';
import { Request, Response } from 'express';
@Controller('_matrix/federation/v1')
export class FederationController {
constructor(private readonly federation: MatrixFederationService) {}
@Post('send/:txnId')
async handleTxn(@Req() req: Request, @Res() res: Response) {
const authHeader = req.headers['authorization'] as string;
// Example header:
// X-Matrix origin=remote.example.org,key="ed25519:a_Kvlj",sig="AbCdEf..."
const match = authHeader.match(/origin=(.*?),key="(.*?)",sig="(.*?)"/);
if (!match) {
return res.status(401).json({ error: 'Missing or invalid auth header' });
}
const [, origin, keyId, sigBase64] = match;
const ok = await this.federation.verifySignature({
origin,
keyId,
sigBase64,
method: req.method,
uri: req.originalUrl,
destination: 'my.example.com',
body: req.body,
});
if (!ok) {
return res.status(403).json({ error: 'Signature verification failed' });
}
// ✅ Request verified, handle transaction
return res.json({ ok: true });
}
}Use the provided MatrixFederationGuard to protect federation endpoints.
import { Controller, Post, UseGuards, Req } from '@nestjs/common';
import { MatrixFederationGuard } from 'nestjs-matrix-federation';
import { Request } from 'express';
@Controller('_matrix/federation/v1')
export class FederationController {
@UseGuards(MatrixFederationGuard)
@Post('send/:txnId')
async handleTxn(@Req() req: Request) {
// If you reach here the signature is valid
return { ok: true };
}
}Run unit tests:
npm testUnit tests cover:
- Key loader
- Signing requests
- Verifying signatures
- Middleware behavior
MIT