Skip to content

Commit

Permalink
Merge pull request request#861 from phedny/master
Browse files Browse the repository at this point in the history
Add support for RFC 6750 Bearer Tokens
  • Loading branch information
nylen committed Apr 11, 2014
2 parents c372cac + 7fefc99 commit 4a2742e
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 2 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ request.get('http://some.server.com/', {
'sendImmediately': false
}
});
// or
request.get('http://some.server.com/').auth(null, null, true, 'bearerToken');
// or
request.get('http://some.server.com/', {
'auth': {
'bearer': 'bearerToken'
}
});
```
If passed as an option, `auth` should be a hash containing values `user` || `username`, `pass` || `password`, and `sendImmediately` (optional). The method form takes parameters `auth(username, password, sendImmediately)`.
Expand All @@ -144,6 +152,8 @@ If passed as an option, `auth` should be a hash containing values `user` || `use
Digest authentication is supported, but it only works with `sendImmediately` set to `false`; otherwise `request` will send basic authentication on the initial request, which will probably cause the request to fail.
Bearer authentication is supported, and is activated when the `bearer` value is available. The value may be either a `String` or a `Function` returning a `String`. Using a function to supply the bearer token is particularly useful if used in conjuction with `defaults` to allow a single function to supply the last known token at the time or sending a request or to compute one on the fly.
## OAuth Signing
```javascript
Expand Down
22 changes: 20 additions & 2 deletions request.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ Request.prototype.init = function (options) {
self.auth(
options.auth.user,
options.auth.pass,
options.auth.sendImmediately
options.auth.sendImmediately,
options.auth.bearer
)
}

Expand Down Expand Up @@ -788,6 +789,11 @@ Request.prototype.onResponse = function (response) {
redirectTo = self.uri
break

case 'bearer':
self.auth(null, null, true, self._bearer)
redirectTo = self.uri
break

case 'digest':
// TODO: More complete implementation of RFC 2617.
// - check challenge.algorithm
Expand Down Expand Up @@ -1150,7 +1156,19 @@ Request.prototype.getHeader = function (name, headers) {
}
var getHeader = Request.prototype.getHeader

Request.prototype.auth = function (user, pass, sendImmediately) {
Request.prototype.auth = function (user, pass, sendImmediately, bearer) {
if (bearer !== undefined) {
this._bearer = bearer
this._hasAuth = true
if (sendImmediately || typeof sendImmediately == 'undefined') {
if (typeof bearer === 'function') {
bearer = bearer()
}
this.setHeader('authorization', 'Bearer ' + bearer)
this._sentAuth = true
}
return this
}
if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) {
throw new Error('auth() received invalid user or password')
}
Expand Down
161 changes: 161 additions & 0 deletions tests/test-bearer-auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
var assert = require('assert')
, http = require('http')
, request = require('../index')
;

var numBasicRequests = 0;

var basicServer = http.createServer(function (req, res) {
console.error('Bearer auth server: ', req.method, req.url);
numBasicRequests++;

var ok;

if (req.headers.authorization) {
if (req.headers.authorization == 'Bearer theToken') {
ok = true;
} else {
// Bad auth header, don't send back WWW-Authenticate header
ok = false;
}
} else {
// No auth header, send back WWW-Authenticate header
ok = false;
res.setHeader('www-authenticate', 'Bearer realm="Private"');
}

if (req.url == '/post/') {
var expectedContent = 'data_key=data_value';
req.on('data', function(data) {
assert.equal(data, expectedContent);
console.log('received request data: ' + data);
});
assert.equal(req.method, 'POST');
assert.equal(req.headers['content-length'], '' + expectedContent.length);
assert.equal(req.headers['content-type'], 'application/x-www-form-urlencoded; charset=utf-8');
}

if (ok) {
console.log('request ok');
res.end('ok');
} else {
console.log('status=401');
res.statusCode = 401;
res.end('401');
}
});

basicServer.listen(6767);

var tests = [
function(next) {
request({
'method': 'GET',
'uri': 'http://localhost:6767/test/',
'auth': {
'bearer': 'theToken',
'sendImmediately': false
}
}, function(error, res, body) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 2);
next();
});
},

function(next) {
// If we don't set sendImmediately = false, request will send bearer auth
request({
'method': 'GET',
'uri': 'http://localhost:6767/test2/',
'auth': {
'bearer': 'theToken'
}
}, function(error, res, body) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 3);
next();
});
},

function(next) {
request({
'method': 'POST',
'form': { 'data_key': 'data_value' },
'uri': 'http://localhost:6767/post/',
'auth': {
'bearer': 'theToken',
'sendImmediately': false
}
}, function(error, res, body) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 5);
next();
});
},

function (next) {
request
.get('http://localhost:6767/test/')
.auth(null,null,false,"theToken")
.on('response', function (res) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 7);
next();
})
},

function (next) {
request
.get('http://localhost:6767/test/')
.auth(null,null,true,"theToken")
.on('response', function (res) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 8);
next();
})
},

function(next) {
request({
'method': 'GET',
'uri': 'http://localhost:6767/test/',
'auth': {
'bearer': function() { return 'theToken' },
'sendImmediately': false
}
}, function(error, res, body) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 10);
next();
});
},

function(next) {
// If we don't set sendImmediately = false, request will send bearer auth
request({
'method': 'GET',
'uri': 'http://localhost:6767/test2/',
'auth': {
'bearer': function() { return 'theToken' }
}
}, function(error, res, body) {
assert.equal(res.statusCode, 200);
assert.equal(numBasicRequests, 11);
next();
});
},
];

function runTest(i) {
if (i < tests.length) {
tests[i](function() {
runTest(i + 1);
});
} else {
console.log('All tests passed');
basicServer.close();
}
}

runTest(0);

0 comments on commit 4a2742e

Please sign in to comment.