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
:
+
+ - a GET endpoint,
/1
, to which the module automatically adds a csrf token header
+ - a POST endpoint,
/2
, to which the module automatically ensures the presence of a valid token in the request headers
+
+
Two simple tests via AJAX (JavaScript must be enabled) are demonstrated below:
+
+ - Test Valid POST using a token retrieved from
/1
first (should succeed with a 200 status)
+ - Test Invalid POST using a forged token (should fail with a 400 status)
+
+
+ {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 (
-
+
+
+
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;