diff --git a/client/components/csrf.jsx b/client/components/csrf.jsx new file mode 100644 index 000000000..e13a9bd67 --- /dev/null +++ b/client/components/csrf.jsx @@ -0,0 +1,84 @@ +import React from "react"; + +export class CSRF extends React.Component { + + constructor() { + super(); + this.state = { + testResult: "" + }; + this.testValid = this.testValid.bind(this); + this.testInvalid = this.testInvalid.bind(this); + } + + doPost(csrfToken, shouldFail) { + fetch('/2', { + credentials: 'same-origin', + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'x-csrf-jwt': csrfToken + }, + body: JSON.stringify({message: "hello"}) + }) + .then((resp) => { + if (resp.status == "200") { + this.setState({testResult: "POST SUCCEEDED with status " + resp.status}); + } else { + this.setState({testResult: "POST FAILED with status " + resp.status}); + } + + }) + .catch((e) => { + this.setState({testResult: "POST FAILED:" + e.toString()}); + }); + } + + testValid() { + this.setState({testResult: "valid"}); + fetch("/1", {credentials: 'same-origin'}) // eslint-disable-line + .then((resp) => { + if (resp.status == "200") { + const token = resp.headers.get('x-csrf-jwt'); + if (token != '') { + console.log("Got CSRF token OK"); + this.doPost(token, false); + } else { + this.setState({testResult: "Unable to get token from GET request"}); + } + } else { + this.setState({testResult: "GET request returned " + resp.status}); + } + }) + .catch((e) => { + this.setState({testResult: e.toString()}); + }); + } + + testInvalid() { + this.doPost("fake", true); + } + + render() { + const text = this.state.testResult; + return ( +
+

Electrode CSRF-JWT Demo

+

This component demonstrates usage of the electrode-csrf-jwt module. Two endpoints are declared in server/plugins/demo.js:

+ +

Two simple tests via AJAX (JavaScript must be enabled) are demonstrated below:

+ +
+ {text} +
+
+ ); + } +} diff --git a/client/components/home.jsx b/client/components/home.jsx index 0f56fddbe..c5a2a1ab0 100644 --- a/client/components/home.jsx +++ b/client/components/home.jsx @@ -3,7 +3,13 @@ import React from "react"; export class Home extends React.Component { render() { return ( -

Hello Electrode

+
+

Hello Electrode

+

Demonstration Components

+ +
); } } diff --git a/client/routes.jsx b/client/routes.jsx index 568897a45..980316b27 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -1,7 +1,11 @@ import React from "react"; import { Route, IndexRoute} from "react-router"; import { Home } from "./components/home"; +import { CSRF } from "./components/csrf"; export const routes = ( - + + + + ); diff --git a/config/default.json b/config/default.json index 17ca74280..3aa2f4845 100644 --- a/config/default.json +++ b/config/default.json @@ -1,5 +1,12 @@ { "plugins": { + "electrode-csrf-jwt": { + "options": { + "secret": "shhhhhh", + "expiresIn": 60 + } + }, + "./server/plugins/csrf": {}, "webapp": { "module": "./server/plugins/webapp", "options": { diff --git a/package.json b/package.json index 2e8a8a41f..b1595f80d 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "bluebird": "^2.10.2", + "electrode-csrf-jwt": "^3.0.1", "electrode-router-resolver-engine": "^1.0.0", "electrode-server": "^1.0.0", "electrode-static-paths": "^1.0.0", diff --git a/server/plugins/csrf.js b/server/plugins/csrf.js new file mode 100644 index 000000000..a3a60cd51 --- /dev/null +++ b/server/plugins/csrf.js @@ -0,0 +1,34 @@ +"use strict"; +/*eslint-env es6*/ +var plugin = {}; + +/* + Sample endpoints to demonstrate CSRF protection via the electrode-csrf-jwt module. + Note the endpoints require no special configuration for protection to be enabled. +*/ +plugin.register = function (server, options, next) { + /* a demo GET endpoint which will return a CSRF cookie + header */ + server.route({ + method: "GET", + path: "/1", + handler: function (req, reply) { + reply("valid"); + } + }); + /* a demo POST endpoint which will require a CSRF cookie + header */ + server.route({ + method: "POST", + path: "/2", + handler: function (req, reply) { + reply("valid"); + } + }); + next(); +}; + +plugin.register.attributes = { + name: "CSRFPlugin", + version: "0.0.1" +}; + +module.exports = plugin;