diff --git a/src/backend/src/api/APIError.js b/src/backend/src/api/APIError.js index 0aad9c1f..5a6b1321 100644 --- a/src/backend/src/api/APIError.js +++ b/src/backend/src/api/APIError.js @@ -492,6 +492,10 @@ module.exports = class APIError { status: 400, message: 'Puter apps must have a valid URL.' }, + 'anti-csrf-incorrect': { + status: 400, + message: 'Incorrect or missing anti-CSRF token.', + }, // Chat // TODO: specifying these errors here might be a violation diff --git a/src/backend/src/middleware/anticsrf.js b/src/backend/src/middleware/anticsrf.js new file mode 100644 index 00000000..c988c983 --- /dev/null +++ b/src/backend/src/middleware/anticsrf.js @@ -0,0 +1,20 @@ +const APIError = require("../api/APIError"); + +const anticsrf = options => async (req, res, next) => { + const svc_antiCSRF = req.services.get('anti-csrf'); + if ( ! req.body.anti_csrf ) { + const err = APIError.create('anti-csrf-incorrect'); + err.write(res); + return; + } + const has = svc_antiCSRF.consume_token(req.user.uuid, req.body.anti_csrf); + if ( ! has ) { + const err = APIError.create('anti-csrf-incorrect'); + err.write(res); + return; + } + + next(); +}; + +module.exports = anticsrf; diff --git a/src/gui/src/util/Collector.js b/src/gui/src/util/Collector.js index 91634ebd..44f6926e 100644 --- a/src/gui/src/util/Collector.js +++ b/src/gui/src/util/Collector.js @@ -11,7 +11,8 @@ const CollectorHandle = (key, collector) => ({ // TODO: link this with kv.js for expiration handling export default def(class Collector { - constructor ({ origin, authToken }) { + constructor ({ antiCSRF, origin, authToken }) { + this.antiCSRF = antiCSRF; this.origin = origin; this.authToken = authToken; this.stored = {}; @@ -29,6 +30,9 @@ export default def(class Collector { return await this.fetch({ method: 'get', route }); } async post (route, body) { + if ( this.antiCSRF ) { + body.anti_csrf = await this.antiCSRF.token(); + } return await this.fetch({ method: 'post', route, body }); }