Skip to content

Commit bb92b1a

Browse files
committed
feat: adds support for x-forwarded-host
Adds `trustProxy` option to HMAC_SHA1 and Provider constructor. It overloads the Provider constructor with the third argument being either a nonceStore or an options object (for a nonceStore, a signer and the trustProxy flag): new Provider(key: string, secret: string, options: NonceStore|Options, signer: Algo)
1 parent 4df2936 commit bb92b1a

File tree

3 files changed

+64
-14
lines changed

3 files changed

+64
-14
lines changed

src/hmac-sha1.coffee

+22-6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ _clean_request_body = (body, query) ->
4040

4141
class HMAC_SHA1
4242

43+
constructor: (options) ->
44+
@trustProxy = options and options.trustProxy
45+
4346
toString: () ->
4447
'HMAC_SHA1'
4548

@@ -52,24 +55,37 @@ class HMAC_SHA1
5255

5356
@sign_string sig.join('&'), consumer_secret, token
5457

58+
host: (req) ->
59+
if not @trustProxy
60+
return req.headers.host
61+
62+
req.headers['x-forwarded-host'] or req.headers.host
63+
64+
protocol: (req) ->
65+
xprotocol = req.headers['x-forwarded-proto']
66+
if @trustProxy and xprotocol
67+
return xprotocol
68+
69+
if req.protocol
70+
return req.protocol
71+
72+
if req.connection.encrypted then 'https' else 'http'
73+
5574
build_signature: (req, body, consumer_secret, token) ->
5675
hapiRawReq = req.raw and req.raw.req
5776
if hapiRawReq
5877
req = hapiRawReq
5978

6079
originalUrl = req.originalUrl or req.url
61-
protocol = req.protocol
80+
host = @host req
81+
protocol = @protocol req
6282

6383
# Since canvas includes query parameters in the body we can omit the query string
6484
if body.tool_consumer_info_product_family_code == 'canvas'
6585
originalUrl = url.parse(originalUrl).pathname
6686

67-
if protocol is undefined
68-
encrypted = req.connection.encrypted
69-
protocol = (encrypted and 'https') or 'http'
70-
7187
parsedUrl = url.parse originalUrl, true
72-
hitUrl = protocol + '://' + req.headers.host + parsedUrl.pathname
88+
hitUrl = protocol + '://' + host + parsedUrl.pathname
7389

7490
@build_signature_raw hitUrl, parsedUrl, req.method, body, consumer_secret, token
7591

src/provider.coffee

+10-4
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@ extensions = require './extensions'
66

77

88
class Provider
9-
constructor: (consumer_key, consumer_secret, nonceStore, signature_method=(new HMAC_SHA1()) ) ->
9+
constructor: (consumer_key, consumer_secret, optionsOrNonceStore, signature_method) ->
1010

1111
if typeof consumer_key is 'undefined' or consumer_key is null
1212
throw new errors.ConsumerError 'Must specify consumer_key'
1313

1414
if typeof consumer_secret is 'undefined' or consumer_secret is null
1515
throw new errors.ConsumerError 'Must specify consumer_secret'
1616

17-
if not nonceStore
17+
if optionsOrNonceStore and optionsOrNonceStore.isNonceStore?()
18+
nonceStore = optionsOrNonceStore
19+
options = {
20+
trustProxy: false
21+
}
22+
else
1823
nonceStore = new MemoryNonceStore()
24+
options = optionsOrNonceStore or {}
1925

20-
if not nonceStore.isNonceStore?()
21-
throw new errors.ParameterError 'Fourth argument must be a nonceStore object'
26+
if not signature_method
27+
signature_method = options.signer or new HMAC_SHA1(options)
2228

2329
@consumer_key = consumer_key
2430
@consumer_secret = consumer_secret

test/Signer.coffee

+32-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@ should = require 'should'
22

33
HMAC_SHA1 = require '../lib/hmac-sha1'
44

5-
6-
7-
signer = new HMAC_SHA1
8-
95
describe 'Signer', () ->
106

117
it 'should include query params', (done) ->
8+
signer = new HMAC_SHA1
129
req =
1310
url: '/developers/LTI/test/v1p1/tool.php?foo=123&foo=bar'
1411
method: 'POST'
@@ -36,3 +33,34 @@ describe 'Signer', () ->
3633

3734
done()
3835

36+
it.only 'should support x-forwarded-*', (done) ->
37+
signer = new HMAC_SHA1 trustProxy: true
38+
req =
39+
url: '/developers/LTI/test/v1p1/tool.php?foo=123&foo=bar'
40+
method: 'POST'
41+
connection:
42+
encrypted: true
43+
headers:
44+
host: 'localhost:5000'
45+
'x-forwarded-host': 'www.imsglobal.org'
46+
'x-forwarded-proto': 'http'
47+
body =
48+
resource_link_id: 'rsc1',
49+
oauth_callback: 'about:blank',
50+
lis_outcome_service_url: 'http://www.imsglobal.org/developers/LTI/test/v1p1/common/tool_consumer_outcome.php?b64=MTIzNDU6OjpzZWNyZXQ=',
51+
lis_result_sourcedid: 'feb-123-456-2929::28883',
52+
launch_presentation_return_url: 'http://www.imsglobal.org/developers/LTI/test/v1p1/lms_return.php',
53+
lti_version: 'LTI-1p0',
54+
lti_message_type: 'basic-lti-launch-request',
55+
oauth_version: '1.0',
56+
oauth_nonce: '7ee33f6dc94117e792ff529898ce3953',
57+
oauth_timestamp: '1397708483',
58+
oauth_consumer_key: '12345',
59+
oauth_signature_method: 'HMAC-SHA1',
60+
oauth_signature: 'dHORwwJqwh5hQQAlvaA9csSIOhc='
61+
62+
signature = signer.build_signature req, body, 'secret'
63+
signature.should.equal body.oauth_signature
64+
65+
done()
66+

0 commit comments

Comments
 (0)