Skip to content

Commit e48233e

Browse files
committed
ConsumerStrategy and TokenStrategy default host support (useful when the server is accessed by proxy or load balancer)
1 parent b1cbe0a commit e48233e

File tree

3 files changed

+56
-58
lines changed

3 files changed

+56
-58
lines changed

lib/passport-http-oauth/strategies/consumer.js

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ var passport = require('passport')
5555
* `authInfo` will contain the following properties:
5656
*
5757
* scheme always set to `OAuth`
58-
* oauth.callbackURL URL to redirect the user to after authorization
58+
* oauth.callbackURL URL to redirect the user to after authorization
5959
*
6060
* When the consumer is making a request to the access token endpoint,
6161
* `authInfo` will contain the following properties (in addition to any optional
@@ -117,14 +117,13 @@ function ConsumerStrategy(options, consumer, token, validate) {
117117
if (!consumer) throw new Error('HTTP OAuth consumer authentication strategy requires a consumer function');
118118
if (!token) throw new Error('HTTP OAuth consumer authentication strategy requires a token function');
119119

120-
// TODO: Add option to default host if its not in the request header.
121-
122120
passport.Strategy.call(this);
123121
this.name = 'oauth';
124122
this._consumer = consumer;
125123
this._token = token;
126124
this._validate = validate;
127125
this._realm = options.realm || 'Clients';
126+
this._host = options.host || null;
128127
}
129128

130129
/**
@@ -142,13 +141,13 @@ util.inherits(ConsumerStrategy, passport.Strategy);
142141
ConsumerStrategy.prototype.authenticate = function(req) {
143142
var params = undefined
144143
, header = null;
145-
144+
146145
if (req.headers && req.headers['authorization']) {
147146
var parts = req.headers['authorization'].split(' ');
148147
if (parts.length == 2) {
149148
var scheme = parts[0]
150149
, credentials = parts[1];
151-
150+
152151
if (/OAuth/i.test(scheme)) {
153152
params = utils.parseHeader(credentials);
154153
header = params;
@@ -157,28 +156,28 @@ ConsumerStrategy.prototype.authenticate = function(req) {
157156
return this.fail(400);
158157
}
159158
}
160-
159+
161160
if (req.body && req.body['oauth_signature']) {
162161
if (params) { return this.fail(400); }
163162
params = req.body;
164163
}
165-
164+
166165
if (req.query && req.query['oauth_signature']) {
167166
if (params) { return this.fail(400); }
168167
token = req.query['access_token'];
169168
params = req.query;
170169
}
171-
170+
172171
if (!params) { return this.fail(this._challenge()); }
173-
172+
174173
if (!params['oauth_consumer_key'] ||
175174
!params['oauth_signature_method'] ||
176175
!params['oauth_signature'] ||
177176
!params['oauth_timestamp'] ||
178177
!params['oauth_nonce']) {
179178
return this.fail(this._challenge('parameter_absent'), 400);
180179
}
181-
180+
182181
var consumerKey = params['oauth_consumer_key']
183182
, requestToken = params['oauth_token']
184183
, signatureMethod = params['oauth_signature_method']
@@ -188,17 +187,17 @@ ConsumerStrategy.prototype.authenticate = function(req) {
188187
, callback = params['oauth_callback']
189188
, verifier = params['oauth_verifier']
190189
, version = params['oauth_version']
191-
190+
192191
if (version && version !== '1.0') {
193192
return this.fail(this._challenge('version_rejected'), 400);
194193
}
195-
196-
194+
195+
197196
var self = this;
198197
this._consumer(consumerKey, function(err, consumer, consumerSecret) {
199198
if (err) { return self.error(err); }
200199
if (!consumer) { return self.fail(self._challenge('consumer_key_rejected')); }
201-
200+
202201
if (!requestToken) {
203202
// If no `oauth_token` is present, the consumer is attempting to abtain
204203
// a request token. Validate the request using only the consumer key
@@ -215,22 +214,22 @@ ConsumerStrategy.prototype.authenticate = function(req) {
215214
var info = {};
216215
info.scheme = 'OAuth';
217216
info.oauth = { callbackURL: callback }
218-
217+
219218
// WARNING: If the consumer is not using OAuth 1.0a, the
220219
// `oauth_callback` parameter will not be present. Instead, it
221220
// will be supplied when the consumer redirects the user to the
222221
// service provider when obtaining authorization. A service
223222
// provider that unconditionally accepts a URL during this
224223
// phase may be inadvertently assisting in session fixation
225224
// attacks, as described here:
226-
//
225+
//
227226
// http://oauth.net/advisories/2009-1/
228227
// http://hueniverse.com/2009/04/explaining-the-oauth-session-fixation-attack/
229228
//
230229
// Service providers are encouraged to implement monitoring to
231230
// detect potential attacks, and display advisory notices to
232231
// users.
233-
232+
234233
return self.success(consumer, info);
235234
});
236235
} else {
@@ -249,41 +248,41 @@ ConsumerStrategy.prototype.authenticate = function(req) {
249248
self._token(requestToken, function(err, tokenSecret, info) {
250249
if (err) { return self.error(err); }
251250
if (!tokenSecret) { return self.fail(self._challenge('token_rejected')); }
252-
251+
253252
validate(tokenSecret, function() {
254253
info = info || {};
255254
info.scheme = 'OAuth';
256255
info.oauth = { token: requestToken, verifier: verifier }
257-
256+
258257
// WARNING: If the consumer is not using OAuth 1.0a, the
259258
// `oauth_verifier` parameter will not be present. This
260259
// makes it impossible to know if the user who authorized the
261260
// request token is the same user returning to the
262261
// application, as described here:
263262
//
264263
// http://hueniverse.com/2009/04/explaining-the-oauth-session-fixation-attack/
265-
264+
266265
return self.success(consumer, info);
267266
});
268267
});
269268
}
270-
269+
271270
function validate(tokenSecret, ok) {
272-
var url = utils.originalURL(req)
271+
var url = utils.originalURL(self._host, req)
273272
, query = req.query
274273
, body = req.body;
275-
274+
276275
var sources = [ header, query ];
277-
if (req.headers['content-type'] &&
276+
if (req.headers['content-type'] &&
278277
req.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
279278
'application/x-www-form-urlencoded') {
280279
sources.push(body);
281280
}
282-
281+
283282
var normalizedURL = utils.normalizeURI(url)
284283
, normalizedParams = utils.normalizeParams.apply(undefined, sources)
285284
, base = utils.constructBaseString(req.method, normalizedURL, normalizedParams);
286-
285+
287286
if (signatureMethod == 'HMAC-SHA1') {
288287
var key = consumerSecret + '&';
289288
if (tokenSecret) { key += tokenSecret; }
@@ -301,7 +300,7 @@ ConsumerStrategy.prototype.authenticate = function(req) {
301300
} else {
302301
return self.fail(self._challenge('signature_method_rejected'), 400);
303302
}
304-
303+
305304
// If execution reaches this point, the request signature has been
306305
// verified and authentication is successful.
307306
if (self._validate) {
@@ -335,12 +334,12 @@ ConsumerStrategy.prototype._challenge = function(problem, advice) {
335334
if (advice && advice.length) {
336335
challenge += ', oauth_problem_advice="' + utils.encode(advice) + '"';
337336
}
338-
337+
339338
return challenge;
340339
}
341340

342341

343342
/**
344343
* Expose `TokenStrategy`.
345-
*/
344+
*/
346345
module.exports = ConsumerStrategy;

lib/passport-http-oauth/strategies/token.js

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,13 @@ function TokenStrategy(options, consumer, verify, validate) {
109109
if (!consumer) throw new Error('HTTP OAuth token authentication strategy requires a consumer function');
110110
if (!verify) throw new Error('HTTP OAuth token authentication strategy requires a verify function');
111111

112-
// TODO: Add option to default host if its not in the request header.
113-
114112
passport.Strategy.call(this);
115113
this.name = 'oauth';
116114
this._consumer = consumer;
117115
this._verify = verify;
118116
this._validate = validate;
119117
this._realm = options.realm || 'Users';
118+
this._host = options.host || null;
120119
}
121120

122121
/**
@@ -134,13 +133,13 @@ util.inherits(TokenStrategy, passport.Strategy);
134133
TokenStrategy.prototype.authenticate = function(req) {
135134
var params = undefined
136135
, header = null;
137-
136+
138137
if (req.headers && req.headers['authorization']) {
139138
var parts = req.headers['authorization'].split(' ');
140139
if (parts.length == 2) {
141140
var scheme = parts[0]
142141
, credentials = parts[1];
143-
142+
144143
if (/OAuth/i.test(scheme)) {
145144
params = utils.parseHeader(credentials);
146145
header = params;
@@ -149,20 +148,20 @@ TokenStrategy.prototype.authenticate = function(req) {
149148
return this.fail(400);
150149
}
151150
}
152-
151+
153152
if (req.body && req.body['oauth_signature']) {
154153
if (params) { return this.fail(400); }
155154
params = req.body;
156155
}
157-
156+
158157
if (req.query && req.query['oauth_signature']) {
159158
if (params) { return this.fail(400); }
160159
token = req.query['access_token'];
161160
params = req.query;
162161
}
163-
162+
164163
if (!params) { return this.fail(this._challenge()); }
165-
164+
166165
if (!params['oauth_consumer_key'] ||
167166
!params['oauth_token'] ||
168167
!params['oauth_signature_method'] ||
@@ -171,68 +170,68 @@ TokenStrategy.prototype.authenticate = function(req) {
171170
!params['oauth_nonce']) {
172171
return this.fail(this._challenge('parameter_absent'), 400);
173172
}
174-
173+
175174
var consumerKey = params['oauth_consumer_key']
176175
, accessToken = params['oauth_token']
177176
, signatureMethod = params['oauth_signature_method']
178177
, signature = params['oauth_signature']
179178
, timestamp = params['oauth_timestamp']
180179
, nonce = params['oauth_nonce']
181180
, version = params['oauth_version']
182-
181+
183182
if (version && version !== '1.0') {
184183
return this.fail(this._challenge('version_rejected'), 400);
185184
}
186-
185+
187186
var self = this;
188187
this._consumer(consumerKey, function(err, consumer, consumerSecret) {
189188
if (err) { return self.error(err); }
190189
if (!consumer) { return self.fail(self._challenge('consumer_key_rejected')); }
191-
190+
192191
self._verify(accessToken, verified);
193-
192+
194193
function verified(err, user, tokenSecret, info) {
195194
if (err) { return self.error(err); }
196195
if (!user) { return self.fail(self._challenge('token_rejected')); }
197-
196+
198197
info = info || {};
199198
info.scheme = 'OAuth';
200199
info.client =
201200
info.consumer = consumer;
202-
203-
var url = utils.originalURL(req)
201+
202+
var url = utils.originalURL(self._host, req)
204203
, query = req.query
205204
, body = req.body;
206-
205+
207206
var sources = [ header, query ];
208-
if (req.headers['content-type'] &&
207+
if (req.headers['content-type'] &&
209208
req.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
210209
'application/x-www-form-urlencoded') {
211210
sources.push(body);
212211
}
213-
212+
214213
var normalizedURL = utils.normalizeURI(url)
215214
, normalizedParams = utils.normalizeParams.apply(undefined, sources)
216215
, base = utils.constructBaseString(req.method, normalizedURL, normalizedParams);
217-
216+
218217
if (signatureMethod == 'HMAC-SHA1') {
219218
var key = consumerSecret + '&';
220219
if (tokenSecret) { key += tokenSecret; }
221220
var computedSignature = utils.hmacsha1(key, base);
222-
221+
223222
if (signature !== computedSignature) {
224223
return self.fail(self._challenge('signature_invalid'));
225224
}
226225
} else if (signatureMethod == 'PLAINTEXT') {
227226
var computedSignature = utils.plaintext(consumerSecret, tokenSecret);
228-
227+
229228
if (signature !== computedSignature) {
230229
return self.fail(self._challenge('signature_invalid'));
231230
}
232231
} else{
233232
return self.fail(self._challenge('signature_method_rejected'), 400);
234233
}
235-
234+
236235
// If execution reaches this point, the request signature has been
237236
// verified and authentication is successful.
238237
if (self._validate) {
@@ -273,5 +272,5 @@ TokenStrategy.prototype._challenge = function(problem, advice) {
273272

274273
/**
275274
* Expose `TokenStrategy`.
276-
*/
275+
*/
277276
module.exports = TokenStrategy;

lib/passport-http-oauth/strategies/utils.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ var url = require('url')
2020
* @return {String}
2121
* @api private
2222
*/
23-
exports.originalURL = function(req) {
23+
exports.originalURL = function(defaultHost, req) {
2424
var headers = req.headers
2525
, protocol = (req.connection.encrypted || req.headers['x-forwarded-proto'] == 'https')
2626
? 'https'
2727
: 'http'
28-
, host = headers.host
28+
, host = defaultHost || headers.host
2929
, path = req.url || '';
3030
return protocol + '://' + host + path;
3131
};
@@ -43,13 +43,13 @@ exports.originalURL = function(req) {
4343
exports.parseHeader = function(credentials) {
4444
var params = {}
4545
, comps = credentials.match(/(\w+)="([^"]+)"/g);
46-
46+
4747
if (comps) {
4848
for (var i = 0, len = comps.length; i < len; i++) {
4949
var comp = /(\w+)="([^"]+)"/.exec(comps[i])
5050
, name = exports.decode(comp[1])
5151
, val = exports.decode(comp[2]);
52-
52+
5353
// Some clients (I'm looking at you request) erroneously add non-protocol
5454
// params to the `Authorization` header. This check filters those params
5555
// out. It also filters out the `realm` parameter, which is valid to
@@ -159,7 +159,7 @@ exports.normalizeParams = function(header, query, body) {
159159
});
160160
}
161161
mh.del('oauth_signature');
162-
162+
163163
var normalizedParams = [];
164164
mh.keys().sort().forEach(function(key) {
165165
mh.values(key).sort().forEach(function(val) {

0 commit comments

Comments
 (0)