csrf-sync
Version:
A utility package to help implement stateful CSRF protection using the Synchroniser Token Pattern in express.
65 lines (64 loc) • 2.02 kB
JavaScript
// src/index.ts
import { randomBytes } from "crypto";
import createHttpError from "http-errors";
var csrfSync = ({
ignoredMethods = ["GET", "HEAD", "OPTIONS"],
getTokenFromRequest = (req) => req.headers["x-csrf-token"],
getTokenFromState = (req) => {
return req.session.csrfToken;
},
storeTokenInState = (req, token) => {
req.session.csrfToken = token;
},
size = 128,
errorConfig: { statusCode = 403, message = "invalid csrf token", code = "EBADCSRFTOKEN" } = {},
skipCsrfProtection
} = {}) => {
const ignoredMethodsSet = new Set(ignoredMethods);
const requiresCsrfProtection = (req) => {
const shouldSkip = typeof skipCsrfProtection === "function" && skipCsrfProtection(req);
return !(ignoredMethodsSet.has(req.method) || typeof shouldSkip === "boolean" && shouldSkip);
};
const invalidCsrfTokenError = createHttpError(statusCode, message, {
code
});
const generateToken = (req, overwrite = false) => {
if (!overwrite && typeof getTokenFromState(req) === "string") {
return getTokenFromState(req);
}
const newToken = randomBytes(size).toString("hex");
storeTokenInState(req, newToken);
return newToken;
};
const revokeToken = (req) => {
storeTokenInState(req);
};
const isRequestValid = (req) => {
const receivedToken = getTokenFromRequest(req);
const storedToken = getTokenFromState(req);
return typeof receivedToken === "string" && typeof storedToken === "string" && receivedToken === storedToken;
};
const csrfSynchronisedProtection = (req, res, next) => {
req.csrfToken = (overwrite) => generateToken(req, overwrite);
if (!requiresCsrfProtection(req)) {
next();
} else if (isRequestValid(req)) {
next();
} else {
next(invalidCsrfTokenError);
}
};
return {
invalidCsrfTokenError,
csrfSynchronisedProtection,
generateToken,
getTokenFromRequest,
getTokenFromState,
isRequestValid,
storeTokenInState,
revokeToken
};
};
export {
csrfSync
};