Source: oauth2.js

/**
 * @file Manages Salesforce OAuth2 operations
 * @author Shinichi Tomita <shinichi.tomita@gmail.com>
 */

'use strict';

var querystring = require('querystring'),
    _ = require('lodash/core'),
    Transport = require('./transport');

var defaults = {
  loginUrl : "https://login.salesforce.com"
};

/**
 * OAuth2 class
 *
 * @class
 * @constructor
 * @param {Object} options - OAuth2 config options
 * @param {String} [options.loginUrl] - Salesforce login server URL
 * @param {String} [options.authzServiceUrl] - OAuth2 authorization service URL. If not specified, it generates from default by adding to login server URL.
 * @param {String} [options.tokenServiceUrl] - OAuth2 token service URL. If not specified it generates from default by adding to login server URL.
 * @param {String} options.clientId - OAuth2 client ID.
 * @param {String} options.clientSecret - OAuth2 client secret.
 * @param {String} options.redirectUri - URI to be callbacked from Salesforce OAuth2 authorization service.
 */
var OAuth2 = module.exports = function(options) {
  if (options.authzServiceUrl && options.tokenServiceUrl) {
    this.loginUrl = options.authzServiceUrl.split('/').slice(0, 3).join('/');
    this.authzServiceUrl = options.authzServiceUrl;
    this.tokenServiceUrl = options.tokenServiceUrl;
    this.revokeServiceUrl = options.revokeServiceUrl;
  } else {
    this.loginUrl = options.loginUrl || defaults.loginUrl;
    this.authzServiceUrl = this.loginUrl + "/services/oauth2/authorize";
    this.tokenServiceUrl = this.loginUrl + "/services/oauth2/token";
    this.revokeServiceUrl = this.loginUrl + "/services/oauth2/revoke";
  }
  this.clientId = options.clientId;
  this.clientSecret = options.clientSecret;
  this.redirectUri = options.redirectUri;
  this._transport =
    options.proxyUrl ? new Transport.ProxyTransport(options.proxyUrl) : new Transport();
};



/**
 *
 */
_.extend(OAuth2.prototype, /** @lends OAuth2.prototype **/ {

  /**
   * Get Salesforce OAuth2 authorization page URL to redirect user agent.
   *
   * @param {Object} params - Parameters
   * @param {String} params.scope - Scope values in space-separated string
   * @param {String} params.state - State parameter
   * @returns {String} Authorization page URL
   */
  getAuthorizationUrl : function(params) {
    params = _.extend({
      response_type : "code",
      client_id : this.clientId,
      redirect_uri : this.redirectUri
    }, params || {});
    return this.authzServiceUrl +
      (this.authzServiceUrl.indexOf('?') >= 0 ? "&" : "?") +
      querystring.stringify(params);
  },

  /**
   * @typedef TokenResponse
   * @type {Object}
   * @property {String} access_token
   * @property {String} refresh_token
   */

  /**
   * OAuth2 Refresh Token Flow
   *
   * @param {String} refreshToken - Refresh token
   * @param {Callback.<TokenResponse>} [callback] - Callback function
   * @returns {Promise.<TokenResponse>}
   */
  refreshToken : function(refreshToken, callback) {
    return this._postParams({
      grant_type : "refresh_token",
      refresh_token : refreshToken,
      client_id : this.clientId,
      client_secret : this.clientSecret
    }, callback);
  },

  /**
   * OAuth2 Web Server Authentication Flow (Authorization Code)
   * Access Token Request
   *
   * @param {String} code - Authorization code
   * @param {Callback.<TokenResponse>} [callback] - Callback function
   * @returns {Promise.<TokenResponse>}
   */
  requestToken : function(code, callback) {
    return this._postParams({
      grant_type : "authorization_code",
      code : code,
      client_id : this.clientId,
      client_secret : this.clientSecret,
      redirect_uri : this.redirectUri
    }, callback);
  },

  /**
   * OAuth2 Username-Password Flow (Resource Owner Password Credentials)
   *
   * @param {String} username - Salesforce username
   * @param {String} password - Salesforce password
   * @param {Callback.<TokenResponse>} [callback] - Callback function
   * @returns {Promise.<TokenResponse>}
   */
  authenticate : function(username, password, callback) {
    return this._postParams({
      grant_type : "password",
      username : username,
      password : password,
      client_id : this.clientId,
      client_secret : this.clientSecret,
      redirect_uri : this.redirectUri
    }, callback);
  },

  /**
   * OAuth2 Revoke Session Token
   *
   * @param {String} accessToken - Access token to revoke
   * @param {Callback.<undefined>} [callback] - Callback function
   * @returns {Promise.<undefined>}
   */
  revokeToken : function(accessToken, callback) {
    return this._transport.httpRequest({
      method : 'POST',
      url : this.revokeServiceUrl,
      body: querystring.stringify({ token: accessToken }),
      headers: {
        "content-type" : "application/x-www-form-urlencoded"
      }
    }).then(function(response) {
      if (response.statusCode >= 400) {
        var res = querystring.parse(response.body);
        if (!res || !res.error) {
          res = { error: "ERROR_HTTP_"+response.statusCode, error_description: response.body };
        }
        var err = new Error(res.error_description);
        err.name = res.error;
        throw err;
      }
    }).thenCall(callback);
  },

  /**
   * @private
   */
  _postParams : function(params, callback) {
    return this._transport.httpRequest({
      method : 'POST',
      url : this.tokenServiceUrl,
      body : querystring.stringify(params),
      headers : {
        "content-type" : "application/x-www-form-urlencoded"
      }
    }).then(function(response) {
      var res;
      try {
        res = JSON.parse(response.body);
      } catch(e) {}
      if (response.statusCode >= 400) {
        res = res || { error: "ERROR_HTTP_"+response.statusCode, error_description: response.body };
        var err = new Error(res.error_description);
        err.name = res.error;
        throw err;
      }
      return res;
    }).thenCall(callback);
  }

});