OAuth2 server creation with nodejs - node.js

I m actually studying REST Apis security, and it seems that many people are using OAuth2 and OpenId protocoles to manage authentication.
I have tried to implement two OAuth2 server using :
http://passportjs.org/ for the client side and https://github.com/jaredhanson/oauth2orize for the server side
https://www.npmjs.org/package/node-oauth2-server
For the first solution, running the examples is working correctly but I need to make something stateless (and in the example the author uses sessions...)
Can you help me to create the simplest oauth2 server possible or defaultly explaining me the whole functionnement of these libraries ?
Thanks for advance

I implemented using "oauth2-server": "^3.0.0-b2"
var express = require('express');
var oauthServer = require('oauth2-server');
var Request = oauthServer.Request;
var Response = oauthServer.Response;
var authenticate = require('./components/oauth/authenticate')
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/models.js
var oauth = new oauthServer({
model: require('./models.js')
});
app.all('/oauth/token', function(req,res,next){
var request = new Request(req);
var response = new Response(res);
oauth
.token(request,response)
.then(function(token) {
// Todo: remove unnecessary values in response
return res.json(token)
}).catch(function(err){
return res.status( 500).json(err)
})
});
app.post('/authorise', function(req, res){
var request = new Request(req);
var response = new Response(res);
return oauth.authorize(request, response).then(function(success) {
res.json(success)
}).catch(function(err){
res.status(err.code || 500).json(err)
})
});
app.get('/secure', authenticate(), function(req,res){
res.json({message: 'Secure data'})
});
app.get('/me', authenticate(), function(req,res){
res.json({
me: req.user,
messsage: 'Authorization success, Without Scopes, Try accessing /profile with `profile` scope',
description: 'Try postman https://www.getpostman.com/collections/37afd82600127fbeef28',
more: 'pass `profile` scope while Authorize'
})
});
app.get('/profile', authenticate({scope:'profile'}), function(req,res){
res.json({
profile: req.user
})
});
app.listen(3000);
To simulate, Use Postman: https://www.getpostman.com/collections/37afd82600127fbeef28
MySQL/PostgreSQL/MSSQL Compatiable: https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/models.js
MySQL DDL: https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/sql/oauth_demo.sql
Mongo Dumps: https://github.com/manjeshpv/node-oauth2-server-implementation/tree/master/mongo-dump
Note that they have an issue there with the validateScope function needs to be replaced with:
function validateScope(user, client) {
return user.scope === client.scope
}

Related

Internal Server Error 500 from API when deploying front-end React.js backend Express app

For my senior capstone, my group and I have developed a web-based application to simulate Bitcoin - using react.js for the front-end and node.js/express for the back-end. Up until recently, we've had all of simulation-creating-code (javascript files) inside the src directory, meaning it was being built client-side. Due to high waiting times to create a simulation from all the hashing necessary in transactions, we decided that our simulation-creating-code would be better suited for the back-end rather than the front end. Taking the load off the client and putting it on the server drastically improved the speed of creating a simulation, so 'Great success!'.
When we made this change, we ended up having some issues with require and import statements. Reactjs only supports import statements and Express uses require statements. We had to use some js functions that we developed in our API's so we imported them with require statements, and we thought we thought it was resolved because on our development environment, everything runs as smooth as butter, but once it's deployed, our login page is unable to make an API call. The error is: Failed to load resource: the server responded with a status of 500 (Internal Server Error).
It's interesting because this route in the API worked prior to making this big switch from require to import, and those changes were in other files/routes. The login API remains completely unchanged.
Either way, I'll drop some code in case it's helpful in troubleshooting.
server.js
const express = require("express");
const app = express();
const router = express.Router();
const path = require("path");
var cors = require("cors");
require("dotenv").config();
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ limit: "50mb" }));
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
// List of routes
router.use("/api/users", require("./api/users"));
router.use("/api/data", require("./api/data"));
router.use("/api/share", require("./api/share"));
router.use("/api/addresses", require("./api/addresses"));
const root = path.join(__dirname, "client/build");
app.use(express.static(root));
app.use(router);
app.use(cors({ origin: true, credentials: true }));
app.listen(
process.env.PORT,
() => `Server running on port ${process.env.PORT}`
);
api/users.js login route
const express = require("express");
const app = express();
const db = require("../dbConn");
const bcrypt = require("bcrypt-nodejs");
const cors = require("cors");
const router = express.Router();
const jwt = require("jwt-simple");
const config = require("../configuration/config.json");
// to parse JSON
app.use(express.json());
router.post("/login", (req, res) => {
//check if email and password are sent
if (!req.body.email || !req.body.password) {
return res.status(401).json({ error: "Missing username and/or password" });
}
// go into mysql and get info
let qry = `select * from user where email = "${req.body.email}"`;
db.query(qry, (err, rows) => {
if (err) {
return res.status(500).json({ error: err });
}
// assert: no error - process the result set
if (rows.length == 0) {
// no users found
res.status(400).json({ msg: "No users found" });
} else {
// process the user records
let users = [];
rows.forEach((row) => {
let user = {
uid: row.uid,
email: row.email,
role: row.role,
dateCreated: row.created_date,
password: row.password,
};
users.push(user);
});
if (users[0]) {
// Does given password hash match the database password hash?
bcrypt.compare(req.body.password, users[0].password, (err, result) => {
// Send back a token that contains the user's username
const token = jwt.encode({ email: req.body.email }, config.secret);
if (result == true) {
res.status(200).json({
msg: "user authenticated",
fname: users[0].fname,
lname: users[0].lname,
role: users[0].role,
token: token,
});
} else {
res.sendStatus(401);
}
});
}
}
});
});
router.post("/auth", cors(), (req, res) => {
try {
let user = jwt.decode(req.body.token, config.secret);
res.status(200).send(user);
} catch (err) {
res.sendStatus(401);
}
});
SignIn.js client/src/components. This is wrapped in a react.useEffect() arrow function, but again I don't believe the issue is here because this page remains unchanged from a working version.
const handleSubmit = (e) => {
e.preventDefault();
const credentials = { email, password };
// API call to login to account
// if successful, redirect to landing page
// if not, display error message
fetch(`http://${process.env.REACT_APP_API_URL}/api/users/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
})
.then(async (res) => {
if (res.status == 200) {
return res.json();
} else {
throw new Error("Failed to Login!");
}
})
.then(async (res) => {
// Store token in cookie
setCookie("token", res.token, { path: "/${path}", maxAge: 3600 * 24 });
// Toggle state of sign in
toggleSignIn();
// Feedback
setFeedback(true);
setFeedbackObj({ message: "Signed in!", severity: "success" });
//redirect
history.push(`${process.env.PUBLIC_URL}/simulation`);
})
.catch(async (err) => {
// Feedback
setFeedback(true);
setFeedbackObj({ message: "Sign In Error", severity: "error" });
console.error(err);
});
};
If there are any other files that are of interest please let me know.
I've tried to mess with the proxy in package.json, but I don't think thats the answer because it was working previously. I've had a really difficult time finding others with similar issues or resources other than how to build a simple app with Express backend and React.js front end. This is not our issue because our application was working perfectly before this big switch. I believe the issue is stemming from require statements in our API and the running of JS functions in the API. I have no way to confirm this because in production (deployment), the errors are super uninformative, and in development, it runs perfectly fine.
I have been trying to solve this issue for a couple of weeks now, and I've made very little progress. If anyone has suggestions or tips on troubleshooting deployment, I would greatly appreciate it.
Thanks!

how to implement social logins using jwt in node.js backEnd and angular 2 frontEnd

how to implement the social login such as facebook and google authentication
using Json Web token (jwt) ,same as Auth0 has implemented. i am using node js as back end and angular 2 as front end.
Perhaps I'm confused by your phrasing but facebook, google, and JWT are distinct, individual authentication strategies.
But check out passport.js it offers hundreds of authentication strategies:
defined strategy: (this example uses mongoose)
const jwtOptions = {
jwtFromRequest: PJWT.ExtractJwt.fromAuthHeader(),
secretOrKey: SECRET, // externally defined private encryption key
}
export const jwtStrategy = new PJWT.Strategy(jwtOptions,
function (jwt_payload, done) {
MongoUser.findOne({ email: jwt_payload.email }, function (err, user) { // presumes the user's email is in the jwt
if (err) return done(err, false);
if (user) return done(null, user);
else return done(null, false);
});
});
also these are needed by passport:
type Done = (err: any, user?: {}) => void;
export const serializeMongoUser = function (user: UserDoc, done: Done) {
done(null, user._id);
}
export const deserializeMongoUser = function (_id: any, done: Done) {
MongoUser.findById(_id, function (err, user) {
done(err, user);
});
}
add the middleware to your server:
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(ppt.serializeMongoUser);
passport.deserializeUser(ppt.deserializeMongoUser);
passport.use(ppt.jwtStrategy);
authenticate from a service in your app:
authenticate(): Observable<any> {
if (!this.security.jwt) return Observable.of({sucess: false})
return this.http.get('/api/authenticate', authHeaders(this.security))
.map(res => res.json());
}
call this function when you need to authenticate:
*note the authHeaders(this.security)
this.security is a subscription to the JWT token in my ngrx store, but you could also put it in localStorage.
export function authHeaders(security: Security) {
const headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization', `JWT ${security.jwt}`);
return { headers };
}
calling authenticate() will return {success: true} or {success: false}
Here is a little example I created to show the basics of socket.io security with JWT authentication. The backend is nodeJS, socket.io, and express.
In the long run you should use passport for user sessions but the below is good to see so you get the whole picture.
Overview
When browsing to http://127.0.0.1:3001, the express middleware socketAuthenticated will create a jwt based on the hard coated user credentials (should use passport sessions for real user info).
This token is then passed to the server EJS view and used to establish a socket connection.
Create a new folder for the app and run npm install express socketio-jwt jsonwebtoken ejs-locals socket.io
Create a "public" folder within the root folder and put the socket.io-client JS file inside (you can run bower install socket.io-client to get the js file)
Create app.js file within the root folder with the following:
let express = require('express'),
socketioJwt = require('socketio-jwt'),
jwt = require('jsonwebtoken'),
port = 3001
engine = require('ejs-locals');
let app = express();
let socketAuthenticated = (req, res, next) => {
let token = jwt.sign({firstName:"jason", lastName:"owl"}, "secret", { expiresIn: 60*5 }); // assuming you have some user object in req
res.token = token; // front end will use this token to authenticate its socket connection
next();
};
app.engine('ejs', engine);
app.set('view engine', 'ejs');
app.set('view options', {defaultLayout: 'layout'});
app.use(express.static('public'));
app.use(socketAuthenticated);
app.route('/')
.get((req, res) => {
console.log(`Server Passing Token: ${res.token} to client`);
res.render('index', { jwt: res.token });
});
let server = require('http').Server(app);
let io = require('socket.io')(server);
// Setup socket server to use JWT
io.set('authorization', socketioJwt.authorize({
secret: "secret",
handshake: true
}));
io.of('/default').on('connection', (socket) => {
socket.on('data', (newConfig) => {
console.log('Data Event Recieved!');
});
});
server.listen(port);
console.log(`\Application: ${process.pid} Listening On Port: ${port}`);
4) Create a new folder called views and create a file called index.ejs:
<script type="text/javascript" src="/socket.io.js"></script>
<script>
var jwttoken = <%- JSON.stringify(jwt) %>;
function connect_socket (token) {
var socket = io.connect('/default', {
query: 'token=' + token
});
return socket;
}
var socket = connect_socket(jwttoken);
socket.emit('data');
</script>
run node app.js and open the browser to localhost:3001.

node expressjs websocket session passing

My code is listed below but I wanted to explain my thought process and have someone correct me at every point because I have been struggling to try and get this done the RIGHT way.
I've been struggling with this for some time(5days+) and I have not found a straight forward way to do this across the web.
So I have 2 separate node apps running. One running just express-js and another running a websocket server. I'm probably just a complete knucklehead with this, but here goes.
I setup a mongo session store. When a user successfully authenticates, the session gets created and I can re-direct the user to the logged in page. While the session lives, when the user hits the 'auth-page' I can just auto redirect the user to the 'logged in page'.
Now my understanding is, when the session gets created in the mongo-store, a cookie gets created on the web browser and it is this cookie that gets to the server for each request the page makes and express-js will nicely handle the magic internally for me and I can use something like
app.post('/', function (req, res) {
}
Where the req variable gets populated with the session id by express, because express got the cookie and decoded it.
This next part is where things are dicey and any suggestions in anyway will be a huge help.
What i'm wanting to do is, inside my app.post('/'...etc) is redirect to another page. This page loads a client which initiates a websocket connection to my websocket server and my websocket server is able to use this same session-id.
So here's the thing. My express-js http server runs as a separate process with its own port and my websocket server runs as a separate process with its own port as well. After doing enough research online, I found out many sources which indicated that, when my browser makes the connection to my websocket server it will send the cookie in the header somewhere to my websocket server. So in the browser, I have some javascript code that runs:
let clientSocket = new WebSocket("ws://socket.server.address:5005");
So then from my node websocket server, I can parse out the socket.upgradeReq.headers , get the cookie, and use that to get the session id and i'm in business. That describes what I've attempted to achieve below in my code. I have been successful doing this, however I've hit different issues when trying to parse the cookie.
Sometimes I get a single cookie & sometimes, I get multiple cookies taking the form
cookie_name1=cookie_value1;cookie_name2=cookie_value2;
cookie_name3=cookie_value3;cookie_name4=cookie_value4;
cookie_name5=cookie_value5;
Sometimes I get a single cookie & sometimes, I get multiple cookies taking the form
question 1 - why do I get multiple cookies being sent to my websocket server? Is that dictated strictly by the browser? What can I do about that if anything?
question 2 - Will the cookies ALWAYs come in that format? I would hate for the semicolon delimiter style to change and that break my code
question 3 - upon reviewing my code, my thought process can you suggest and guide me with a complete different/better implementation to achieve this? Can you suggest I change parts? My goal is to be able to spin up multiple different websocket servers & webservers and load-balance between them. I'm trying to find a reliable way to do this so that my code doesn't break... my node apps are just very frail, some direction would help. It seems like for nodejs, despite its maturity in 2017, good information lives only on stackoverflow,github issue threads and irc.freenode and I classify some of these things as basic...
packages and versions used
web-server package versions
---------------
express#4.15.2
express-session#1.15.2
mongodb#2.2.26
cookie-parser#1.4.3
body-parser#1.17.1
connect-mongodb-session#1.3.0
socket-server package versions
---------------
uws#0.14.1
below is my code
webserver.js
'use strict';
const bodyparser = require('body-parser');
const cookieparser = require('cookie-parser');
const express = require('express');
const app = express();
const express_session = require('express-session');
const connect_mongo = require('connect-mongodb-session')(express_session);
const port = process.env.NODE_WEBSERVER_PORT;
const _ = require('underscore');
const mongo_store = new connect_mongo({
uri: 'mongodb://mongo1.weave.local:27017/sessiondb',
collection: 'sess'
});
const session_time = 1000 * 60 * 5 ; // 5 minute(s)
app.use(express_session({
secret: 'superman',
cookie: {
maxAge: session_time,
httpOnly: false
},
store: mongo_store,
resave: false,
saveUninitialized: false,
name: 'inspect_the_deq',
httpOnly: false
}));
app.use(bodyparser.urlencoded({ extended: false }))
app.use(bodyparser.json());
app.set('view engine', 'pug');
app.set('views', __dirname+'/pugs')
app.use(express.static(__dirname + '/com/js'));
app.use(express.static(__dirname + '/com/asset'));
const mongo = require('mongodb').MongoClient;
const mongo_url = 'mongodb://mongo1.weave.local:27017/main';
let account = null;
let database = null;
mongo.connect(mongo_url, function(err, db) {
let collection = db.collection('account');
account = collection;
database = db;
});
app.get('/', function (req, res) {
if(req.session.user){
const user = req.session.user;
res.render('main', {message: 'user '+user+' logged in' });
console.log('session found logging you on');
}else{
res.render('login', {message: 'Login'});
console.log('no session exists');
}
});
app.post('/', function (req, res) {
const user = req.body.username, pass = req.body.password;
const seconds = session_time;
account.findOne({username: user, password: pass }, function(err, document) {
if( document !== null ){
req.session.user = user;
req.session.cookie.expires = new Date(Date.now() + seconds);
req.session.cookie.signed = true;
res.render('main', {message: 'user '+user+' logged in'});
console.log('some id is '+req.session.id);
console.log('cookie id is '+req.session.cookie);
console.log('sess id is '+req.sessionID);
}else
res.render('login', {message: 'Login', login_error: 'invalid username or password'});
});
});
app.listen(port, function () {
console.log('http server '+port);
});
Socket Server code here
'use strict';
const _ = require('underscore');
const uwsPlugin = require('uws').Server;
const socket_port = process.env.NODE_SOCKET_PORT;
const ws = new uwsPlugin({ port: socket_port, maxPayload: 0 });
//const Meepack = require('./core/meepack');
const cookieparser = require('cookie-parser');
const express_session = require('express-session');
const connect_mongo = require('connect-mongodb-session')(express_session);
const mongo_store = new connect_mongo({
uri: 'mongodb://mongo1.weave.local:27017/sessiondb',
collection: 'sess'
});
ws.on('connection', function connection(socket) {
'use strict';
console.log('client verification process ');
let headers = Object.keys(socket.upgradeReq.headers);
let upgradeReq = Object.keys(socket.upgradeReq.headers.cookie);
let cookie = socket.upgradeReq.headers.cookie;
//use the cookie here to get the session_id and do whatever you want
socket.on('close', function close(e) {
console.log('connection closed');
});
socket.on('message', function close(data) {
'use strict';
});
});

Post request never getting made, just sends get

I am using Mongoose and nodejs to write an API.
My users.js looks as follow:
var express = require('express');
var router = express.Router();
var user = require('../models/users.js');
router.post('/',function(req, res, next) {
console.log("made a post");
var user2 = new user(); // create a new instance of the Bear model
user2.firstName = req.body.firstName; // set the bears name (comes from the request)
user2.lastName=req.body.lastName;
user2.email=req.body.email;
user2.save(function(err) {
if (err)
res.send(err);
console.log("User created");
});
})
//The model acts as our user object...this returns all users.
.get('/', function(req, res, next) {
console.log("sending a get request");
user.find(function(err, users) {
if (err)
res.send(err);
res.json(users);
});
})
module.exports = router;
When I send a get request, it works perfectly. However, I am now trying to develop the POST request. I send a request such as the following:
http://localhost:4000/users?firstName=Han&lastName=Bo#email=haBo#yah.com
and I receive the following in my console:
sending a get request
GET /users?firstName=Han&lastName=Bo#email=haBo#yah.com
200 15.522 ms - 1365
And I receive the output of my GET request in the browser.
I'm new to node and would appreciate some help with this.
Thanks.
You are putting your parameters as URL parameters, while your POST API reads parameters from body of request.
Here is explanation of POST parameters.
Also, if you are not already using it, use postman to send requests.

401 Error with post request Stormpath Express + React + Node + Gulp

When attempting to login any user, the following POST error results:
XMLHttpRequest cannot load https://api.stormpath.com/v1/applications/[APP_HREF]/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401.
In my server.js file, I have put the following:
var express = require('express');
var stormpath = require('express-stormpath');
var cors = require('cors');
var app = express();
app.use(stormpath.init(app, {
apiKey: {
id: 'xyz',
secret: 'abc' // Using the unsafe inline option for example purposes
},
application: {
href: `[APP_HREF]`
},
web: {
produces: ['application/json']
},
debug: 'info'
}));
app.use(cors({
origin: 'http://localhost:8080',
credentials: true
}));
app.post('/', stormpath.loginRequired, function (req, res) {
function writeError(message) {
res.status(400);
res.json({ message: message, status: 400 });
res.end();
}
});
app.on('stormpath.ready', function () {
var server = app.listen(process.env.PORT, function() {
try {
process.send('CONNECTED');
} catch(e) {}
});
});
In my login.jsx file, I have included:
login: function(e) {
e.preventDefault();
e.stopPropagation();
this.serverRequest = $.post('https://api.stormpath.com/v1/applications/[APP_HREF]/login',
{
"username": document.getElementById("email").value,
"password": document.getElementById("pass").value
}, function (result) {
console.log(result);
});
I also have saved my stormpath.yml file.
I'm not using React-Stormpath because I already created my views for Login and Registering. It looks like I only need to access the REST api with Stormpath, but I'm not sure what I need to add in order to get the API key validated.
On the actual login.jsx file, would I have to send the ID:SECRET pair as well as part of the POST request?
I see that in your login.jsx you are trying to post directly to the Stormpath REST API, but unfortunately that isn't possible yet. Instead you will make the post to your Express server, and in turn it will communicate with Stormpath.
You already have express-stormpath in your Express server, so you just need to post your login form to /login and Stormpath will take care of the rest :)
Let us know if you run into any issues! FYI, we will be adding a "serverless" feature soon, you can follow that here: http://ideas.stormpath.com/ideas/IAM-I-59

Resources