Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Steve King committed Feb 16, 2013
1 parent 76fa5e4 commit 806c384
Show file tree
Hide file tree
Showing 12 changed files with 555 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
.idea/*
*.iml
node_modules/*
4 changes: 4 additions & 0 deletions .npmignore
@@ -0,0 +1,4 @@
.idea/*
*.iml
.git*
node_modules/*
23 changes: 23 additions & 0 deletions package.json
@@ -0,0 +1,23 @@
{
"name": "gauth",
"version": "0.0.1",
"description": "Middleware component to authenticate users through their Google account",
"author": "Steve King <steve@mydev.co>",
"contributors": [ { "name": "Steve King", "email": "steve@mydev.co" } ],
"keywords": ["authentication", "oauth", "google", "express", "connect", "middleware"],
"repository": "git://github.com/steveukx/gauth",
"main":"./src/index.js",
"engines": { "node": ">= 0.8.0" },
"dependencies":{
"promise-lite": "",
"subscribable": "",
"xhrequest": ""
},
"devDependencies": {
"express": "3.1.0",
"unit-test": ""
},
"scripts": {
"test": "node test/test.js"
}
}
41 changes: 41 additions & 0 deletions src/accesstokengenerator.js
@@ -0,0 +1,41 @@
/**
* @exports AccessTokenGenerator
*/
(function () {

"use strict";

/**
* The AccessTokenGenerator requires an end point URL that requests can be sent to and returns a promise interface
* that will be resolved with the access token or rejected should any errors take place.
*
* @name AccessTokenGenerator
* @constructor
*/
function AccessTokenGenerator(endPointUrl) {
var promise = new (require('promise-lite').Promise);
var query = '?' + require('querystring').stringify({
"openid.ns": 'http://specs.openid.net/auth/2.0',
"openid.mode": "associate",
"openid.assoc_type": "HMAC-SHA1",
"openid.session_type": "no-encryption"
});

require('xhrequest')(endPointUrl + query, {
success: function (data) {
promise.resolve(AccessTokenGenerator.getResultFromServerResponse(data.toString('utf8')));
},
error: function () {
promise.reject(new Error("Unable to fetch an access token"));
}
});
return promise;
}

AccessTokenGenerator.getResultFromServerResponse = function (data) {
return data.match(/assoc_handle\:(.+)/)[1];
};

module.exports = AccessTokenGenerator;

}());
128 changes: 128 additions & 0 deletions src/authenticator.js
@@ -0,0 +1,128 @@
/**
* @exports Authenticator
*/
(function () {

"use strict";

/**
*
* @name Authenticator
* @constructor
*/
function Authenticator(endPointUrl, assocHandle, baseUrl) {
this._assocHandle = assocHandle;
this._endPointUrl = endPointUrl;
this._baseUrl = baseUrl;
this._realm = (function(url) { return url.protocol + '//' + url.host + '/'; }(require('url').parse(baseUrl)));
}

/**
* @type {String} The base url is the absolute URL to the root of the authenticated content (eg: http://domain.com/usercontent/)
*/
Authenticator.prototype._baseUrl = "";

/**
* @type {String} The realm is the trusted domain that the user must agree to authenticating with, derived from the host of the base url (eg: http://domain.com/)
*/
Authenticator.prototype._realm = "";

/**
* Sets the base url to be used in this authenticator, and will as a result also set the realm to match the base url.
* @param {String} baseUrl
* @return {Authenticator}
*/
Authenticator.prototype.setBaseUrl = function(baseUrl) {
if(baseUrl != this._baseUrl) {
this._baseUrl = baseUrl;
this._realm = (function(url) { return url.protocol + '//' + url.host + '/'; }(require('url').parse(baseUrl)));
}
return this;
};

/**
* Gets the URL that should be used to authenticate a user with a terminal URL of the supplied backTo path.
*
* @param {String} backTo
* @return {string}
*/
Authenticator.prototype.getLogInUrl = function(backTo) {
var params = this._getLoginParameters();
params['openid.return_to'] += '?next=' + backTo;

return this._endPointUrl + '?' + require('querystring').stringify(params);
};

/**
* Applies properties to the supplied object that will include email request parameters
* @param {Object} params
*/
Authenticator.prototype._mergeEmailRequest = function(params) {
params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
params['openid.ax.mode'] = 'fetch_request';
params['openid.ax.required'] = 'email,firstname,lastname';

params['openid.ax.type.email'] = 'http://schema.openid.net/contact/email';
params['openid.ax.type.firstname'] = 'http://axschema.org/namePerson/first';
params['openid.ax.type.lastname'] = 'http://axschema.org/namePerson/last';
};

/**
* Gets the URL parameters used for associating a user's google account with this application
* @return {Object}
*/
Authenticator.prototype._getLoginParameters = function() {
var params = this._getParameters();

params['openid.claimed_id'] = 'http://specs.openid.net/auth/2.0/identifier_select';
params['openid.identity'] = 'http://specs.openid.net/auth/2.0/identifier_select';
params['openid.return_to'] = this._baseUrl + '/responder';
params['openid.realm'] = this._realm;
params['openid.assoc_handle'] = '';
params['openid.mode'] = 'checkid_setup';
this._mergeEmailRequest(params);

return params;
};

Authenticator.prototype._getIdentityResponseParameters = function() {
var params = this._getParameters();

params['openid.claimed_id'] = 'http://specs.openid.net/auth/2.0/identifier_select';
params['openid.identity'] = 'http://specs.openid.net/auth/2.0/identifier_select';
params['openid.return_to'] = this._baseUrl + '/responder';
params['openid.realm'] = this._realm;
params['openid.assoc_handle'] = '';
params['openid.mode'] = 'checkid_setup';
this._mergeEmailRequest(params);

return params;
};

/**
* Gets the URL parameters used for cancelling the association of the google account with this application
* @return {Object}
*/
Authenticator.prototype._getCancelParameters = function() {
return {
'openid.ns': 'http://specs.openid.net/auth/2.0',
'openid.mode': 'cancel'
};
};

/**
* Gets default parameters for all requests
* @return {Object}
*/
Authenticator.prototype._getParameters = function() {
return {
'openid.ns': 'http://specs.openid.net/auth/2.0',
'openid.assoc_handle': this._assocHandle
};
};



module.exports = Authenticator;

}());
43 changes: 43 additions & 0 deletions src/endpointresolver.js
@@ -0,0 +1,43 @@
/**
* @exports EndPointResolver
*/
(function () {

"use strict";

/**
* Sends a request to the supplied discovery URL and parses the response for the URL that subsequent requests should
* be sent. The result of calling the EndPointResolver is a promise interface that will be resolved with the end point
* url or rejected if any errors took place.
*
* @param {String} discoveryUrl
*
* @name EndPointResolver
* @constructor
*/
function EndPointResolver(discoveryUrl) {
var promise = new (require('promise-lite').Promise);
require('xhrequest')(discoveryUrl, {
success: function(data) {
promise.resolve(EndPointResolver.getResultFromServerResponse(data.toString('utf8')));
},
error: function() {
promise.reject(new Error("Unable to connect to end point discovery URL"));
}
});
return promise;
}

/**
* Given the response data from the discovery resource, retrieves the URI for making all further requests bound to.
*
* @param {String} data
* @return {String}
*/
EndPointResolver.getResultFromServerResponse = function(data) {
return data.match(/<URI>(.+)<\/URI>/i)[1];
};

module.exports = EndPointResolver;

}());

0 comments on commit 806c384

Please sign in to comment.