f62447cc26346bee50ed474f45d6e758d919951bb0650be58f4e560ebda1a3e0
{
  "bitpost": "article",
  "data": "Reference documentation can be found here:\nhttps://docs.moneybutton.com/docs/api/api-overview.html\nhttps://expressjs.com/\nhttps://nodejs.org/en/docs/\nhttps://ejs.co/\nhttp://www.passportjs.org/\nhttps://mongoosejs.com/docs/guide.html\nhttps://github.com/winstonjs/winston\n\nThe finished project is hosted here: https://github.com/big-riz/mbtokens\n\nYou might want to use MongoDB Compass for hosting your local database. https://www.mongodb.com/try/download/compass\n\nThis is a tutorial for a NodeJS express website rendering pages with ejs. We will use passport for managing user logins.\nOur database for storing users will be MongoDB using mongoose to connect.\nWinston is for logging.\n\nFirst, we need to initialize the project. Make sure Node is installed (check the reference documentation for install instructions). Open your project's directory in the terminal and enter \"npm init\". This will create your package.json file.\n\nNow you can install the required dependencies.\nThe command is \"npm install express express-session ejs mongoose passport passport-local-mongoose winston\"\n\nLet's start by creating our project entry point:\n# index.js\n\nImporting required dependencies\n```javascript\nconst fs = require('fs')\nconst express = require(\"express\")\nconst mongoose = require(\"mongoose\")\nconst passport = require(\"passport\")\nconst bodyParser = require(\"body-parser\")\nconst LocalStrategy = require(\"passport-local\")\nconst passportLocalMongoose = require(\"passport-local-mongoose\")\nconst User = require(\"./models/user\")\nconst https = require('https')\nconst winston = require(\"winston\")\n```\n\nInitializing the winston logger\n```javascript\nconst logger = winston.createLogger({\n  level: 'info',\n  format: winston.format.json(),\n  defaultMeta: { service: 'user-service' },\n  transports: [\n    new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),\n    new winston.transports.File({ filename: 'logs/combined.log' }),\n  ],\n})\n\n//\n// If we're not in production then log to the `console` with the format:\n// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `\n//\nif (process.env.NODE_ENV !== 'production') {\n  logger.add(new winston.transports.Console({\n    format: winston.format.simple(),\n  }))\n}\n\nfunction logRequest(req, res, next) {\n    logger.log(\"info\", `Request ${req.ip} ${req.method} ${req.path}`)\n    next()\n}\n```\n\nSetting SSL credentials (https://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl)\n```javascript\nvar credentials = {key: fs.readFileSync(\"./keys/private-key.pem\"), cert: fs.readFileSync(\"./keys/public-cert.pem\")}\n```\n\nSetting mongoose parameters and connecting to the database\n```javascript\nmongoose.set('useNewUrlParser', true)\nmongoose.set('useFindAndModify', false)\nmongoose.set('useCreateIndex', true)\nmongoose.set('useUnifiedTopology', true)\nmongoose.connect(\"mongodb://localhost:27017/mbtokens\")\n```\n\nSetting express middleware\n```javascript\nvar app = express()\napp.use(bodyParser.urlencoded({ extended: true }))\napp.set(\"view engine\", \"ejs\")\napp.use( express.static( \"public\" ) )\napp.use(require(\"express-session\")({ \n    secret: \"topsecret.pleasechangethis\",\n    resave: false, \n    saveUninitialized: false\n}))\napp.use(passport.initialize())\napp.use(passport.session()) \napp.use(logRequest)\n```\n\nSetting passport stuff\n```javascript\npassport.use(new LocalStrategy(User.authenticate())); \npassport.serializeUser(User.serializeUser()); \npassport.deserializeUser(User.deserializeUser()); \n```\n\nImport required routes... and finally, create the server and listen on port 3001\n```javascript\nvar routes = require(\"./routes\")\nroutes(app, passport)\n\nvar httpsServer = https.createServer(app);\n\nhttpsServer.listen(3001);\n```\n\nBefore this will work, we need to create routes.js and /models/user.js. \nNow we can create our second file:\n## routes.js\n\nImporting required dependencies\n```javascript\nvar User = require(\"./models/user\")\nvar crypto = require('crypto')\nconst https = require('https')\n```\n\nSmall function to check whether user is authenticated\n```javascript\nfunction isLoggedIn(req, res, next) { \n    if (req.isAuthenticated()) return next(); \n    res.redirect(\"/login\"); \n} \n```\n\nSet your Money Button Oauth Client Id(https://www.moneybutton.com/settings/apps/create )(Instead of hard-coding this, you can use a .env file)\n```javascript\nMB_CLIENT_OAUTH_ID = \"xxxxxxxxx\"\n```\n\nThe routes\n```javascript\nmodule.exports = function (app, passport)\t{\n\tsecurestrings = {}\n\t\n  app.get(\"/\", async function (req, res) { \n      res.render(\"index\", {user: req.user})\n    })\n  \n  // more routes will be added here...\n  \n }\n```\n\nTwo more files, then we can test it.\n\n# /models/user.js\n\nThis is just the schema for our database object. (Passport will add in the username and password fields)\n```javascript\nconst mongoose = require('mongoose')\nconst Schema = mongoose.Schema\nconst passportLocalMongoose = require('passport-local-mongoose')\n\nconst User = new Schema({\n\tinitTime: Date,\n\tmoneybuttonPaymail: String,\n\tmoneybuttonId: Number,\n\taccessToken: String\n})\n\nUser.plugin(passportLocalMongoose)\n\nmodule.exports = mongoose.model('User', User)\n```\n\n# /views/index.ejs\n\nThis is the rendering template for our index/home page. For now, it's just a link to the login page (which we haven't added yet)\n```javascript\n<html>\n<head>\n</head>\n<body>\n<a href=\"/login\">Login</a>\n</body>\n</html>\n```\n\nCongrats! You have added all of the files we will need to test that this thing can run. Hello world!\n\nTo test it, go back to your terminal and type in \"node index.js\".\nIf that doesn't give you an error, go to your web browser and type in \"https://localhost:3001\".\nYou should be directed to your new index page with the \"Login\" link.\n\nIf you get an error, you might be missing your SSL keys. Make sure /keys/private-key.pem and /keys/public-cert.pem are both there in the keys folder. You will need to self-sign your certificate (https://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl)\n\nLet's make that login page.\nIn routes.js, underneath your first app.get(), you can add a second app.get() like the one below:\n# routes.js\n```javascript\napp.get(\"/login\", function (req, res) { \n\t\trandomsecurestring = String(Date.now())+\".\"+crypto.randomBytes(32).toString('hex')\n\t\tsecurestrings[String(req.ip)+String(Date.now())] = randomsecurestring\n\t\t\n\t\tres.render(\"login\", {randomsecurestring: randomsecurestring, oauthid: MB_CLIENT_OAUTH_ID})\n\t}); \n```\nNote that this renders the \"login\" page, so we'll need to add a login.ejs file to our views.\n\n# /views/login.ejs\n```javascript\n<html>\n<head>\n<meta charset=\"UTF-8\"/> \n</head>\n<body>\n<a href=\"https://www.moneybutton.com/oauth/v1/authorize?response_type=code&client_id=<%= oauthid %>&redirect_uri=https://localhost:3001/moneybuttonredirect&scope=auth.user_identity:read users.profiles:read users.asset:write users.asset:read&state=<%= randomsecurestring %>\">Login with Money Button </a>\n</body>\n</html>\n```\nThis adds in the first step in our oauth process. When the user clicks that link, they will be redirected to Money Button's website and asked to login.\nThen, they will be redirected back to our website (https://localhost:3001/moneybuttonredirect) with an access code.\n\nNow we need to make the route for /moneybuttonredirect.\n# routes.js\n```javascript\napp.get(\"/moneybuttonredirect\", async function (req, res) { \n\t\tres.render(\"oauthredirect\", {oauthid: MB_CLIENT_OAUTH_ID})\n\t})\n```\n\n...and the render template for \"oauthredirect\"\n# /views/oauthredirect.ejs\n```javascript\n<html>\n<head>\n<meta charset=\"UTF-8\"/> \n<script src=\"https://code.jquery.com/jquery-3.5.1.min.js\"></script>\n<script>\n$(function() {\n\tconst urlParams = new URLSearchParams(window.location.search);\n\tconst myParam = urlParams.get('code');\n\n\tconst secret = urlParams.get('state')\n\n\tif (myParam){\n\t\t$.post(\n\t\t\t\t\"https://www.moneybutton.com/oauth/v1/token\",\n\t\t\t\t{grant_type: \"authorization_code\", client_id: \"<%= oauthid %>\", code: myParam, redirect_uri: \"https://localhost:3001/moneybuttonredirect\"},\n\t\t\t\tfunction (data) {\n\t\t\t\t\tconst access_token = data.access_token\n\t\t\t\t\tvar mb_id = null\n\t\t\t\t\tconsole.log(data)\n\t\t\t\t\t$.ajaxSetup({\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"authorization\": \"Bearer \"+access_token\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\t$.get(\"https://www.moneybutton.com/api/v1/auth/user_identity\", function(data){\n\t\t\t\t\t\tconsole.log(data)\n\t\t\t\t\t\tmb_id = data.data.id\n\t\t\t\t\t\t$.get(\"https://www.moneybutton.com/api/v1/users/\"+mb_id.toString()+\"/profile\", function(data){\n\t\t\t\t\t\tconsole.log(data)\n\t\t\t\t\t\t\tdocument.getElementById(\"paymail\").value = data.data.attributes[\"primary-paymail\"]\n\t\t\t\t\t\t\tdocument.getElementById(\"username\").value = mb_id\n\t\t\t\t\t\t\tdocument.getElementById(\"secret\").value = secret\n\t\t\t\t\t\t\tdocument.getElementById(\"accessToken\").value = access_token\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t)\n\t}\n})\n</script>\n</head>\n<body>\n\n<form action=\"/registermoneybutton\" method=\"post\">\n<input type=\"password\" name=\"password\" placeholder=\"password\" class=\"form-control\" id=\"inputPassword\">\n<input type=\"hidden\" value=\"\" name=\"paymail\" id=\"paymail\">\n<input type=\"hidden\" value=\"\" name=\"username\" id=\"username\">\n<input type=\"hidden\" value=\"\" name=\"secret\" id=\"secret\">\n<input type=\"hidden\" value=\"\" name=\"accessToken\" id=\"accessToken\">\n<button type=\"submit\">Submit</button>\n</form>\n\n</body>\n</html>\n```\nThis page will receive the code from the oauth and use it in a request for the access token. Once it receives the access token, it will send another request for the moneybutton user's id. Then it will send a third request for the primary paymail of the user. It stores the access token, the paymail, and the moneybutton id (as the username) as hidden values in the form. The final value in the form - the password - must be entered by the user. When the user clicks submit, the form data is sent via POST request to localhost:3001/registermoneybutton. Next, we will add the route for that URL.\n\n# routes.js\n```javascript\napp.post(\"/registermoneybutton\", async function(req, res) {\n\t\tvar password = req.body.password \n\t\tif (securestrings[String(req.ip)+String(req.body.secret.split('.')[0])] == req.body.secret){\n\t\t\tcurrentUser = await User.findOne({username: req.body.username}).exec()\n\t\t\tif (currentUser != undefined){\n\t\t\t\t\n\t\t\t\tcurrentUser.accessToken = req.body.accessToken\n\t\t\t\tcurrentUser.moneybuttonPaymail = req.body.paymail\n\t\t\t\tcurrentUser.save()\n\t\t\t\tpassport.authenticate(\"local\")( \n\t\t\t\t\t\t\t\t\t\treq, res, function () {\n\t\t\t\t\t\t\t\t\t\t\tres.redirect(\"/?success=true\")\n\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\n\t\t\t}else{\n\t\t\t\n\t\t\t\tUser.register(new User({ username: req.body.username, moneybuttonPaymail: req.body.paymail, moneybuttonId: parseInt(req.body.username), accessToken: req.body.accessToken}), \n\t\t\t\t\t\t\t  password,\n\t\t\t\t\t\t\t  function (err, user) { \n\t\t\t\t\t\t\t\t\tif (err) { \n\t\t\t\t\t\t\t\t\t\tconsole.log(err); \n\t\t\t\t\t\t\t\t\t\treturn res.redirect(\"/?failure=true\"); \n\t\t\t\t\t\t\t\t\t} \n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tpassport.authenticate(\"local\")( \n\t\t\t\t\t\t\t\t\t\treq, res, function () {\n\t\t\t\t\t\t\t\t\t\t\tres.redirect(\"/?success=true\")\n\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t); \n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t ); \n\t\t\t}\n\t\t\n\t\t}\n\t})\n```\nFirst, we check if the secure string matches the one we generated. This is for security. We then check if the user is stored in the database. If it is, we update the access token and paymail of the user and .save(). Now we can authenticate the user with passport. (It just reads the req.body.username and req.body.password). If the user is not already in the database, we register a new user and save the access token etc. Then we can authenticate.\n\nYou can test this by restarting your server (ctrl+c in terminal to quit process, then \"node index.js\" to restart). Go to https://localhost:3001/login\nand go through the process. If you end up with /?success=true in the URL bar, you've successfully registered.\n\nOk now here's the fun stuff.\n# index.ejs\n```javascript\n<html>\n<head>\n<meta charset=\"UTF-8\"/> \n</head>\n<body>\n<a href=\"/login\">Login</a>\n<% if (user != undefined) { %>\n<a href=\"/token/create\">Mint Token</a>\n<% } %>\n</body>\n</html>\n```\nIn EJS, we check if the user is logged in. If they are, we show them the /token/create link.\n\nNow add the route...\n# routes.js\n```javascript\n\tapp.get(\"/token/create\", async function(req, res){\n\t\t\n\t\tres.render(\"create\", {user: req.user})\n\t})\n```\n\nAnd the template...\n# /views/create.ejs\n```javascript\n<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<form action=\"/token/create\" method=\"post\" >\n<input name=\"name\" placeholder=\"name\">\n<input name=\"initialSupply\" placeholder=\"1\">\n<input name=\"description\" placeholder=\"description\">\n<input name=\"avatar\" placeholder=\"avatarwebsite.com/avatar\">\n<button type=\"submit\">Mint Token</button>\n</form>\n</body>\n</html>\n```\n\nThis page will send a POST request to /token/create with the details of the new token the users plans to mint.\n\nNow we must make the POST /token/create route.\n# routes.js\n```javascript\napp.post(\"/token/create\", isLoggedIn, async function(req, res){\n\t\tconst data = JSON.stringify({'protocol': \"SFP@0.1\", 'name': req.body.name, 'initialSupply': parseInt(req.body.initialSupply), 'description': req.body.description, \"avatar\": req.body.avatar})\n\n\t\t\n\t\tconst options = {\n\t\t  hostname: \"www.moneybutton.com\",\n\t\t  path: \"/api/v2/me/assets\",\n\t\t  method: 'POST',\n\t\t  headers: {\n\t\t\t'content-type': 'application/json',\n\t\t\t\"Authorization\": \"Bearer \"+req.user.accessToken\n\t\t  }\n\t\t}\n\t\t\n\t\tconst request = https.request(options, result => {\n\t\t  console.log(`statusCode: ${res.statusCode}`)\n\t\t\tconsole.log(request.url)\n\t\t  result.on('data', d => {\n\t\t\tprocess.stdout.write(d)\n\t\t\tconsole.log(JSON.parse(d.toString()))\n\t\t\tres.render(\"mint\", {user: req.user, paymailAlias: JSON.parse(d.toString()).paymailAlias, initialSupply: req.body.initialSupply})\n\t\t  \n\t\t  })\n\t\t})\n\t\t\n\t\trequest.write(data)\n\t\trequest.end()\n\t\t\n\t})\n```\n\nWhen the initial POST /token/create is received, we send another request to moneybutton to create the asset (no bitcoin transaction involved). Moneybutton then sends us back the paymail address of the asset. Now, we can render the page where the user mints the asset.\n\n# /views/mint.ejs\n```javascript\n<html>\n<head>\n<script src=\"https://code.jquery.com/jquery-3.5.1.min.js\"></script>\n<script src=\"https://www.moneybutton.com/moneybutton.js\"></script>\n<script>\n$(function() {\n\tmoneyButton.render(document.getElementById('mb-button'), {\n\t\t  outputs: [\n\t\t\t{\n\t\t\t  asset: \"<%= paymailAlias %>\" + \"@moneybutton.com\",\n\t\t\t  amount: <%= initialSupply %>,\n\t\t\t}\n\t\t  ]\n\t\t})\n\t})\n</script>\n</head>\n<body>\n<div id='mb-button'></div>\n</body>\n</html>\n```\n\nTry it! :)\nIf you found this useful, you can tip me - 21394@moneybutton.com\n\n",
  "opts": {
    "enc": "0",
    "pub": "1"
  },
  "signatures": {
    "child": {
      "hash": "666c70f8a965055fa3b9dda2ce4f3af4bf0d805849cf20bc2add982cd17cbd71",
      "pubkey": "16yGRhvwWY719FWLWuKhJPVkPGUAAC8wxv",
      "signature": "H1b1Ou/5CgeAccJmOfmMCldFr2etiNLv10S58nnFlXZaZAnrrSVMTuc57mYx2gqk/QXmIPVh1AIjNhbZ9fXkFQc=",
      "timestamp": 1613345144,
      "verified": true
    },
    "parent": {
      "hash": "0fb4890b77b58c5d7535760ed3385a2597b0b94290f05bb80eb2b2dedbd01f87",
      "pubkey": "1NzYaZSoBwMGgS51A2GnhegrVvtNbRwJwF",
      "signature": "IGrBiZiqbrhfauJqbl5ZwOvNk63n/DzZQyWhQ+9vkC9rGtDv8tET5Dxsv1rUWTdHUDxh1zQ3LyFr9Wtc+IYXOYw=",
      "timestamp": 1613345144,
      "verified": true
    }
  },
  "title": "Money Button Token API Website Example",
  "type": "text/markdown"
}