The below code is part of what I use to handle logins in an app. You can use this as a base for a NodeJS login routine (in a file called app.js). I was going to use StormPath API but was too slow and unreliable for (as it turns out StormPath are merging with Okta and are closing down services).
This code is not functionally complete but you can hack out what you need. Rolling your own solution can be faster and slow you to fully embed in server side logic but make sure it is secure.
I am using these node modules (app.js).
var app = require('express')(); var http = require('http').Server(app); var mysql = require('mysql'); var uuid = require('node-uuid'); var Type = require('type-of-is'); var jsesc = require('jsesc'); var bcrypt = require('bcrypt'); var filessystem = require('fs');
The following code is located in myutils.js is a handy place to place your common code. You don’t need it but it is demonstrated here.
module.exports = function(){ this.sum = function(a,b){ return a+b }; this.multiply = function(a,b){ return a*b }; this.samplefunction = function(a){ return 'Hello World' }; }
Don’t forget to include it myutils.js in app.js
require('./myutils.js')(); //Remove this if you dont use it
Declare the body parser module (app.js).
app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json());
Set the MySQL connection details and you can setup the number of ready MySQL connections in the pool here (app.js).
console.log(' mysql_pool.createPool()'); var mysql_pool = mysql.createPool({ connectionLimit : 1000, host : 'localhost', database : 'thedatabasename', user : 'thedatabaseuser', password : 'thedatabasepassword' });
Create a static API endpoint (app.js).
// Generic static API Point app.get('/api/myappname/v1/hello/world/',function(req,res){ var data = { "Version":"" }; data["Version"] = "Hello World"; // Add a JSON value res.json(data); // Return the JSON });
Create MySQL functions (app.js).
mysql_pool.getConnection(function(err, connection){ // See if a connection is available in the pool by querying the database console.log(' mysql_pool.getConnection()'); if (err) { console.log(' error: ' + err); throw err; } connection.query('select * from thedatabasename;', function(err2, rows){ if (err2) { connection.release(); console.log(' error: ' + err2); throw err2; } else { console.log( rows ); console.log('Ready'); } connection.release(); }); console.log(' mysql_pool.release()'); }); mysql_pool.on('connection', function (connection) { connection.query('SET SESSION auto_increment_increment=1') console.log(' mysql_pool.on(connection) - Set session increment'); }); mysql_pool.on('enqueue', function () { console.log(' mysql_pool.on(enqueue). Waiting for available connection slot.'); });
/api/myappname/v1/login function (app.js).
// Login ----------------------------------------------------------------------- app.post('/api/myappname/v1/login',function(req,res){ console.log('API CALL: /api/myappname/v1/login'); var errData = ""; // Check to see if the users posted token is valid var useraccesstoken = req.headers['x-user-access-token']; if (appaccesstoken == undefined) { // If a user token is missng then block the login, we only process logins whre valid user tokens are posted. Users are generated a token at acount creation console.log(" 498 token required"); errData = {"Status": "498", "Error": "Invalid user token. Please login again (error 498)."}; res.statusCode = 499; res.send(errData); res.end(); } // I usually send an x-access-token in the posted JSON payload to eliminate older apps. var appaccesstoken = req.headers['x-access-token']; if (appaccesstoken == undefined) { // If an app token is missng then block the login, we only process logins whre valid app tokens are posted. console.log(" 499 token required"); errData = {"Status": "499", "Error": "Invalid app token. Please update your app (error 499)."}; res.statusCode = 499; res.send(errData); res.end(); } else { // ok we have a token posted var appaccesstokenpassed = false; // Check the API Key against known API Keys if (appaccesstoken == "appname33ffda6d-4303-4c65-81ca-0ccf0f172a37") { appaccesstokenpassed = true;} // Debug API Key for ICS 0.9.1 // OK Process the posted data if (appaccesstokenpassed == true) { var userenteredemail = req.body.email; var userenteredpass = req.body.password; //todo: Add Encryption/Decryption (currenty a BCrypt Hash is sent from the app) // If this is Yes a new session token is sent to the user later, this will in valitae older logged in sessions. var resetsessions = req.body.resetsessions; if (req.body.resetsessions == undefined) { resetsessions = "Yes"; } // Log for debugging console.log(' Email: ' + userenteredemail); console.log(' Password: ' + pass); console.log(' Reset Sessions: ' + resetsessions); var data = {} mysql_pool.getConnection(function(err, connection) { if (err) { console.log(' mysql_pool.release()'); connection.release(); console.log(' Error getting mysql_pool connection: ' + err); throw err; } // Revised SQL connection.query("SELECT * FROM usertablename WHERE usertablename.active=1 AND usertablename.lockedout=0 AND usertablename.email=? LIMIT 1",[userenteredemail],function(err, rows, fields){ if (err) { // Log database errors console.log(" 503 Service unavailable (database error)"); errData = {"Status": "503", "Error": "Service unavailable (database error)"}; res.statusCode = 503; res.send(errData); res.end(); } else { // Do we have a recordset if (rows.length != 0) { // Check password hash - https://github.com/ncb000gt/node.bcrypt.js var salt = rows[0].password_salt; var hash = rows[0].password_hash; // bcrypt.hashSync(pass, salt); hash = hash.replace(/^\$2y(.+)$/i, '\$2a$1'); var passwordok = false; console.log(' About to check password.'); passwordok = bcrypt.compareSync(userenteredpass, hash); console.log(passwordok); if (passwordok == true) { // Allow access to records var userid = rows[0].id; // User ID var user_guid = rows[0].user_guid; // Internal App Guid var push_guid = rows[0].push_guid; // Internal GUID user for Push Notifications var newkey = rows[0].usertokenkey; // Generate a new API secret key var newpass = rows[0].usertokenpassword; // Generate a a new API secret pass // This will create two guids to use for all future api hiots from the user if (resetsessions == "Yes") { newkey = uuid.v4(); // Generate a new API secret key newpass = uuid.v4(); // Generate a a new API secret pass } var stat_total_logins = rows[0].stat_total_logins; // Read total Logins are stored in my usedatabase stat_total_logins = stat_total_logins + 1; // Increment total logins for later saving var username = rows[0].username; // Read the user Username from the database var usertag = rows[0].usertagline; // Read the user Tagling from the database var email = rows[0].email; // Read the user Email from the database var block_transactions = rows[0].block_transactions; // Has the User Blocked Transactions var credit = rows[0].credit; // Credit Remaining var currency = rows[0].currency; // Users Currency (used for the log) var smssendalertatlogin = rows[0].user_pref_sms_alert_at_login; // Does the user want SMS Sent a Login var emailsendalertatlogin = rows[0].user_pref_email_alert_at_login; // Does the user want an email var pushsendalertatlogin = rows[0].user_pref_push_alert_at_login; // Push alert the user at login var user_pref_timezone = rows[0].user_pref_timezone; var user_pref_timezone_int = rows[0].user_pref_timezone_int; var stat_total_password_resets = rows[0].stat_total_password_resets; // Pass this throgh to the App var active = rows[0].active; // Is the user active var lockedout = rows[0].lockedout; // Is the user locked ut var changing_password = rows[0].changing_password; var querylimit5min = rows[0].QueryLimit5Min; // Read from he databse how manu API hits the user has a minute // I like to return the api version and version int with all api JSON payloads (makes checking on the App side easier) var jsonvalid = "yes"; var jsonversion = "2017.01.01.22.00"; var jsonversionint = "102"; //Update API Keys mysql_pool.getConnection(function(err, connection) { if (err) { console.log(' mysql_pool.release()'); connection.release(); console.log(' Error getting mysql_pool connection: ' + err); throw err; } // Update the users logins and new keys if thye asked to wipe all past logins. connection.query('UPDATE usertablename SET usertokenkey = ?, usertokenpassword = ?, stat_total_logins = ?, last_login = NOW() WHERE id = ?', [newkey, newpass, stat_total_logins, userid],function(errupdate, rowsupdate, fieldsupdate){ if (errupdate) { console.log(" 503 service unavailable (database update error)"); errData = {"Status": "503", "Error": "503 service unavailable (database update error)"}; res.statusCode = 503; res.send(errData); res.end(); } else { if (rowsupdate.length != 0) { // Inform User of Valid Login res.statusCode = 200; data["Data"] = "Successfully logged in.."; // statis string thta reports the successfull login // The Key and Pass are used liek OAUTH tokens and are stored in the app and user database. All API hits check these tokens. data["Key"] = newkey; //LOADS IN XCODE v0.9.1 data["Pass"] = newpass; //LOADS IN XCODE v0.9.1 // User guids are used to store the unique user rather than the database record ID data["user_guid"] = user_guid; data["push_guid"] = push_guid; // Internal GUID user for Push Notifications data["Logins"] = stat_total_logins; // Total Logins are stored in my usedatabase data["Username"] = username; // Username data["Email"] = email; // Email // The notify user code ay ogin has been removed byt can be called here data["SMSAtLogin"] = smssendalertatlogin; data["EmailAtLogin"] = emailsendalertatlogin; data["PushAtLogin"] = pushsendalertatlogin; // What is the users timezome data["timezone"] = user_pref_timezone; data["timezoneint"] = user_pref_timezone_int; data["stat_total_password_resets"] = stat_total_password_resets; // How many times has the user changed passwords data["active"] = active; // Is the user active data["lockedout"] = lockedout; // Is th user currently locked out data["changing_password"] = changing_password; // Is the user currebtky changing a password data["levelquerylimit5min"] = querylimit5min; // How any times can the user hit the API (handled by the app) data["jsonvalid"] = jsonvalid; // JSON ID data["jsonversion"] = jsonversion; // JSON Version data["jsonversionint"] = jsonversionint; // JSON Version // Send the JSON back to the user res.json(data); res.end(); //Login Confirmed console.log("User " + username + " / " + email + " logged in."); } else { console.log(" 504 service unavailable (database update error)"); errData = {"Status": "504", "Error": "504 service unavailable (database update error)"}; res.statusCode = 504; res.send(errData); res.end(); } } console.log(' mysql_pool.release()'); connection.release(); // release the MySQL connection pool connection }); }); } else { console.log('402 Invalid Login (' + req.body.email + ', ' + req.body.password + ')'); errData = {"Status": "402", "Error": "Invalid Login"}; res.statusCode = 402; res.send(errData); res.end(); } } else { console.log('403 Account is locked out, not active or unknown. Please visit https://www.myappname.com to reactivate your account. (' + req.body.email + ')'); errData = {"Status": "403", "Error": "Account is locked out, not active or unknown."}; res.statusCode = 403; res.send(errData); res.end(); } } console.log(' mysql_pool.release()'); connection.release(); }); }); } else { console.log("499 token required"); errData = {"Status": "499", "Error": "Invalid app token. Please update your app (error 499)."}; res.statusCode = 409; res.send(errData); res.end(); } } });
/api/myappname/v1/verifylogin verify login function (app.js).
// Verify Login ----------------------------------------------------------------------- app.post('/api/myappname/v1/verifylogin',function(req,res){ console.log('API CALL: /api/myappname/v1/verifylogin'); // check to see if the App included an api key var appaccesstoken = req.headers['x-access-token']; var errData = ""; if (appaccesstoken == undefined) { console.log(" 499 token required"); errData = {"Status": "499", "Error": "valid token required"}; res.statusCode = 499; res.send(errData); res.end(); } else { //console.log(" Received appaccesstoken: " + appaccesstoken); var appaccesstokenpassed = false; // Check the API Key against known API Keys if (appaccesstoken == "ics201701-1d95-9271-a6c7-a9a33770287") { appaccesstokenpassed = true;} // Debug API Key if (appaccesstokenpassed == true) { var key = req.body.key; var pass = req.body.pass; var data = {} console.log(" - Key: " + key ) console.log(" - Pass: " + pass ) // Grab the user IP and log it var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; console.log(" - IP: " + ip ); mysql_pool.getConnection(function(err, connection) { if (err) { console.log(' mysql_pool.release()'); connection.release(); console.log(' Error getting mysql_pool connection: ' + err); throw err; } // Check the user record (only if the user is active and provided thier token key and passowrd) connection.query("SELECT * FROM usertablename WHERE active=1 AND usertokenkey=? AND usertokenpassword=? LIMIT 1",[key, pass],function(err, rows, fields){ if (err) { console.log(" 405 Login Session Expired"); errData = {"Status": "405", "Error": "Login Session Expired"}; res.statusCode = 405; res.send(errData); res.end(); } else { if (rows.length != 0) { console.log(" 200 Login Session OK"); res.statusCode = 200; data["Status"] = "OK"; res.json(data); res.end(); } else { console.log(" 405 Login Session Expired"); errData = {"Status": "405", "Error": "Login Session Expired"}; res.statusCode = 405; res.send(errData); res.end(); } } console.log(' mysql_pool.release()'); connection.release(); }); }); } else { console.log("499 token required"); errData = {"Status": "499", "Error": "valid token required"}; res.statusCode = 409; res.send(errData); res.end(); } } });
Start the web service (app.js).
http.listen(3000,function(){ console.log("Connected & Listen to port 3000 at /api/myappname/v1/ .."); console.log(" Do.."); });
Hope this helps
Donate and make this blog better
Ask a question or recommend an article
[contact-form-7 id=”30″ title=”Ask a Question”]
V1.0 Initial Post