-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
112 lines (96 loc) · 3.03 KB
/
index.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
const jwt = require('jsonwebtoken')
const supportedTokenLocations = ['header', 'query']
const getToken = ({ req, tokenLocation, tokenName }) => {
if (tokenLocation === 'header') {
return req.get(tokenName)
} else {
return req[tokenLocation][tokenName]
}
}
/**
* Get a particular value out of a JWT lying within a Express.js request object
* @param {Object} params.req Express.js request object
* @param {String} params.tokenLocation property of req where the token is to be found. ex: 'header' or 'query'
* @param {String} params.tokenName name of the token property or header
* @param {String} params.payloadKey name of the property in the JWT payload
* @return value from the JWT
*/
const getParamValue = ({ req, tokenLocation, tokenName, payloadKey }) => {
const token = getToken({ req, tokenLocation, tokenName })
if (!token) {
const err = new Error('Cannot subsitute value for alias: token does not exist')
err.statusCode = 400
throw err
}
const decoded = jwt.decode(token.replace('Bearer ', ''))
if (!decoded) {
const err = new Error('Cannot substitue value for alias: token is not a JWT')
err.statusCode = 400
throw err
}
const { [payloadKey]: value } = decoded
if (!value) {
const err = new Error('Cannot subsitute value for alias: not contained in token')
err.statusCode = 400
throw err
}
return value
}
const required = (name) => {
throw new Error(`Required parameter: ${name}`)
}
const rewriteUrl = ({ req, alias, value }) => {
const re = new RegExp(`(?<=/)${alias}(?=/)?`)
return req.url.replace(re, value)
}
/**
* Allows using route parameter aliases in a request. Aliases are mapped to values
* in the payload of a JWT provided elsewhere in the request
*
* Example:
* ```javascript
* const routeParamAlias = require('route-param-alias')
* const meConverter = routeParamAlias({
* alias: 'me',
* paramName: 'id',
* tokenLocation: 'header',
* tokenName: 'Authorization',
* payloadKey: 'sub'
* })
*
* app.get('/:id', (req, res) => {
* const payload = jwt.decode(req.headers.authorization)
*
* /// Assertions before applying middleware
* assert.equals(req.params.id, 'me')
* assert.not.equals(req.params.id, payload.sub)
*
* meConverter(req, res, () => {
* /// Assertions after applying middleware
* assert.not.equals(req.params.id, 'me')
* assert.equals(req.params.id, payload.sub)
* ...
* })
* })
* ```
*/
module.exports = ({
alias = required('alias'),
paramName = required('paramName'),
tokenLocation = required('tokenLocation'),
tokenName = required('tokenName'),
payloadKey = required('payloadKey')
}) => {
if (!supportedTokenLocations.includes(tokenLocation)) {
throw new Error(`Unsupported tokenLocation: ${tokenLocation}`)
}
const middleware = (req, _, next) => {
if (req.params[paramName] === alias) {
const value = getParamValue({ req, tokenLocation, tokenName, payloadKey })
req.params[paramName] = value
req.url = rewriteUrl({ req, alias, value })
}
next()
}
return middleware
}