forked from mrblur/connect-auth
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improved the http/digest Authorization header parsing (still not conv…
…inced its right). Added support for facebook's OAuth2 sign-on.
- Loading branch information
Showing
7 changed files
with
138 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,18 @@ | ||
exports.merge(require('./auth.strategy.base')) | ||
Object.merge(exports, require('./auth.strategy.base')) | ||
AuthStrategy= exports.AuthStrategy | ||
BaseHttpStrategy= require('./auth.strategies/http/base').BaseHttpStrategy | ||
exports.merge(require('./auth.strategies/http/digest')) | ||
exports.merge(require('./auth.strategies/http/basic')) | ||
exports.merge(require('./auth.strategies/http/http')) | ||
exports.merge(require('./auth.strategies/anonymous')) | ||
exports.merge(require('./auth.strategies/never')) | ||
exports.merge(require('./auth.strategies/twitter')) | ||
Object.merge(exports, require('./auth.strategies/http/digest')) | ||
Object.merge(exports, require('./auth.strategies/http/basic')) | ||
Object.merge(exports, require('./auth.strategies/http/http')) | ||
Object.merge(exports, require('./auth.strategies/anonymous')) | ||
Object.merge(exports, require('./auth.strategies/never')) | ||
Object.merge(exports, require('./auth.strategies/twitter')) | ||
Object.merge(exports, require('./auth.strategies/facebook')) | ||
|
||
exports.merge(require('./strategies')) | ||
exports.merge(require('./strategyExecutor')) | ||
Object.merge(exports, require('./strategies')) | ||
Object.merge(exports, require('./strategyExecutor')) | ||
|
||
Http= exports.Http | ||
Never= exports.Never | ||
Anonymous= exports.Anonymous | ||
exports.merge(require('./auth.core')) | ||
Object.merge(exports, require('./auth.core')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,106 @@ | ||
var md5= require('support/ext/lib/ext/md5'), | ||
utils = require('express/utils'); | ||
|
||
var sys= require('sys'); | ||
BaseHttpStrategy= require('./base').BaseHttpStrategy; | ||
|
||
exports.Digest= BaseHttpStrategy.extend({ | ||
constructor: function(options){ | ||
var options= options || {} | ||
BaseHttpStrategy.prototype.constructor.call(this, options) | ||
this._realm= options.realm || "test" | ||
this._realm= options.realm || "secure" | ||
this._getPasswordForUser= options.getPasswordForUser; | ||
}, | ||
|
||
authenticate: function(request, callback) { | ||
var self= this; | ||
var authHeader= request.header('Authorization'); | ||
// Digest username="foo", realm="test", nonce="b343d03296358b5d7f985500568b", uri="/", response="52bc08c966a3b16bedb62f1b4a5b40f8" | ||
//TODO: parse this properly, temporary regex hack. | ||
var isDigest= /^[D]igest\s.+"/.exec(authHeader) | ||
var username= /^[D]igest\susername="(.+?)"/.exec(authHeader) | ||
var response= /^[D]igest.+?response="(.+?)"/.exec(authHeader) | ||
var nonce= /^[D]igest.+?nonce="(.+?)"/.exec(authHeader) | ||
if( isDigest && username && username[1] && response && response[1] && nonce && nonce[1]) { | ||
nonce= nonce[1]; | ||
username= username[1]; | ||
response= response[1]; | ||
} | ||
var method= request.method | ||
var href= request.url.href | ||
if(authHeader) { | ||
var credentials= this._splitAuthorizationHeader(authHeader); | ||
var method= request.method | ||
var href= request.url.href | ||
|
||
|
||
this._getPasswordForUser(username, function(error, password){ | ||
if(error) callback(error); | ||
else { | ||
var HA1= md5.hash( username+":"+ self._realm + ":"+ password) | ||
var HA2= md5.hash( method+ ":" + href ) | ||
var myResponse= md5.hash(HA1 + ":"+ nonce + ":"+ HA2 ) | ||
if( myResponse == response ) { | ||
self.success({"username":username}, callback); | ||
} | ||
this._getPasswordForUser(credentials.username, function(error, password){ | ||
if(error) callback(error); | ||
else { | ||
self._unAuthenticated(request, callback) | ||
var HA1= md5.hash( credentials.username+":"+ self._realm + ":"+ password) | ||
var HA2= md5.hash( method+ ":" + href ) | ||
var myResponse= md5.hash(HA1 + ":"+ credentials.nonce + ":"+ HA2 ) | ||
if( myResponse == credentials.response ) { | ||
self.success({ username : credentials.username}, callback); | ||
} | ||
else { | ||
self._unAuthenticated(request, callback) | ||
} | ||
} | ||
} | ||
}) | ||
}) | ||
} | ||
else { | ||
self._unAuthenticated(request, callback) | ||
} | ||
|
||
}, | ||
|
||
getAuthenticateResponseHeader: function( request ) { | ||
return "Digest realm=" + this._realm + ", nonce="+ utils.uid(); | ||
return "Digest realm=\"" + this._realm.replace("\"","\\\"") + "\", nonce=\""+ this._getNonce(32)+"\""; | ||
}, | ||
|
||
/** | ||
* Given a valid Digest Authorization HTTP Header will return an object literal | ||
* that contains the passed credentials. | ||
* | ||
* @return {object} The digest credentials, un-encoded and un-quoted. | ||
* @api private | ||
*/ | ||
_splitAuthorizationHeader: function( authorizationHeader ) { | ||
|
||
var results= {}; | ||
|
||
var parameterPairs= []; | ||
var isInQuotes= false; | ||
var lastStringStartingBoundary= 0; | ||
|
||
//Need to pull off authentication type first | ||
results.type= /^([a-zA-Z]+)\s/.exec(authorizationHeader)[1]; | ||
authorizationHeader= authorizationHeader.substring( results.type.length + 1 ) // type + 1 whitespace | ||
|
||
for(var i=0;i< authorizationHeader.length;i++) { | ||
if( authorizationHeader[i] == "\"" && authorizationHeader[i-1] != "\\" ) { | ||
// WE've found an un-escaped quote (do escaped quotes exist, need to check the RFC) | ||
isInQuotes= !isInQuotes; | ||
} | ||
if( authorizationHeader[i] == "," && !isInQuotes ) { | ||
var credentialsPart= authorizationHeader.substr(lastStringStartingBoundary, (i-lastStringStartingBoundary)); | ||
//Strip whitespace.. | ||
credentialsPart= credentialsPart.replace(/^\s+|\s+$/g,'') | ||
|
||
|
||
parameterPairs[parameterPairs.length]= credentialsPart; | ||
lastStringStartingBoundary= i+1; // skip the comma. | ||
} | ||
} | ||
|
||
// Refactor this code. | ||
if( lastStringStartingBoundary < authorizationHeader.length ) { | ||
var credentialsPart= authorizationHeader.substr(lastStringStartingBoundary, (authorizationHeader.length-lastStringStartingBoundary)); | ||
//Strip whitespace.. | ||
credentialsPart= credentialsPart.replace(/^\s+|\s+$/g,'') | ||
parameterPairs[parameterPairs.length]= credentialsPart; | ||
lastStringStartingBoundary= i+1; // skip the comma. | ||
} | ||
|
||
|
||
for(var key in parameterPairs) { | ||
var pair= /^(.+)?=(.+)/.exec(parameterPairs[key]) | ||
|
||
//de-code quotes and un-escape inter-stitial quotes if appropriate | ||
// I'm lost as to the correct behaviour of this bit tbh, the rfcs don't seem to be specifc | ||
// around whether quoted strings need to quote the quotes or not!! (that I can find anyway :) ) | ||
var value= pair[2].replace(/^"|"$/g, '') | ||
value= value.replace(/\\"/g, '"') | ||
|
||
results[pair[1]]= value | ||
} | ||
|
||
return results; | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters