diff --git a/class/User.js b/class/User.js index a4f8e867..ad013a71 100644 --- a/class/User.js +++ b/class/User.js @@ -37,7 +37,7 @@ export class User { return this._password; } getAccessToken() { - return this._acess_token; + return this._access_token; } getRefreshToken() { return this._refresh_token; @@ -48,37 +48,41 @@ export class User { let access_token = authorization.replace('Bearer ', ''); let userid = await this._redis.get('userid_for_' + access_token); - if (userid) { - this._userid = userid; - return true; + if (!userid) { + return false } - return false; + let refresh_token = await this._redis.get('refresh_token_for_' + userid) + if (refresh_token === access_token) { + return false + } + + this._userid = userid; + return true; } async loadByRefreshToken(refresh_token) { let userid = await this._redis.get('userid_for_' + refresh_token); - if (userid) { - this._userid = userid; - await this._generateTokens(); - return true; + + if (!userid) { + return false; } - return false; + let access_token = await this._redis.get('access_token_for_' + userid) + if (access_token === refresh_token) { + return false + } + + this._userid = userid; + await this._generateTokens(); + return true; } async create() { - let buffer = crypto.randomBytes(10); - let login = buffer.toString('hex'); - - buffer = crypto.randomBytes(10); - let password = buffer.toString('hex'); + this._login = this._generateDigest(); + this._password = this._generateDigest(); + this._userid = this._generateDigest(); - buffer = crypto.randomBytes(24); - let userid = buffer.toString('hex'); - this._login = login; - this._password = password; - this._userid = userid; await this._saveUserToDatabase(); } @@ -479,16 +483,49 @@ export class User { } async _generateTokens() { - let buffer = crypto.randomBytes(20); - this._acess_token = buffer.toString('hex'); + await this._invalidateCurrentTokens(); + + await this._generateAccessToken(); + await this._generateRefreshToken(); + } + + async _invalidateCurrentTokens() { + this._access_token = await this._redis.get('access_token_for_' + this._userid); + this._refresh_token = await this._redis.get('refresh_token_for_' + this._userid); + + await this._redis.del('access_token_for_' + this._userid); + await this._redis.del('refresh_token_for_' + this._userid); + await this._redis.del('userid_for_' + this._access_token); + await this._redis.del('userid_for_' + this._refresh_token); + + this._access_token = null + this._refresh_token = null + } + + async _generateAccessToken() { + this._access_token = this._generateDigest(); - buffer = crypto.randomBytes(20); - this._refresh_token = buffer.toString('hex'); + const key_UId_AT = 'userid_for_' + this._access_token; + const key_AT_UId = 'access_token_for_' + this._userid; - await this._redis.set('userid_for_' + this._acess_token, this._userid); - await this._redis.set('userid_for_' + this._refresh_token, this._userid); - await this._redis.set('access_token_for_' + this._userid, this._acess_token); - await this._redis.set('refresh_token_for_' + this._userid, this._refresh_token); + await this._redis.set(key_UId_AT, this._userid); + await this._redis.set(key_AT_UId, this._access_token); + + await this._redis.expire(key_UId_AT, accessTokenLifeTime); + await this._redis.expire(key_AT_UId, accessTokenLifeTime); + } + + async _generateRefreshToken() { + this._refresh_token = this._generateDigest(); + + const key_UId_RT = 'userid_for_' + this._refresh_token; + const key_RT_UId = 'refresh_token_for_' + this._userid; + + await this._redis.set(key_UId_RT, this._userid); + await this._redis.set(key_RT_UId, this._refresh_token); + + await this._redis.expire(key_UId_RT, refreshTokenLifeTime); + await this._redis.expire(key_RT_UId, refreshTokenLifeTime); } async _saveUserToDatabase() { @@ -496,6 +533,11 @@ export class User { await this._redis.set((key = 'user_' + this._login + '_' + this._hash(this._password)), this._userid); } + _generateDigest() { + const buffer = crypto.randomBytes(256); + return crypto.createHash('sha1').update(buffer).digest('hex'); + } + /** * Fetches all onchain txs for user's address, and compares them to * already imported txids (stored in database); Ones that are not imported - diff --git a/config.js b/config.js index 840a265a..7fa1e6d2 100644 --- a/config.js +++ b/config.js @@ -4,6 +4,10 @@ let config = { rateLimit: 200, forwardReserveFee: 0.01, // default 0.01 intraHubFee: 0.003, // default 0.003 + auth: { + accessTokenLifeTime: 3600, + refreshTokenLifeTime: 86400, + }, bitcoind: { rpc: 'http://login:password@1.1.1.1:8332/wallet/wallet.dat', }, diff --git a/controllers/api.js b/controllers/api.js index 2a582e63..2ff447a1 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -21,6 +21,8 @@ redis.monitor(function (err, monitor) { /** GLOBALS */ global.forwardFee = config.forwardReserveFee || 0.01; global.internalFee = config.intraHubFee || 0.003; +global.accessTokenLifeTime = config.auth?.accessTokenLifeTime || 3600; +global.refreshTokenLifeTime = config.auth?.refreshTokenLifeTime || 86400; /****** END SET FEES FROM CONFIG AT STARTUP ******/ let bitcoinclient = require('../bitcoin'); @@ -157,23 +159,28 @@ router.post('/create', postLimiter, async function (req, res) { router.post('/auth', postLimiter, async function (req, res) { logger.log('/auth', [req.id]); - if (!((req.body.login && req.body.password) || req.body.refresh_token)) return errorBadArguments(res); - let u = new User(redis, bitcoinclient, lightning); + const u = new User(redis, bitcoinclient, lightning); - if (req.body.refresh_token) { - // need to refresh token - if (await u.loadByRefreshToken(req.body.refresh_token)) { - res.send({ refresh_token: u.getRefreshToken(), access_token: u.getAccessToken() }); - } else { - return errorBadAuth(res); - } + let authenticated = false + if (req.body.login && req.body.password) { + authenticated = await u.loadByLoginAndPassword(req.body.login, req.body.password); + } else if (req.body.refresh_token) { + authenticated = await u.loadByRefreshToken(req.body.refresh_token) } else { - // need to authorize user - let result = await u.loadByLoginAndPassword(req.body.login, req.body.password); - if (result) res.send({ refresh_token: u.getRefreshToken(), access_token: u.getAccessToken() }); - else errorBadAuth(res); + return errorBadArguments(res); + } + + if (!authenticated) { + return errorBadAuth(res); } + + return res.send({ + token_type: 'bearer', + refresh_token: u.getRefreshToken(), + access_token: u.getAccessToken(), + expires_in: accessTokenLifeTime, + }); }); router.post('/addinvoice', postLimiter, async function (req, res) {