├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── gulpfile.js ├── lib └── index.js ├── package.json └── test └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v5 4 | - v4 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 calidion (calidion.github.io) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-weixin-oauth [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Coverage percentage][coveralls-image]][coveralls-url] 2 | > 3 | 4 | > 微信 Oauth 模块,用于验证微信用户,并且获取用户信息 5 | 6 | 7 | ## 说明 8 | 9 | 这个项目是 10 | ([node-weixin-api](https://github.com/node-weixin/node-weixin-api) 11 | 或者 12 | [node-weixin-express](https://github.com/node-weixin/node-weixin-express)) 13 | 的一个子项目。 14 | 15 | 作用是完成用户的Oauth验证。 16 | 17 | 微信Oauth模块与Auth模块在node-weixin里是两个不同的模块。 18 | 1. Oauth主要用于用户身份的验证。 19 | 2. Auth主要用于应用服务器的验证。即运行着auth模块的用户服务器被腾讯官方的微信服务器所验证。 20 | 21 | Oauth的实现主要分成以下几个部分。 22 | 1. 重新向到验证服务,并且提交返回URL 23 | 2. 在验证服务器输入用户登录信息(在微信中,由于用户已经登录,所以不需要这个过程) 24 | 3. 成功则返回到提交的URL,并提交code给用户的应用服务器。 25 | 4. 用户的应用服务器根据code,结合自己的appid, appsecret向微信服务器请求acess token, refresh token, openid等 26 | 27 | 交流QQ群: 39287176 28 | 29 | 注: 30 | 31 | [node-weixin-express](https://github.com/node-weixin/node-weixin-express)是基于node-weixin-*的服务器端参考实现。 32 | 33 | [node-weixin-api](https://github.com/node-weixin/node-weixin-api)是基于node-weixin-*的API接口SDK。 34 | 35 | 它们都是由下列子项目组合而成: 36 | 37 | 1. [node-weixin-config](https://github.com/node-weixin/node-weixin-config) 38 | 用于微信配置信息的校验 39 | 40 | 2. [node-weixin-auth](https://github.com/node-weixin/node-weixin-auth) 41 | 用于与微信服务器握手检验 42 | 43 | 3. [node-weixin-util](https://github.com/node-weixin/node-weixin-util) 44 | 一些常用的微信请求,加密,解密,检验的功能与处理 45 | 46 | 4. [node-weixin-request](https://github.com/node-weixin/node-weixin-request) 47 | 微信的各类服务的HTTP请求的抽象集合 48 | 49 | 5. [node-weixin-oauth](https://github.com/node-weixin/node-weixin-oauth) 50 | 微信OAuth相关的操作 51 | 52 | 6. [node-weixin-pay](https://github.com/node-weixin/node-weixin-pay) 53 | 微信支付的服务器接口 54 | 55 | 7. [node-weixin-jssdk](https://github.com/node-weixin/node-weixin-jssdk) 56 | 微信JSSDK相关的服务器接口 57 | 58 | 8. [node-weixin-menu](https://github.com/node-weixin/node-weixin-menu) 59 | 微信菜单相关的操作与命令 60 | 61 | 9. [node-weixin-user](https://github.com/node-weixin/node-weixin-user) 62 | 微信用户API 63 | 64 | 10. [node-weixin-media](https://github.com/node-weixin/node-weixin-media) 65 | 微信多媒体API 66 | 67 | 11. [node-weixin-qrcode](https://github.com/node-weixin/node-weixin-qrcode) 68 | 微信二维码API 69 | 70 | 71 | ## 安装 72 | 73 | ```sh 74 | $ npm install --save node-weixin-oauth 75 | ``` 76 | 77 | ## 使用 78 | 79 | 1、得到oauth对象 80 | 81 | ```js 82 | var nwo = require('node-weixin-oauth'); 83 | ``` 84 | 2、创建用户通过微信可访问的URL 85 | - state是随意表示当前程序状态的值 86 | - userInfo: 0 表示最少的基本信息, 1表示获取更多用户信息 87 | - 创建好URL后,需要将用户引导到创建的地址进行校验 88 | 89 | ```js 90 | var url = nwo.createURL(appId, redirectUri, state, userInfo) 91 | res.redirect(url); 92 | ``` 93 | 94 | 3、在重定向函数里处理调用信息 95 | 在校验用户成功后,微信会将用户带回到redirectUri指定的地址进行下一步操作 96 | 在redirectUri里需要使用success来对返回的code进行校验,并获取以下三样数据: 97 | 1. 微信服务器的access token 98 | 2. 微信服务器的refresh token 99 | 3. 用户的openid 100 | 可以通过nwo.session访问,也可以通过返回的body访问。 101 | nwo.session里的数据结构如下: 102 | 103 | ```js 104 | { 105 | oauth.session = { 106 | openId: json.openid, 107 | accessToken: json.access_token, 108 | refreshToken: json.refresh_token 109 | }; 110 | ``` 111 | 112 | 调用如下: 113 | * 其中 114 | - app:是node-weixin-config通过app.init校验的数据 115 | - code:服务器返回校验数据 116 | 117 | ```js 118 | nwo.success(app, code, function(error, body) { 119 | if (!error) { 120 | var accessToken = body.acess_token; 121 | var refreshToken = body.refresh_token; 122 | } 123 | }); 124 | ``` 125 | 126 | 4、获取用户信息(可选) 127 | 当scope为1时,我们还可以进一步的获取用户信息 128 | 129 | 130 | ```js 131 | nwo.profile(openId, accessToken, function(error, body) { 132 | }); 133 | ``` 134 | 5、刷新access token(可选) 135 | 136 | ```js 137 | nwo.refresh(appId, refreshToken, function(error, body) { 138 | }); 139 | ``` 140 | 141 | 6、检验token有效性(可选) 142 | 143 | ```js 144 | nwo.validate(openid, accessToken, function(error, body) { 145 | }); 146 | ``` 147 | 148 | >实际的调用过程参考node-weixin-express 149 | 150 | 151 | ## License 152 | 153 | Apache-2.0 © [calidion](calidion.github.io) 154 | 155 | 156 | [npm-image]: https://badge.fury.io/js/node-weixin-oauth.svg 157 | [npm-url]: https://npmjs.org/package/node-weixin-oauth 158 | [travis-image]: https://travis-ci.org/node-weixin/node-weixin-oauth.svg?branch=master 159 | [travis-url]: https://travis-ci.org/node-weixin/node-weixin-oauth 160 | [daviddm-image]: https://david-dm.org/node-weixin/node-weixin-oauth.svg?theme=shields.io 161 | [daviddm-url]: https://david-dm.org/node-weixin/node-weixin-oauth 162 | [coveralls-image]: https://coveralls.io/repos/node-weixin/node-weixin-oauth/badge.svg 163 | [coveralls-url]: https://coveralls.io/r/node-weixin/node-weixin-oauth 164 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var gulp = require('gulp'); 4 | var eslint = require('gulp-eslint'); 5 | var excludeGitignore = require('gulp-exclude-gitignore'); 6 | var mocha = require('gulp-mocha'); 7 | var istanbul = require('gulp-istanbul'); 8 | var nsp = require('gulp-nsp'); 9 | var plumber = require('gulp-plumber'); 10 | var coveralls = require('gulp-coveralls'); 11 | 12 | gulp.task('static', function () { 13 | return gulp.src('**/*.js') 14 | .pipe(excludeGitignore()) 15 | .pipe(eslint()) 16 | .pipe(eslint.format()) 17 | .pipe(eslint.failAfterError()); 18 | }); 19 | 20 | gulp.task('nsp', function (cb) { 21 | nsp({package: path.resolve('package.json')}, cb); 22 | }); 23 | 24 | gulp.task('pre-test', function () { 25 | return gulp.src('lib/**/*.js') 26 | .pipe(excludeGitignore()) 27 | .pipe(istanbul({ 28 | includeUntested: true 29 | })) 30 | .pipe(istanbul.hookRequire()); 31 | }); 32 | 33 | gulp.task('test', ['pre-test'], function (cb) { 34 | var mochaErr; 35 | 36 | gulp.src('test/**/*.js') 37 | .pipe(plumber()) 38 | .pipe(mocha({reporter: 'spec'})) 39 | .on('error', function (err) { 40 | mochaErr = err; 41 | throw err; 42 | }) 43 | .pipe(istanbul.writeReports()) 44 | .on('end', function () { 45 | cb(mochaErr); 46 | }); 47 | }); 48 | 49 | gulp.task('watch', function () { 50 | gulp.watch(['lib/**/*.js', 'test/**'], ['test']); 51 | }); 52 | 53 | gulp.task('coveralls', ['test'], function () { 54 | if (!process.env.CI) { 55 | return; 56 | } 57 | 58 | gulp.src(path.join(__dirname, 'coverage/lcov.info')) 59 | .pipe(coveralls()); 60 | }); 61 | 62 | gulp.task('prepublish', ['nsp']); 63 | gulp.task('default', ['static', 'test', 'coveralls']); 64 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint camelcase: [2, {properties: "never"}] */ 4 | 5 | var assert = require('assert'); 6 | var restful = require('node-weixin-request'); 7 | var util = require('node-weixin-util'); 8 | 9 | var oauth = { 10 | session: {}, 11 | 12 | /** 13 | * Build parameters into oauth2 url 14 | * @param params 15 | * @returns {string} 16 | */ 17 | buildUrl: function (params) { 18 | var oauthUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize'; 19 | return oauthUrl + '?' + util.toParam(params) + '#wechat_redirect'; 20 | }, 21 | 22 | /** 23 | * Step 1: Create a url for weixin oauth give state, scope and type 24 | * 25 | * @param state User defined state to check use validation 26 | * @param scope The scope of user info which app want to have 27 | * @param type Response type of weixin api, currently on 'code' is supported 28 | * @returns {*} 29 | */ 30 | createURL: function (appId, redirectUri, state, scope, type) { 31 | assert((scope >= 0) && (scope <= 1)); 32 | assert(state !== null); 33 | type = 0; 34 | var params = { 35 | appid: appId, 36 | redirect_uri: redirectUri, 37 | // Only one type currently 38 | response_type: ['code'][type], 39 | scope: ['snsapi_base', 'snsapi_userinfo'][scope], 40 | state: state 41 | }; 42 | return this.buildUrl(params); 43 | }, 44 | 45 | /** 46 | * Refresh authorization info when the access token expires 47 | * @param appId 48 | * @param refreshToken 49 | * @param cb 50 | */ 51 | 52 | refresh: function (appId, refreshToken, cb) { 53 | var oauthUrl = 'https://api.weixin.qq.com/sns/oauth2/refresh_token'; 54 | var params = { 55 | appId: appId, 56 | grant_type: 'refresh_token', 57 | refresh_token: refreshToken 58 | }; 59 | var url = oauthUrl + '?' + util.toParam(params); 60 | restful.request(url, null, cb); 61 | }, 62 | 63 | /** 64 | * Get user profile 65 | * 66 | * @param openId 67 | * @param accessToken 68 | * @param cb 69 | */ 70 | profile: function (openId, accessToken, cb) { 71 | var oauthUrl = 'https://api.weixin.qq.com/sns/userinfo'; 72 | var params = { 73 | access_token: accessToken, 74 | openid: openId, 75 | lang: 'zh_CN' 76 | }; 77 | var url = oauthUrl + '?' + util.toParam(params); 78 | restful.request(url, null, cb); 79 | }, 80 | 81 | /** 82 | * Validate if the accessToken is still valid 83 | * @param openid 84 | * @param accessToken 85 | * @param cb 86 | */ 87 | validate: function (openid, accessToken, cb) { 88 | var oauthUrl = 'https://api.weixin.qq.com/sns/auth'; 89 | var params = { 90 | access_token: accessToken, 91 | openid: openid 92 | }; 93 | var url = oauthUrl + '?' + util.toParam(params, true); 94 | restful.request(url, null, function (error, json) { 95 | if (json.errcode) { 96 | cb(false); 97 | } else { 98 | cb(true); 99 | } 100 | }); 101 | }, 102 | 103 | /** 104 | * Get access token from server 105 | * 106 | * @param appToken 107 | * @param params 108 | * @param cb 109 | */ 110 | tokenize: function (appToken, params, cb) { 111 | var oauthUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token'; 112 | params.access_token = appToken; 113 | var url = oauthUrl + '?' + util.toParam(params) + '#wechat_redirect'; 114 | restful.request(url, null, cb); 115 | }, 116 | 117 | /** 118 | * Get access token after code retrieved 119 | * @param app 120 | * @param code 121 | * @param cb 122 | */ 123 | authorize: function (app, code, cb) { 124 | var params = { 125 | appid: app.id, 126 | secret: app.secret, 127 | grant_type: 'authorization_code', 128 | code: code 129 | }; 130 | this.tokenize(app.token, params, function (error, json) { 131 | if (error) { 132 | cb(true, error); 133 | } else { 134 | cb(false, json); 135 | } 136 | }); 137 | }, 138 | 139 | /** 140 | * Callback when oauth from weixin is successful. 141 | * 142 | * @param app 143 | * @param code 144 | * @param cb 145 | */ 146 | success: function (app, code, cb) { 147 | var self = this; 148 | this.authorize(app, code, function (error, json) { 149 | if (error) { 150 | cb(true, json); 151 | return; 152 | } 153 | if (json.openid) { 154 | self.session = { 155 | openId: json.openid, 156 | accessToken: json.access_token, 157 | refreshToken: json.refresh_token 158 | }; 159 | if (cb) { 160 | cb(false, json); 161 | } 162 | return; 163 | } 164 | cb(true, json); 165 | }); 166 | } 167 | }; 168 | 169 | module.exports = oauth; 170 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-weixin-oauth", 3 | "version": "0.3.0", 4 | "description": "", 5 | "homepage": "", 6 | "author": { 7 | "name": "calidion", 8 | "email": "calidion@gmail.com", 9 | "url": "calidion.github.io" 10 | }, 11 | "files": [ 12 | "lib" 13 | ], 14 | "main": "lib/index.js", 15 | "keywords": [ 16 | "" 17 | ], 18 | "devDependencies": { 19 | "eslint-config-xo-space": "^0.7.0", 20 | "gulp": "^3.9.0", 21 | "gulp-coveralls": "^0.1.0", 22 | "gulp-eslint": "^1.0.0", 23 | "gulp-exclude-gitignore": "^1.0.0", 24 | "gulp-istanbul": "^0.10.3", 25 | "gulp-mocha": "^2.0.0", 26 | "gulp-nsp": "^2.1.0", 27 | "gulp-plumber": "^1.0.0", 28 | "nock": "^7.2.2", 29 | "node-weixin-config": "^0.2.0" 30 | }, 31 | "eslintConfig": { 32 | "extends": "xo-space", 33 | "env": { 34 | "mocha": true 35 | } 36 | }, 37 | "repository": "node-weixin/node-weixin-oauth", 38 | "scripts": { 39 | "prepublish": "gulp prepublish", 40 | "test": "gulp" 41 | }, 42 | "license": "Apache-2.0", 43 | "dependencies": { 44 | "node-weixin-request": "^0.4.0", 45 | "node-weixin-util": "^0.3.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint camelcase: [2, {properties: "never"}] */ 4 | 5 | var assert = require('assert'); 6 | 7 | var nwc = require('node-weixin-config'); 8 | 9 | var nodeWeixinOauth = require('../'); 10 | var app = { 11 | id: process.env.APP_ID, 12 | secret: process.env.APP_SECRET, 13 | token: process.env.APP_TOKEN 14 | }; 15 | var urls = { 16 | access: 'http://oauth.domain.com/weixin/access', 17 | redirect: 'http://oauth.domain.com/weixin/back', 18 | success: 'http://oauth.domain.com/weixin/success' 19 | }; 20 | 21 | nwc.app.init(app); 22 | nwc.urls.oauth.init(urls); 23 | 24 | describe('node-weixin-oauth node module', function () { 25 | it('should be able to build oauth url', function () { 26 | var params = { 27 | a: 'a', 28 | b: 'b', 29 | c: 123, 30 | 中国: 'nodd' 31 | }; 32 | var url = nodeWeixinOauth.buildUrl(params); 33 | var result = 'https://open.weixin.qq.com/connect/oauth2/authorize?a=a&b=b&c=123&%E4%B8%AD%E5%9B%BD=nodd#wechat_redirect'; 34 | console.log(url); 35 | console.log(result); 36 | assert.equal(true, url === result); 37 | }); 38 | 39 | it('should create oauth url ', function () { 40 | var url = nodeWeixinOauth.createURL(app.id, urls.redirect, 'init', 1, 1); 41 | var genUrl = 42 | 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + 43 | app.id + '&redirect_uri=http%3A%2F%2Foauth.domain.com%2Fweixin%2Fback&response_type=code&scope=snsapi_userinfo&state=init#wechat_redirect'; 44 | assert(genUrl === url); 45 | }); 46 | 47 | it('should be able to send a tokenize request', function (done) { 48 | var code = 'sosos'; 49 | var params = { 50 | appid: app.id, 51 | secret: app.secret, 52 | grant_type: 'authorization_code', 53 | code: code 54 | }; 55 | params.access_token = app.token; 56 | var nock = require('nock'); 57 | var url = 'https://api.weixin.qq.com'; 58 | // var query = util.toParam(params) + '#wechat_redirect'; 59 | nock(url) 60 | .post('/sns/oauth2/access_token') 61 | .query(params) 62 | .reply(200, params); 63 | nodeWeixinOauth.authorize(app, code, function (error, body) { 64 | assert.equal(true, !error); 65 | assert.equal(true, Boolean(body)); 66 | done(); 67 | }); 68 | }); 69 | 70 | it('should fail to send a tokenize request', function (done) { 71 | var code = 'sosos'; 72 | var params = { 73 | appid: app.id, 74 | secret: app.secret, 75 | grant_type: 'authorization_code', 76 | code: code 77 | }; 78 | params.access_token = app.token; 79 | var nock = require('nock'); 80 | var url = 'https://api.weixin.qq.com'; 81 | nock(url) 82 | .post('/sns/oauth2/access_token') 83 | .query(params) 84 | .reply(500, params); 85 | nodeWeixinOauth.authorize(app, code, function (error) { 86 | assert.equal(true, error); 87 | done(); 88 | }); 89 | }); 90 | 91 | it('should be able to refresh', function (done) { 92 | var refreshToken = 'hsosos'; 93 | var nock = require('nock'); 94 | var params = { 95 | appId: app.id, 96 | grant_type: 'refresh_token', 97 | refresh_token: refreshToken 98 | }; 99 | var url = 'https://api.weixin.qq.com'; 100 | 101 | nock(url) 102 | .post('/sns/oauth2/refresh_token') 103 | .query(params) 104 | .reply(200, params); 105 | nodeWeixinOauth.refresh(app.id, refreshToken, function (error, body) { 106 | assert.equal(true, !error); 107 | assert.equal(true, Boolean(body)); 108 | done(); 109 | }); 110 | }); 111 | 112 | it('should be able to request info', function (done) { 113 | var accessToken = 'hsosos'; 114 | var openId = 'aaaa'; 115 | var nock = require('nock'); 116 | var params = { 117 | access_token: accessToken, 118 | openid: openId, 119 | lang: 'zh_CN' 120 | }; 121 | var url = 'https://api.weixin.qq.com'; 122 | 123 | nock(url) 124 | .post('/sns/userinfo') 125 | .query(params) 126 | .reply(200, params); 127 | nodeWeixinOauth.profile(openId, accessToken, function (error, body) { 128 | assert.equal(true, !error); 129 | assert.equal(true, Boolean(body)); 130 | done(); 131 | }); 132 | }); 133 | 134 | it('should be able to profile a user info', function (done) { 135 | var accessToken = 'aa'; 136 | var openId = 'ssoos'; 137 | var nock = require('nock'); 138 | 139 | var params = { 140 | access_token: accessToken, 141 | openid: openId, 142 | lang: 'zh_CN' 143 | }; 144 | var url = 'https://api.weixin.qq.com'; 145 | 146 | nock(url) 147 | .post('/sns/userinfo') 148 | .query(params) 149 | .reply(200, params); 150 | nodeWeixinOauth.profile(openId, accessToken, function (error, body) { 151 | assert.equal(true, !error); 152 | assert.equal(true, Boolean(body)); 153 | done(); 154 | }); 155 | }); 156 | 157 | it('should be able to validate a token', function (done) { 158 | var accessToken = 'aa'; 159 | var openId = 'ssoos'; 160 | var nock = require('nock'); 161 | 162 | var params = { 163 | access_token: accessToken, 164 | openid: openId 165 | }; 166 | var url = 'https://api.weixin.qq.com'; 167 | 168 | nock(url) 169 | .post('/sns/auth') 170 | .query(params) 171 | .reply(200, { 172 | errcode: 1 173 | }); 174 | nodeWeixinOauth.validate(openId, accessToken, function (error) { 175 | assert.equal(true, !error); 176 | done(); 177 | }); 178 | }); 179 | 180 | it('should fail to validate a token', function (done) { 181 | var accessToken = 'aa'; 182 | var openId = 'ssoos'; 183 | var nock = require('nock'); 184 | 185 | var params = { 186 | access_token: accessToken, 187 | openid: openId 188 | }; 189 | var url = 'https://api.weixin.qq.com'; 190 | 191 | nock(url) 192 | .post('/sns/auth') 193 | .query(params) 194 | .reply(200, { 195 | errcode: 0 196 | }); 197 | nodeWeixinOauth.validate(openId, accessToken, function (error) { 198 | assert.equal(true, error); 199 | done(); 200 | }); 201 | }); 202 | 203 | it('should be able to handler success', function (done) { 204 | var code = 'aaa'; 205 | var nock = require('nock'); 206 | var params = { 207 | appid: app.id, 208 | secret: app.secret, 209 | grant_type: 'authorization_code', 210 | code: code, 211 | access_token: app.token 212 | }; 213 | var url = 'https://api.weixin.qq.com'; 214 | var reply = { 215 | openid: 'sofdso', 216 | access_token: 'sossoso', 217 | refresh_token: 'refresh_token' 218 | }; 219 | 220 | nock(url) 221 | .post('/sns/oauth2/access_token') 222 | .query(params) 223 | .reply(200, reply); 224 | nodeWeixinOauth.success(app, code, function (error, json) { 225 | assert.equal(true, !error); 226 | assert.equal(true, json.openid === reply.openid); 227 | assert.equal(true, json.access_token === reply.access_token); 228 | assert.equal(true, json.refresh_token === reply.refresh_token); 229 | done(); 230 | }); 231 | }); 232 | 233 | it('should fail to handler success', function (done) { 234 | var code = 'aaa'; 235 | var nock = require('nock'); 236 | var params = { 237 | appid: app.id, 238 | secret: app.secret, 239 | grant_type: 'authorization_code', 240 | code: code, 241 | access_token: app.token 242 | }; 243 | var url = 'https://api.weixin.qq.com'; 244 | var reply = { 245 | openid: 'sofdso', 246 | access_token: 'sossoso', 247 | refresh_token: 'refresh_token' 248 | }; 249 | 250 | nock(url) 251 | .post('/sns/oauth2/access_token') 252 | .query(params) 253 | .reply(500, reply); 254 | nodeWeixinOauth.success(app, code, function (error) { 255 | assert.equal(true, error); 256 | done(); 257 | }); 258 | }); 259 | 260 | it('should fail to handler success', function (done) { 261 | var code = 'aaa'; 262 | var nock = require('nock'); 263 | var params = { 264 | appid: app.id, 265 | secret: app.secret, 266 | grant_type: 'authorization_code', 267 | code: code, 268 | access_token: app.token 269 | }; 270 | var url = 'https://api.weixin.qq.com'; 271 | var reply = { 272 | access_token: 'sossoso', 273 | refresh_token: 'refresh_token' 274 | }; 275 | 276 | nock(url) 277 | .post('/sns/oauth2/access_token') 278 | .query(params) 279 | .reply(200, reply); 280 | nodeWeixinOauth.success(app, code, function (error) { 281 | assert.equal(true, error); 282 | done(); 283 | }); 284 | }); 285 | }); 286 | --------------------------------------------------------------------------------