314 lines
7.8 KiB
JavaScript
314 lines
7.8 KiB
JavaScript
|
/*jslint indent: 2*/
|
||
|
/*global require: true*/
|
||
|
/**
|
||
|
* For more details about the ClientLogin authentication check out this:
|
||
|
* http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html
|
||
|
*/
|
||
|
var EventEmitter = require('events').EventEmitter,
|
||
|
util = require('util');
|
||
|
|
||
|
// useragent string
|
||
|
const userAgent = 'GCLNodejs';
|
||
|
// version string
|
||
|
const ver = '0.2.2';
|
||
|
|
||
|
const loginURL = '/accounts/ClientLogin';
|
||
|
const googleHost = 'www.google.com';
|
||
|
const captchaRequiredError = 'CaptchaRequired';
|
||
|
|
||
|
// error messages
|
||
|
const errors = {
|
||
|
captchaMissing: 'User entered captcha is missing',
|
||
|
tokenMissing: 'Login token is missing',
|
||
|
loginFailed: 'Login failed'
|
||
|
};
|
||
|
|
||
|
const events = {
|
||
|
login: 'login',
|
||
|
error: 'error'
|
||
|
};
|
||
|
|
||
|
// Google account types
|
||
|
const accountTypes = {
|
||
|
google: 'GOOGLE', // get authorization for a Google account only
|
||
|
hosted: 'HOSTED', // get authorization for a hosted account only
|
||
|
hostedOrGoogle: 'HOSTED_OR_GOOGLE' // get authorization first for a hosted account; if attempt fails, get authorization for a Google account
|
||
|
};
|
||
|
// http://code.google.com/apis/gdata/faq.html#clientlogin
|
||
|
const services = {
|
||
|
adwords: 'adwords',
|
||
|
analytics: 'analytics',
|
||
|
apps: 'apps',
|
||
|
base: 'gbase',
|
||
|
sites: 'jotspot',
|
||
|
blogger: 'blogger',
|
||
|
book: 'print',
|
||
|
calendar: 'cl',
|
||
|
codesearch: 'codesearch',
|
||
|
contacts: 'cp',
|
||
|
docs: 'writely',
|
||
|
finance: 'finance',
|
||
|
mail: 'mail',
|
||
|
health: 'health',
|
||
|
weaver: 'weaver',
|
||
|
maps: 'local',
|
||
|
picasaweb: 'lh2',
|
||
|
reader: 'reader',
|
||
|
sidewiki: 'annotateweb',
|
||
|
spreadsheets: 'wise',
|
||
|
webmastertools: 'sitemaps',
|
||
|
youtube: 'youtube',
|
||
|
c2dm: 'ac2dm',
|
||
|
voice: 'grandcentral',
|
||
|
fusiontables: 'fusiontables'
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Helps to log in to any google service with the clientlogin method
|
||
|
* Google returns 3 values when login was success:
|
||
|
* Auth, SID, LSID
|
||
|
*
|
||
|
* After the login you need to include the Auth value into
|
||
|
* the Authorization HTTP header on each request:
|
||
|
*
|
||
|
* client.request('GET', '...', {
|
||
|
* ...,
|
||
|
* 'Authorization':'GoogleLogin auth=' + googleClientLoginInstance.getAuthId()
|
||
|
* })
|
||
|
*
|
||
|
* @class GoogleClientLogin
|
||
|
* @constructor
|
||
|
* @param Object conf An object, with two properties: email and password
|
||
|
*/
|
||
|
var GoogleClientLogin = function (conf) {
|
||
|
this.conf = conf || {};
|
||
|
// stores the authentication data
|
||
|
this.auths = {};
|
||
|
this.loginProcessing = false;
|
||
|
};
|
||
|
GoogleClientLogin.prototype = {};
|
||
|
util.inherits(GoogleClientLogin, EventEmitter);
|
||
|
|
||
|
/**
|
||
|
* Splits response data into key-value pairs,
|
||
|
* Only for internal usage
|
||
|
* @method _parseData
|
||
|
*/
|
||
|
GoogleClientLogin.prototype._parseData = function (data) {
|
||
|
this.auths = {};
|
||
|
data.split('\n').forEach(function (dataStr) {
|
||
|
var data = dataStr.split('=');
|
||
|
this.auths[data[0]] = data[1];
|
||
|
}.bind(this));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Parses the response of the login
|
||
|
* emits error and login event
|
||
|
* @method _parseLoginResponse
|
||
|
* @param {http.ClientResponse} response The response object
|
||
|
*/
|
||
|
GoogleClientLogin.prototype._parseLoginResponse = function (response) {
|
||
|
|
||
|
var data = '';
|
||
|
|
||
|
response.on('data', function (chunk) {
|
||
|
data += chunk;
|
||
|
}.bind(this));
|
||
|
|
||
|
response.on('error', function (e) {
|
||
|
this.emit(events.error, e);
|
||
|
}.bind(this));
|
||
|
|
||
|
response.on('end', function () {
|
||
|
this.loginProcessing = false;
|
||
|
var statusCode = response.statusCode, error;
|
||
|
this._parseData(data);
|
||
|
if (statusCode >= 200 && statusCode < 300) {
|
||
|
/**
|
||
|
* Fires when login was success
|
||
|
* @event login
|
||
|
*/
|
||
|
this.emit(events.login);
|
||
|
} else {
|
||
|
/**
|
||
|
* Fires when login failed
|
||
|
* @event loginFailed
|
||
|
*/
|
||
|
error = new Error(errors.loginFailed);
|
||
|
error.data = data;
|
||
|
error.response = response;
|
||
|
this.emit(events.error, error);
|
||
|
}
|
||
|
}.bind(this));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to find out which account type should we use, default is HOSTED_OR_GOOGLE
|
||
|
* Only for internal usage
|
||
|
* @method _getAccountType
|
||
|
* @returns string
|
||
|
*/
|
||
|
GoogleClientLogin.prototype._getAccountType = function (params) {
|
||
|
var output = accountTypes.hostedOrGoogle;
|
||
|
|
||
|
if (typeof params === 'object' && params.accountType === 'string' &&
|
||
|
typeof accountTypes[params.accountType] === 'string') {
|
||
|
output = accountTypes[params.accountType];
|
||
|
} else if (typeof this.conf.accountType === 'string') {
|
||
|
output = accountTypes[this.conf.accountType];
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to create the content of the login request
|
||
|
* Only for internal usage
|
||
|
* @method _getRequestContent
|
||
|
* @param {Object} params (Optional) You can pass the logincaptcha and
|
||
|
* logintoken and the accountType as properties
|
||
|
* @returns string
|
||
|
*/
|
||
|
GoogleClientLogin.prototype._getRequestContent = function (params) {
|
||
|
var output, hasCaptcha, hasToken, error;
|
||
|
|
||
|
output = {
|
||
|
accountType: this._getAccountType(params),
|
||
|
Email: this.conf.email,
|
||
|
Passwd: this.conf.password,
|
||
|
service: services[this.conf.service],
|
||
|
source: userAgent + '_' + ver
|
||
|
};
|
||
|
|
||
|
if (typeof params === 'object') {
|
||
|
|
||
|
hasCaptcha = typeof params.logincaptcha === 'string';
|
||
|
hasToken = typeof params.logintoken === 'string';
|
||
|
|
||
|
if (hasCaptcha && hasToken) {
|
||
|
output.logincaptcha = params.logincaptcha;
|
||
|
output.logintoken = params.logintoken;
|
||
|
// if the captcha or the token is given the other also required
|
||
|
} else if (!hasCaptcha && hasToken) {
|
||
|
error = errors.captchaMissing;
|
||
|
} else if (!hasToken && hasCaptcha) {
|
||
|
error = errors.tokenMissing;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (error) {
|
||
|
this.emit(events.error, new Error(error));
|
||
|
output = false;
|
||
|
} else {
|
||
|
output = require('querystring').stringify(output);
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Logs in the user
|
||
|
* @method login
|
||
|
* @param {Object} params (optional) You can pass the logincaptcha and
|
||
|
* logintoken and the accountType as properties
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.login = function (params) {
|
||
|
// don't try to log in, if one is already in progress
|
||
|
if (!this.loginProcessing) {
|
||
|
this.loginProcessing = true;
|
||
|
|
||
|
var content, request;
|
||
|
|
||
|
content = this._getRequestContent(params);
|
||
|
|
||
|
if (content !== false) {
|
||
|
request = require('https').request(
|
||
|
{
|
||
|
host: 'www.google.com',
|
||
|
port: 443,
|
||
|
path: loginURL,
|
||
|
method: 'POST',
|
||
|
headers: {
|
||
|
'Content-Length': content.length,
|
||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||
|
}
|
||
|
},
|
||
|
this._parseLoginResponse.bind(this)
|
||
|
);
|
||
|
request.write(content);
|
||
|
request.end();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to get the AuthId property
|
||
|
* @method getAuthId
|
||
|
* @returns the AuthId or undefined
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.getAuthId = function () {
|
||
|
return this.auths.Auth;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to ge the SID property
|
||
|
* @method getSID
|
||
|
* @returns the value of the SID property or undefined
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.getSID = function () {
|
||
|
return this.auths.SID;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to get the LSID property
|
||
|
* @method getLSID
|
||
|
* @returns the value of the LSID property or undefined
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.getLSID = function () {
|
||
|
return this.auths.LSID;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to get the error code
|
||
|
* @method getError
|
||
|
* @returns the error code or undefined
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.getError = function () {
|
||
|
return this.auths.Error;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to know if captcha is required
|
||
|
* @method isCaptchaRequired
|
||
|
* @returns boolean
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.isCaptchaRequired = function () {
|
||
|
return this.getError() === captchaRequiredError;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Method to get the captcha url
|
||
|
* @method getCaptchaUrl
|
||
|
* @returns the value of the CaptchaUrl property or undefined
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.getCaptchaUrl = function () {
|
||
|
return this.auths.CaptchaUrl;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns the value of the CaptchaToken property
|
||
|
* @method getCaptchaToken
|
||
|
* @returns string or undefined
|
||
|
*/
|
||
|
GoogleClientLogin.prototype.getCaptchaToken = function () {
|
||
|
return this.auths.CaptchaToken;
|
||
|
};
|
||
|
|
||
|
GoogleClientLogin.errors = errors;
|
||
|
GoogleClientLogin.events = events;
|
||
|
GoogleClientLogin.accountTypes = accountTypes;
|
||
|
|
||
|
exports.GoogleClientLogin = GoogleClientLogin;
|