-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathparser.js
117 lines (104 loc) · 4.15 KB
/
parser.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
* @author Axel SHAÏTA <[email protected]>
* MIT Licensed
*/
const {
BadAuthenticationSchemeError,
BadHeaderFormatError,
ExpiredRequestError,
MissingRequiredHeadersError,
MissingRequiredSignatureParamsError,
UnsupportedAlgorithmError
} = require('./errors');
module.exports = {
/**
* @function
* @public
* @description Parse the request and extract the signature parameters
* @param {Object} req The request
* @param {Object} options An object with options.
* @param {Array} options.algorithms A list of available algorithms
* @param {Number|null} [options.requestLifetime=300] The lifetime of a request in second (set to null to disable it)
* @return {Object} Signature parameters
* @throws {MissingRequiredHeadersError}
* @throws {MissingRequiredSignatureParamsError}
* @throws {BadAuthenticationSchemeError}
*/
parseRequest(req, options) {
if (!req.headers || !req.headers.authorization) {
throw new MissingRequiredHeadersError('authorization');
}
const { algorithms, requestLifetime = 300, requiredHeaders=['date'] } = options;
/* Check the authorization scheme */
let { authorization } = req.headers;
const scheme = 'signature';
const prefix = authorization.substring(0, scheme.length).toLowerCase();
if (prefix !== scheme) {
throw new BadAuthenticationSchemeError();
}
/* Get the signature parameters */
authorization = authorization.substring(scheme.length).trim();
const parts = authorization.split(',');
const signatureParams = {};
for (const part of parts) {
const index = part.indexOf('="');
const key = part.substring(0, index).toLowerCase();
const value = part.substring(index + 2, part.length - 1);
signatureParams[key] = value;
}
/* Check if the signature param exists */
const requiredParams = ['keyid', 'algorithm', 'signature'];
const missingSignatureParams = [];
for (const param of requiredParams) {
if (!signatureParams[param.toLowerCase()]) {
missingSignatureParams.push(param);
}
}
if (missingSignatureParams.length > 0) {
throw new MissingRequiredSignatureParamsError(...missingSignatureParams);
}
/* If "headers" param not exists use the date HTTP header by default */
signatureParams.headers = signatureParams.headers ? signatureParams.headers.toLowerCase().split(' ') : ['date'];
/* Check if "required headers" param not exists use the date HTTP header by default */
requiredHeaders.forEach(function (requiredHeader){
if(signatureParams.headers.indexOf(requiredHeader) === -1){
throw new MissingRequiredSignatureParamsError(...requiredHeader);
}
});
/* Check algoritm */
if (algorithms.indexOf(signatureParams.algorithm) === -1) {
throw new UnsupportedAlgorithmError(...algorithms);
}
/* Check if the request if expired */
if (signatureParams.headers.indexOf('date') !== -1 && req.headers.date && requestLifetime) {
/* Check if the request is not expired */
const currentDate = new Date().getTime();
const requestDate = Date.parse(req.headers.date);
if (Number.isNaN(requestDate)) {
throw new BadHeaderFormatError('date', '<day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT');
}
if (Math.abs(currentDate - requestDate) >= requestLifetime * 1000) {
throw new ExpiredRequestError();
}
}
/* Create the signature string */
const missingRequiredHeaders = [];
signatureParams.signingString = '';
signatureParams.headers.forEach((header, index, arr) => {
if (header === '(request-target)') {
signatureParams.signingString += `(request-target): ${req.method.toLowerCase()} ${req.path}`;
} else if (req.headers[header]) {
signatureParams.signingString += `${header}: ${req.headers[header]}`;
} else {
missingRequiredHeaders.push(header);
}
if (index < arr.length - 1) {
signatureParams.signingString += '\n';
}
});
if (missingRequiredHeaders.length > 0) {
throw new MissingRequiredHeadersError(...missingRequiredHeaders);
}
return signatureParams;
}
};