mpauthx
Advanced tools
Comparing version 1.2.3 to 1.3.0
@@ -15,4 +15,6 @@ module.exports = { | ||
openIdOrUnionIdNotMatch: 6009, | ||
userNotExistInDB: 6010, | ||
userInfoDecryptionError: 6011, | ||
unknownError: 9999 | ||
} | ||
} |
124
index.js
@@ -160,3 +160,12 @@ const constants = require('./core/constants.js'); | ||
var pc = new WXBizDataCrypt(valproxy.appid, online_sessionKey); | ||
var data = pc.decryptData(encryptedData, iv); | ||
var data = null; | ||
// try to decrypt userInfo, if error then return now | ||
try { | ||
data = pc.decryptData(encryptedData, iv); | ||
} catch (err) { | ||
console.log('userInfo decryption error'); | ||
reject(util.createErrorObject(constants.statusCode.userInfoDecryptionError, 'Decrypt userInfo error')); | ||
return; | ||
} | ||
@@ -229,3 +238,3 @@ var offline_openIdOrUnionIdIfAvailable = data.openId; | ||
// set expire | ||
redisClient.expire(offline_openIdOrUnionIdIfAvailable, valproxy.tokenTTL); | ||
redisClient.expire(token, valproxy.tokenTTL); | ||
@@ -256,3 +265,3 @@ // respond back with generated token | ||
// set expire | ||
redisClient.expire(offline_openIdOrUnionIdIfAvailable, valproxy.tokenTTL); | ||
redisClient.expire(token, valproxy.tokenTTL); | ||
@@ -291,2 +300,108 @@ // respond back with generated token | ||
/** | ||
* Get a new access token. | ||
* | ||
* It will invalidate previous acquired access token for input userid (either openid or unionid) only for attached app then generate a new one. | ||
* | ||
* @param {String} userid User id either is openid or unionid | ||
* @return {Object} Promise object. Success contains a new access token string, failure contains Error object with code property for reason of why it fails. | ||
*/ | ||
function refreshToken(userId) { | ||
return new Promise((resolve, reject) => { | ||
// check required params, missing or not | ||
if (userId == null) { | ||
// reject with error object | ||
reject(util.createErrorObject(constants.statusCode.requiredParamsMissingError, 'Missing userId parameter')); | ||
// return immediately | ||
return; | ||
} | ||
// check in db first whether user exists | ||
db.all(`SELECT * FROM user WHERE openId LIKE '${userId}'`, function(e, rows) { | ||
if (e) { | ||
console.log(`error select redis: ${e.message}`); | ||
reject(util.createErrorObject(constants.statusCode.databaseRelatedError, `Error: ${e.message}`)); | ||
return; | ||
} | ||
else { | ||
// not exist, then return error | ||
if (rows == null || (rows != null && rows.length == 0)) { | ||
reject(util.createErrorObject(constants.statusCode.userNotExistInDB, 'Cannot find such user in DB')); | ||
return; | ||
} | ||
// exist, and it should only have 1 record | ||
else if (rows != null && rows.length == 1) { | ||
console.log('here'); | ||
// continue operation | ||
// find userid in redisdb, if found we will remove it to invalidate that token | ||
redisClient.keys(`${userId}|${valproxy.sku}|*`, function(err, replies) { | ||
// error | ||
if (err) { | ||
console.log('error 1', err); | ||
reject(util.createErrorObject(constants.statusCode.databaseRelatedError, `Error: ${err.message}`)); | ||
return; | ||
} | ||
console.log('replies: ', replies); | ||
// delete existing records if any | ||
if (replies.length > 0) { | ||
// remove all existing records, should have only 1 | ||
redisClient.del(replies, function(err, res) { | ||
// error | ||
if (err) { | ||
console.log('error 2-1', err); | ||
reject(util.createErrorObject(constants.statusCode.databaseRelatedError, `Error: ${err.message}`)); | ||
return; | ||
} | ||
// if done, then generate a new access token | ||
let timestamp = Date.now(); | ||
let token = generateToken(userId, timestamp); | ||
redisClient.hmset(token, { ctime: timestamp }, function(e, reply) { | ||
if (e) { | ||
console.log('error 3-1', err); | ||
reject(util.createErrorObject(constants.statusCode.databaseRelatedError, `Error: ${e.message}`)); | ||
return; | ||
} | ||
console.log('ok now 1: ' + token); | ||
// set TTL | ||
redisClient.expire(token, valproxy.tokenTTL); | ||
resolve(util.createSuccessObject(constants.statusCode.success, 'OK', token)); | ||
return; | ||
}); | ||
}); | ||
} | ||
// generate | ||
else { | ||
// if done, then generate a new access token | ||
let timestamp = Date.now(); | ||
let token = generateToken(userId, timestamp); | ||
redisClient.hmset(token, { ctime: timestamp }, function(e, reply) { | ||
if (e) { | ||
console.log('error 3-2', err); | ||
reject(util.createErrorObject(constants.statusCode.databaseRelatedError, `Error: ${e.message}`)); | ||
return; | ||
} | ||
console.log('ok now 2: ' + token); | ||
// set TTL | ||
redisClient.expire(token, valproxy.tokenTTL); | ||
resolve(util.createSuccessObject(constants.statusCode.success, 'OK', token)); | ||
return; | ||
}); | ||
} | ||
}); | ||
} | ||
// should not happen | ||
else { | ||
reject(util.createErrorObject(constants.statusCode.unknownError, 'Unknown error after finding user record from db')); | ||
return; | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Close DB connection. | ||
@@ -330,3 +445,4 @@ */ | ||
isTokenValid: isTokenValid, | ||
authorize: authorize, | ||
authorize: authorize, | ||
refreshToken: refreshToken, | ||
extractOpenId: extractOpenId, | ||
@@ -333,0 +449,0 @@ close: close, |
{ | ||
"name": "mpauthx", | ||
"version": "1.2.3", | ||
"version": "1.3.0", | ||
"description": "Token giver for Users logged in to WeChat Mini-program. Based on top of redis for fast token checking/access, and sqlite3 for flexible user db storage.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -5,2 +5,9 @@ # mpauthx | ||
# Features | ||
* Generate and assign an access token to each user for attached project (`sku`) supporting multiple devices | ||
* Provide functionality to initially and firstly register user via our user database (sqlite) via `authorize(code, encryptedData, iv)` function | ||
* Provide functionality to refresh access token only if user knows its own user id (either openId or unionId) via `refreshToken()` function | ||
* Each access token comes with configurabe TTL (time-to-live) thus when it's expired, such token will be deleted from redis db automatically. | ||
# How-To | ||
@@ -31,4 +38,6 @@ | ||
Call `mpauthx.authorize(code, encryptedData, iv);` whenever your end-point needs to authorize WeChat user and give user a token so user can save such token for subsequent API calls later in the future. | ||
Call `mpauthx.authorize(code, encryptedData, iv)` whenever your end-point needs to authorize WeChat user and give user a token so user can save such token for subsequent API calls later in the future. | ||
Call `mpauthx.refreshToken(userId)` whenever you want to refresh token. If previously assigned token to such user exists, then it will be invalidated before generating and assigning new one. Client side should persist such token value and make use of it first to see if it is still not expired. | ||
as well | ||
@@ -60,2 +69,6 @@ | ||
## OpenId & UnionId | ||
Also `openId` is umbrella word to represent either openid or unionid. If your app has been setup to work with unionid, then mpauthx will automatically use that instead of openid. This will allow you to peek database for users related to all apps across your company's WeChat Official Account. | ||
# API | ||
@@ -76,3 +89,3 @@ | ||
{ | ||
status_code: <number>, // see core/constants.js or mpauthx.constants for all statuses | ||
status_code: <number>, // see core/constants.js or mpauthx.constants for all statuses | ||
status_message: <string>, | ||
@@ -92,2 +105,3 @@ response: <string> // returned generated token for such user | ||
Return openId part of specified token. | ||
Note that openId is umbrella word to represent either openId or unionId. See OpenId & UnionId. | ||
@@ -110,2 +124,2 @@ * `extractSku(token)` - extract sku part of specified token | ||
[Apache License 2.0](https://github.com/abzico/mpauthx/blob/master/LICENSE), [abzi.co](https://abzi.co) | ||
[Apache License 2.0](https://github.com/abzico/mpauthx/blob/master/LICENSE), [abzi.co](https://abzi.co) |
45253
730
120