-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathtoken.js
95 lines (83 loc) · 3.1 KB
/
token.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
var mach = require('../index');
var makeToken = require('../utils/makeToken');
mach.extend(
require('../extensions/server')
);
/**
* The set of HTTP request methods that are considered safe because they
* do not alter server data.
*/
var SAFE_METHODS = {
GET: true,
HEAD: true,
OPTIONS: true,
TRACE: true
};
/**
* A middleware that helps to prevent Cross-site Request Forgery attacks by
* requiring the client to include an authentication token in all form
* submissions that matches a value stored in the session cookie. See
* http://www.codinghorror.com/blog/2008/10/preventing-csrf-and-xsrf-attacks.html
*
* If the session does not already have an authentication token one is
* automatically generated and stored in the session. The default session key
* is "_token". All form submissions need to include this value in the "_token"
* parameter, like this:
*
* <form method="POST" action="/">
* <input type="hidden" name="_token" value="{{session._token}}">
* </form>
*
* On the backend, you need to put both mach.session and mach.params in front of
* mach.token in order for it to be able to retrieve values from the request session
* and parameters, like this:
*
* app.use(mach.session);
* app.use(mach.params);
* app.use(mach.token);
* app.run(function (conn) {
* // The connection authenticated successfully
* });
*
* Options may be any of the following:
*
* - paramName The name of the request parameter that contains the token
* (i.e. the value of the "name" attribute on your <input>).
* Defaults to "_token"
* - sessionKey The name of the session variable to use to store the token.
* Defaults to "_token"
* - byteLength The length of the token in bytes. Defaults to 32
*
* Note: Non-POST requests are always forwarded to the downstream app regardless of
* whether or not they contain the token since it is assumed they are not modifying
* anything and are safe.
*/
function verifyToken(app, options) {
options = options || {};
if (typeof options === 'string')
options = { paramName: options };
var paramName = options.paramName || '_token';
var sessionKey = options.sessionKey || '_token';
var byteLength = options.byteLength || 32;
return function (conn) {
var session = conn.session, params = conn.params;
if (!session) {
conn.onError(new Error('No session! Use mach.session in front of mach.token'));
} else if (!params) {
conn.onError(new Error('No params! Use mach.params in front of mach.token'));
} else {
var token = session[sessionKey];
// Create a new session token if needed.
if (!token)
token = session[sessionKey] = makeToken(byteLength);
if (params[paramName] && params[paramName] === token)
return conn.call(app);
}
// If the request is not a POST we assume it's not a form submission
// and therefore not modifying anything. Pass it downstream.
if (SAFE_METHODS[conn.method] === true)
return conn.call(app);
conn.text(403, 'Forbidden');
};
}
module.exports = verifyToken;