From 07450e6864972696bf720347dedfa5c2a1bd46f1 Mon Sep 17 00:00:00 2001 From: xialvjun Date: Fri, 19 Apr 2019 16:05:37 +0800 Subject: [PATCH] support miniprogram like wechat-oauth --- lib/oauth.js | 108 ++++++++++++++++++++++++++++++++++++++++++++++++--- package.json | 3 +- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/lib/oauth.js b/lib/oauth.js index ff57565..d0fa68e 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -4,6 +4,9 @@ const httpx = require('httpx'); const querystring = require('querystring'); + +const WxBizDataCrypt = require('wechat-oauth/lib/wx_biz_data_crypt'); + class AccessToken { constructor(data) { this.data = data; @@ -39,11 +42,13 @@ class AccessToken { * @param {String} appsecret 在公众平台上申请得到的app secret * @param {Generator} getToken 用于获取token的方法 * @param {Generator} saveToken 用于保存token的方法 + * @param {Boolean} isMiniProgram 是否是微信小程序 */ class OAuth { - constructor(appid, appsecret, getToken, saveToken) { + constructor(appid, appsecret, getToken, saveToken, isMiniProgram) { this.appid = appid; this.appsecret = appsecret; + this.isMiniProgram = isMiniProgram; // token的获取和存储 this.store = {}; this.getToken = getToken || async function (openid) { @@ -201,6 +206,47 @@ class OAuth { return this.processToken(data); } + /** + * 根据授权获取到的code,换取小程序的session key和openid(以及有条件下的unionid) + * 获取openid之后,可以调用`wechat.API`来获取更多信息 + * Examples: + * ``` + * await api.getSessionKey(code); + * ``` + * Exception: + * + * - `err`, 获取session key出现异常时的异常对象 + * + * 返回值: + * ``` + * { + * data: { + * "session_key": "SESSION_KEY", + * "openid": "OPENID", + * "unionid": "UNIONID" + * } + * } + * ``` + * @param {String} code 授权获取到的code + */ + async getSessionKey(code) { + var info = { + appid: this.appid, + secret: this.appsecret, + js_code: code, + grant_type: 'authorization_code', + }; + + var url = `https://api.weixin.qq.com/sns/jscode2session?${querystring.stringify(info)}`; + var data = await this.request(url, { + headers: { + accept: 'application/json' + } + }); + + return this.processToken(data); + } + /** * 根据refresh token,刷新access token,调用getAccessToken后才有效 * Examples: @@ -262,6 +308,39 @@ class OAuth { }); } + /** + * 根据服务器保存的sessionKey对从小程序客户端获取的加密用户数据进行解密 + * Examples: + * ``` + * api.decryptMiniProgramUser({encryptedData, iv}); + * ``` + * 返回值: + * ``` + *{ + * "openId": "OPENID", + * "nickName": "NICKNAME", + * "gender": "GENDER", + * "city": "CITY", + * "province": "PROVINCE", + * "country": "COUNTRY", + * "avatarUrl": "AVATARURL", + * "unionId": "UNIONID", + * "watermark": + * { + * "appid":"APPID", + * "timestamp":TIMESTAMP + * } + *} + * ``` + * @param {Object} options 需要解密的对象 + * @param {String} options.encryptedData 从小程序中获得的加密过的字符串 + * @param {String} options.iv 从小程序中获得的加密算法初始向量 + */ + decryptMiniProgramUser(options) { + var decrypter = new WxBizDataCrypt(this.appid, options.sessionKey); + return decrypter.decryptData(options.encryptedData, options.iv); + } + /** * 根据openid,获取用户信息。 * 当access token无效时,自动通过refresh token获取新的access token。然后再获取用户信息 @@ -368,11 +447,30 @@ class OAuth { * ] * } * ``` - * @param {String} code 授权获取到的code + * @param {Object|String} options 授权获取到的code */ - async getUserByCode(code) { - var token = await this.getAccessToken(code); - return this.getUser(token.data.openid); + async getUserByCode(options) { + var lang, code; + if (typeof options === 'string') { + code = options; + } else { + lang = options.lang; + code = options.code; + } + + if (this.isMiniProgram) { + var result = await this.getSessionKey(code); + var sessionKey = result.data.session_key; + return this.decryptMiniProgramUser({ + sessionKey, + encryptedData: options.encryptedData, + iv: options.iv, + }); + } else { + var result = this.getAccessToken(code); + var openid = result.data.openid; + return this.getUser({openid: openid, lang: lang}); + } } } diff --git a/package.json b/package.json index 9915556..f97c504 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "wechat" ], "dependencies": { - "httpx": "^2.1.1" + "httpx": "^2.1.1", + "wechat-oauth": "^1.5.0" }, "devDependencies": { "coveralls": "*",