wenwei1202/passport-wechat-public

View on GitHub
lib/strategy.js

Summary

Maintainability
C
1 day
Test Coverage
var passport = require('passport-strategy')
var url = require('url')
var util = require('util')
var utils = require('./utils')
var OAuth = require('wechat-oauth')
var debug = require('debug')('passport-wechat-public')



function WechatPublicStrategy(options, verify) {
  options = options || {}

  if (!verify) {
    throw new Error('WechatPublicStrategy requires a verify callback')
  }

  if (!options.appId) {
    throw new Error('WechatPublicStrategy requires a appId option')
  }
  if (!options.appSecret) {
    throw new Error('WechatPublicStrategy requires a appSecret option')
  }

  passport.Strategy.call(this)
  this.name = 'wechat-public'
  this._oauth = new OAuth(options.appId, options.appSecret, options.getToken, options.saveToken)
  this._agent = options.agent || 'wechat'
  this._callbackURL = options.callbackURL
  this._scope = options.scope || 'snsapi_base'
  this._scopeSeparator = options.scopeSeparator || ' '
  this._state = options.state
  this._verify = verify
  this._passReqToCallback = options.passReqToCallback
    //state can't be blank, needs it to determine if user disapproved auth.
  this._state = options.state || 'state'
  this._lang = options.language || 'zh_CN'
}

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(WechatPublicStrategy, passport.Strategy)


WechatPublicStrategy.prototype.authenticate = function(req, options) {
  options = options || {}

  var self = this
    //user disapproved, in the wechat docs it says it won't have code when user disapproved, actually get code 'authdeny'
  if (req.query && req.query.state && (!req.query.code || req.query.code === 'authdeny')) {
    debug('User disapproved authentication.')
    return this.fail({
      message: 'access_denied'
    })
  }

  var callbackURL = options.callbackURL || this._callbackURL
  if (callbackURL) {
    var parsed = url.parse(callbackURL)
    if (!parsed.protocol) {
      // The callback URL is relative, resolve a fully qualified URL from the
      // URL of the originating request.
      callbackURL = url.resolve(utils.originalURL(req, {
        proxy: this._trustProxy
      }), callbackURL)
    }
  }

  var params = {}
  if (req.query && req.query.code) {
    var code = req.query.code
    this._code = code
    this._oauth.getAccessToken(code,
      function(err, result) {
        if (err) return self.error(err)
        var data = result.data
        debug('retrieved access token.%j', data)
        var accessToken = data.access_token
        var refreshToken = data.refresh_token
        var openId = data.openid
        if (data.scope === 'snsapi_base') {
          var profile = {
            id: openId,
            openid: openId
          }
          verifyResult(accessToken, refreshToken, {}, profile, verified)
        } else {
          var lang = this._lang
          var options = {
            openid: openId,
            lang: lang
          }
          self._oauth.getUser(options, function(err, profile) {
            if (err) return self.error(err)
            debug('retrieved user profile: %j', profile)
            profile.id = profile.openid
            verifyResult(accessToken, refreshToken, {}, profile, verified)
          })
        }
      }
    )
  } else {
    params.redirect_uri = callbackURL
    var scope = options.scope || this._scope
    if (scope) {
      if (Array.isArray(scope)) {
        scope = scope.join(this._scopeSeparator)
      }
      params.scope = scope
    }
    var authUrlMethod = this._agent === 'wechat' ? 'getAuthorizeURL' : 'getAuthorizeURLForWebsite'
    var location = this._oauth[authUrlMethod](params.redirect_uri, this._state, params.scope)
    debug('start authentication, agent: %s, redirect to %s', this._agent, location)
    this.redirect(location, 302)
  }

  function verified(err, user, info) {
    if (err) {
      return self.error(err)
    }
    if (!user) {
      return self.fail(info)
    }
    self.success(user, info)
  }

  function verifyResult(accessToken, refreshToken, params, profile, verified) {
    try {
      var arity = self._verify.length
      if (self._passReqToCallback) {
        if (arity === 6) {
          self._verify(req, accessToken, refreshToken, params, profile, verified)
        } else { // arity == 5
          self._verify(req, accessToken, refreshToken, profile, verified)
        }
      } else {
        if (arity === 5) {
          self._verify(accessToken, refreshToken, params, profile, verified)
        } else { // arity == 4
          self._verify(accessToken, refreshToken, profile, verified)
        }
      }
    } catch (ex) {
      self.error(ex)
    }
  }
}

module.exports = WechatPublicStrategy