mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-21 16:52:03 -07:00
feat(JWT): Added automatic JWT handling for FullStack.cash
This commit is contained in:
+8
-2
@@ -69,8 +69,14 @@ async function startServer () {
|
|||||||
console.log(`Server started on ${config.port}`)
|
console.log(`Server started on ${config.port}`)
|
||||||
|
|
||||||
// Create the system admin user.
|
// Create the system admin user.
|
||||||
const success = await adminLib.createSystemUser()
|
try {
|
||||||
if (success) console.log('System admin user created.')
|
const success = await adminLib.createSystemUser()
|
||||||
|
if (success) console.log('System admin user created.')
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(
|
||||||
|
'Error trying to create system admin. Perhaps one already exists?'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+12
@@ -23,6 +23,18 @@ module.exports = {
|
|||||||
? process.env.EMAILPASS
|
? process.env.EMAILPASS
|
||||||
: 'emailpassword',
|
: 'emailpassword',
|
||||||
|
|
||||||
|
// FullStack.cash account information, used for automatic JWT handling.
|
||||||
|
AUTHSERVER: process.env.AUTHSERVER
|
||||||
|
? process.env.AUTHSERVER
|
||||||
|
: 'https://auth.fullstack.cash',
|
||||||
|
APISERVER: process.env.APISERVER
|
||||||
|
? process.env.APISERVER
|
||||||
|
: 'https://api.fullstack.cash/v5/',
|
||||||
|
FULLSTACKLOGIN: process.env.FULLSTACKLOGIN
|
||||||
|
? process.env.FULLSTACKLOGIN
|
||||||
|
: 'demo@demo.com',
|
||||||
|
FULLSTACKPASS: process.env.FULLSTACKPASS ? process.env.FULLSTACKPASS : 'demo',
|
||||||
|
|
||||||
// IPFS settings.
|
// IPFS settings.
|
||||||
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
|
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
|
||||||
|
|
||||||
|
|||||||
Generated
+17
@@ -16,6 +16,7 @@
|
|||||||
"ipfs-coord": "^3.2.0",
|
"ipfs-coord": "^3.2.0",
|
||||||
"jsonrpc-lite": "^2.2.0",
|
"jsonrpc-lite": "^2.2.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"jwt-bch-lib": "^1.3.0",
|
||||||
"kcors": "^2.2.2",
|
"kcors": "^2.2.2",
|
||||||
"koa": "^2.13.1",
|
"koa": "^2.13.1",
|
||||||
"koa-bodyparser": "^4.3.0",
|
"koa-bodyparser": "^4.3.0",
|
||||||
@@ -13576,6 +13577,14 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jwt-bch-lib": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-bch-lib/-/jwt-bch-lib-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-CZs3jUHJlWvConvoTeLDogDEwUysKeojYJxfUhbwJKJrtDCSfi3ZWbsZZ8WN30Xs28aZPDB+qlHPJlTXQmup0w==",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/k-bucket": {
|
"node_modules/k-bucket": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz",
|
||||||
@@ -37694,6 +37703,14 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jwt-bch-lib": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-bch-lib/-/jwt-bch-lib-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-CZs3jUHJlWvConvoTeLDogDEwUysKeojYJxfUhbwJKJrtDCSfi3ZWbsZZ8WN30Xs28aZPDB+qlHPJlTXQmup0w==",
|
||||||
|
"requires": {
|
||||||
|
"axios": "^0.21.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"k-bucket": {
|
"k-bucket": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"ipfs-coord": "^3.2.0",
|
"ipfs-coord": "^3.2.0",
|
||||||
"jsonrpc-lite": "^2.2.0",
|
"jsonrpc-lite": "^2.2.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"jwt-bch-lib": "^1.3.0",
|
||||||
"kcors": "^2.2.2",
|
"kcors": "^2.2.2",
|
||||||
"koa": "^2.13.1",
|
"koa": "^2.13.1",
|
||||||
"koa-bodyparser": "^4.3.0",
|
"koa-bodyparser": "^4.3.0",
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
A library of utility functions for working with FullStack.cash JWT tokens.
|
||||||
|
|
||||||
|
Feel free to copy this library into your own app, as well as the unit tests
|
||||||
|
for this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const JwtLib = require('jwt-bch-lib')
|
||||||
|
const BCHJS = require('@psf/bch-js')
|
||||||
|
|
||||||
|
class FullStackJWT {
|
||||||
|
constructor (localConfig = {}) {
|
||||||
|
// Input Validation
|
||||||
|
this.authServer = localConfig.authServer
|
||||||
|
if (!this.authServer || typeof this.authServer !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
'Must pass a url for the AUTH server when instantiating FullStackJWT class.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.apiServer = localConfig.apiServer
|
||||||
|
if (!this.apiServer || typeof this.apiServer !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
'Must pass a url for the API server when instantiating FullStackJWT class.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.login = localConfig.login
|
||||||
|
if (!this.login || typeof this.login !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
'Must pass a FullStack.cash login (email) instantiating FullStackJWT class.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.password = localConfig.password
|
||||||
|
if (!this.password || typeof this.password !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
'Must pass a FullStack.cash account password when instantiating FullStackJWT class.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulate dependencies
|
||||||
|
this.jwtLib = new JwtLib({
|
||||||
|
// Overwrite default values with the values in the config file.
|
||||||
|
server: this.server,
|
||||||
|
login: this.login,
|
||||||
|
password: this.password
|
||||||
|
})
|
||||||
|
|
||||||
|
// State
|
||||||
|
this.apiToken = '' // Default value.
|
||||||
|
this.bchjs = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get's a JWT token from FullStack.cash.
|
||||||
|
async getJWT () {
|
||||||
|
try {
|
||||||
|
// Log into the auth server.
|
||||||
|
await this.jwtLib.register()
|
||||||
|
|
||||||
|
this.apiToken = this.jwtLib.userData.apiToken
|
||||||
|
if (!this.apiToken) {
|
||||||
|
throw new Error('This account does not have a JWT')
|
||||||
|
}
|
||||||
|
console.log(`Retrieved JWT token: ${this.apiToken}\n`)
|
||||||
|
|
||||||
|
// Ensure the JWT token is valid to use.
|
||||||
|
const isValid = await this.jwtLib.validateApiToken()
|
||||||
|
|
||||||
|
// Get a new token with the same API level, if the existing token is not
|
||||||
|
// valid (probably expired).
|
||||||
|
if (!isValid.isValid) {
|
||||||
|
this.apiToken = await this.jwtLib.getApiToken(
|
||||||
|
this.jwtLib.userData.apiLevel
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
`The JWT token was not valid. Retrieved new JWT token: ${this.apiToken}\n`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
console.log('JWT token is valid.\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.apiToken
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`Error trying to log into ${this.server} and retrieve JWT token.`
|
||||||
|
)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an instance of bchjs with the validated JWT token. Returns this
|
||||||
|
// instance of bch-js.
|
||||||
|
instanceBchjs () {
|
||||||
|
try {
|
||||||
|
this.bchjs = new BCHJS({
|
||||||
|
restURL: this.apiServer,
|
||||||
|
apiToken: this.apiToken
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.bchjs
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in instanceBchjs()')
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = FullStackJWT
|
||||||
+16
-1
@@ -4,6 +4,9 @@
|
|||||||
https://troutsblog.com/blog/clean-architecture
|
https://troutsblog.com/blog/clean-architecture
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Load the config file
|
||||||
|
const config = require('../../config')
|
||||||
|
|
||||||
// Load individual adapter libraries.
|
// Load individual adapter libraries.
|
||||||
const IPFSAdapter = require('./ipfs')
|
const IPFSAdapter = require('./ipfs')
|
||||||
const LocalDB = require('./localdb')
|
const LocalDB = require('./localdb')
|
||||||
@@ -12,6 +15,7 @@ const Passport = require('./passport')
|
|||||||
const Nodemailer = require('./nodemailer')
|
const Nodemailer = require('./nodemailer')
|
||||||
const { wlogger } = require('./wlogger')
|
const { wlogger } = require('./wlogger')
|
||||||
const JSONFiles = require('./json-files')
|
const JSONFiles = require('./json-files')
|
||||||
|
const FullStackJWT = require('./fullstack-jwt')
|
||||||
|
|
||||||
// Instantiate adapter libraries.
|
// Instantiate adapter libraries.
|
||||||
const ipfs = new IPFSAdapter()
|
const ipfs = new IPFSAdapter()
|
||||||
@@ -21,6 +25,15 @@ const passport = new Passport()
|
|||||||
const nodemailer = new Nodemailer()
|
const nodemailer = new Nodemailer()
|
||||||
const jsonFiles = new JSONFiles()
|
const jsonFiles = new JSONFiles()
|
||||||
|
|
||||||
|
// Get a valid JWT API key and instance bch-js.
|
||||||
|
const fullStackJwt = new FullStackJWT({
|
||||||
|
authServer: config.AUTHSERVER,
|
||||||
|
apiServer: config.APISERVER,
|
||||||
|
login: config.FULLSTACKLOGIN,
|
||||||
|
password: config.FULLSTACKPASS
|
||||||
|
})
|
||||||
|
const bchjs = {} // Placeholder.
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ipfs,
|
ipfs,
|
||||||
localdb,
|
localdb,
|
||||||
@@ -28,5 +41,7 @@ module.exports = {
|
|||||||
passport,
|
passport,
|
||||||
nodemailer,
|
nodemailer,
|
||||||
wlogger,
|
wlogger,
|
||||||
jsonFiles
|
jsonFiles,
|
||||||
|
fullStackJwt,
|
||||||
|
bchjs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,15 @@ class IPFS {
|
|||||||
|
|
||||||
// Provides a global start() function that triggers the start() function in
|
// Provides a global start() function that triggers the start() function in
|
||||||
// the underlying libraries.
|
// the underlying libraries.
|
||||||
async start () {
|
async start (localConfig = {}) {
|
||||||
try {
|
try {
|
||||||
|
const bchjs = localConfig.bchjs
|
||||||
|
if (!bchjs) {
|
||||||
|
throw new Error(
|
||||||
|
'Instance of bch-js must be passed when instantiating IPFS adapter.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Start IPFS
|
// Start IPFS
|
||||||
await this.ipfsAdapter.start()
|
await this.ipfsAdapter.start()
|
||||||
console.log('IPFS is ready.')
|
console.log('IPFS is ready.')
|
||||||
@@ -30,7 +37,8 @@ class IPFS {
|
|||||||
|
|
||||||
// Start ipfs-coord
|
// Start ipfs-coord
|
||||||
this.ipfsCoordAdapter = new this.IpfsCoordAdapter({
|
this.ipfsCoordAdapter = new this.IpfsCoordAdapter({
|
||||||
ipfs: this.ipfs
|
ipfs: this.ipfs,
|
||||||
|
bchjs
|
||||||
})
|
})
|
||||||
await this.ipfsCoordAdapter.start()
|
await this.ipfsCoordAdapter.start()
|
||||||
console.log('ipfs-coord is ready.')
|
console.log('ipfs-coord is ready.')
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
// Global npm libraries
|
// Global npm libraries
|
||||||
const IpfsCoord = require('ipfs-coord')
|
const IpfsCoord = require('ipfs-coord')
|
||||||
const BCHJS = require('@psf/bch-js')
|
// const BCHJS = require('@psf/bch-js')
|
||||||
|
|
||||||
// Local libraries
|
// Local libraries
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
@@ -23,11 +23,16 @@ class IpfsCoordAdapter {
|
|||||||
'Instance of IPFS must be passed when instantiating ipfs-coord.'
|
'Instance of IPFS must be passed when instantiating ipfs-coord.'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
this.bchjs = localConfig.bchjs
|
||||||
|
if (!this.bchjs) {
|
||||||
|
throw new Error(
|
||||||
|
'Instance of bch-js must be passed when instantiating ipfs-coord.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Encapsulate dependencies
|
// Encapsulate dependencies
|
||||||
this.IpfsCoord = IpfsCoord
|
this.IpfsCoord = IpfsCoord
|
||||||
this.ipfsCoord = {}
|
this.ipfsCoord = {}
|
||||||
this.bchjs = new BCHJS()
|
|
||||||
// this.rpc = new JSONRPC()
|
// this.rpc = new JSONRPC()
|
||||||
this.config = config
|
this.config = config
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,24 @@ const RESTControllers = require('./rest-api')
|
|||||||
// Top-level function for this library.
|
// Top-level function for this library.
|
||||||
// Start the various Controllers and attach them to the app.
|
// Start the various Controllers and attach them to the app.
|
||||||
async function attachControllers (app) {
|
async function attachControllers (app) {
|
||||||
// Attach the REST controllers to the Koa app.
|
try {
|
||||||
attachRESTControllers(app)
|
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
||||||
|
// to all the rest of the apps controllers and adapters.
|
||||||
|
await adapters.fullStackJwt.getJWT()
|
||||||
|
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||||
|
adapters.bchjs = await adapters.fullStackJwt.instanceBchjs()
|
||||||
|
|
||||||
// Start IPFS.
|
// Attach the REST controllers to the Koa app.
|
||||||
await adapters.ipfs.start()
|
attachRESTControllers(app)
|
||||||
|
|
||||||
attachRPCControllers()
|
// Start IPFS.
|
||||||
|
await adapters.ipfs.start({ bchjs: adapters.bchjs })
|
||||||
|
|
||||||
|
attachRPCControllers()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in attachControllers()')
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachRESTControllers (app) {
|
function attachRESTControllers (app) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// Public npm libraries
|
// Public npm libraries
|
||||||
const jsonrpc = require('jsonrpc-lite')
|
const jsonrpc = require('jsonrpc-lite')
|
||||||
const BCHJS = require('@psf/bch-js')
|
// const BCHJS = require('@psf/bch-js')
|
||||||
|
|
||||||
// Local libraries
|
// Local libraries
|
||||||
// const UserLib = require('../../../use-cases/user')
|
// const UserLib = require('../../../use-cases/user')
|
||||||
@@ -32,7 +32,8 @@ class BCHRPC {
|
|||||||
this.jsonrpc = jsonrpc
|
this.jsonrpc = jsonrpc
|
||||||
this.validators = new Validators(localConfig)
|
this.validators = new Validators(localConfig)
|
||||||
this.rateLimit = new RateLimit()
|
this.rateLimit = new RateLimit()
|
||||||
this.bchjs = new BCHJS()
|
// this.bchjs = new BCHJS()
|
||||||
|
this.bchjs = this.adapters.bchjs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top-level router for this library. All other methods in this class are for
|
// Top-level router for this library. All other methods in this class are for
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Controller for the /fulcrum REST API endpoints.
|
Controller for the /fulcrum REST API endpoints.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const BCHJS = require('@psf/bch-js')
|
// const BCHJS = require('@psf/bch-js')
|
||||||
|
|
||||||
let _this
|
let _this
|
||||||
|
|
||||||
@@ -22,7 +22,8 @@ class BCHRESTController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bchjs = new BCHJS()
|
// this.bchjs = new BCHJS()
|
||||||
|
this.bchjs = this.adapters.bchjs
|
||||||
|
|
||||||
_this = this
|
_this = this
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user