swaf/src/components/CsrfProtectionComponent.ts

55 lines
1.8 KiB
TypeScript

import ApplicationComponent from "../ApplicationComponent";
import {Express, Router} from "express";
import crypto from "crypto";
import {BadRequestError} from "../HttpError";
export default class CsrfProtectionComponent extends ApplicationComponent<void> {
public async start(app: Express, router: Router): Promise<void> {
router.use((req, res, next) => {
if (!req.session) {
throw new Error('Session is unavailable.');
}
res.locals.getCSRFToken = () => {
if (typeof req.session!.csrf !== 'string') {
req.session!.csrf = crypto.randomBytes(64).toString('base64');
}
return req.session!.csrf;
};
if (!['GET', 'HEAD', 'OPTIONS'].find(s => s === req.method)) {
if (req.session.csrf === undefined) {
throw new InvalidCsrfTokenError(req.baseUrl, `You weren't assigned any CSRF token.`);
} else if (req.body.csrf === undefined) {
throw new InvalidCsrfTokenError(req.baseUrl, `You didn't provide any CSRF token.`);
} else if (req.session.csrf !== req.body.csrf) {
throw new InvalidCsrfTokenError(req.baseUrl, `Tokens don't match.`);
}
}
next();
});
}
public async stop(): Promise<void> {
}
}
class InvalidCsrfTokenError extends BadRequestError {
constructor(url: string, details: string, cause?: Error) {
super(
`Invalid CSRF token`,
`${details} We can't process this request. Please try again.`,
url,
cause
);
}
get name(): string {
return 'Invalid CSRF Token';
}
get errorCode(): number {
return 401;
}
}