How to add a users auth in feathers middleware? - feathersjs

I use feathersjs framework in my projekt. In older version my middleware it was work but afer update a framework and after created new app with authenticate a middleware not working.
My index.js file show like below:
const cookieParser = require('cookie-parser');
const { authenticate } = require('#feathersjs/express');
module.exports = function (app) {
app.get('/login', (req, res) => {
res.sender('login');
});
app.use('/', cookieParser(), authenticate('jwt'), async (req, res) => {
const { user } = req;
try {
await app.service('users').get(user._id);
res.sender('home');
} catch(e){
res.redirect('/login');
}
});
}
I have a login script in jQuery like below:
$(document).ready(function(){
const socket = io();
const app = feathers();
app.configure(feathers.socketio(socket));
app.configure(feathers.authentication({
storage: window.localStorage
}));
$('.form-signin').submit(function(){
app.authenticate({
strategy: 'local',
username: $('#inputUsername').val(),
password: $('#inputPassword').val(),
}).then( result => {
document.cookie = "feathers-jwt=" + result.accessToken;
window.location.href = "/";
}).catch(error => {});
});
});
My problem is when I click a login button with a data correctly I receive an accessToken but I can't see home page and app show me every time 401 error code - not authorization with.
An console shows me this info: info: Invalid authentication information (no `strategy` set) {"type":"FeathersError","name":"NotAuthenticated","code":401,"className":"not-authenticated","errors":{}}
In new version not working too failureRedirect: '/login'
SOLUTION
Add below code before app.get('/', authenticate('jwt'), (req, res) => {});
app.use('/', cookieParser(), (req, res, next) => {
var cookies = req.cookies;
var token = cookies['feathers-jwt'];
req.authentication = {
strategy: 'jwt',
accessToken: token
}
next();
})

Related

Where to place code to show data from MySQL to Handlebars?

Goal:
I am aiming to teach myself how to use Node JS, MySQL and express.
I'm struggling to understand where to place my code for loading MySQL data into HTML.
Let me show you the whole code.
app.js
var express = require('express');
var mysql = require('mysql');
var dotenv = require('dotenv');
var path = require('path');
var cookieParser = require('cookie-parser');
dotenv.config({path: './.env'});
var app = express();
// Connection to MySQL
var db = mysql.createConnection({
host: process.env.DATABASE_HOST,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE
});
db.connect(function(error) {
if(error) {
console.log(error);
}
else{
console.log("Connected");
}
});
// Parse URL-Encoded bodies
app.use(express.urlencoded({extended: false}));
// Parse JSON bodies
app.use(express.json());
// Initialize a cookie
app.use(cookieParser());
// View engine to control HTML
app.set('view engine', 'hbs');
// Public dir
var publicDir = path.join(__dirname, './public');
app.use(express.static(publicDir));
// Define routes
app.use('/', require('./routes/pages'));
app.use('/auth', require('./routes/auth'));
app.listen(3000, function() {
console.log("Server is running on port 3000");
});
routes/pages.js
var express = require('express');
var authController = require('../controllers/auth');
var router = express.Router();
// Home
router.get("/", authController.isLoggedIn, function(req,res) {
res.render("index", {
user: req.user
});
});
// Register
router.get("/register", function(req, res) {
res.render("register");
});
// Login
router.get("/login", function(req, res) {
res.render("login");
});
// Profile
router.get('/profile', authController.isLoggedIn, function(req, res) {
if(req.user) {
res.render('profile', {
user: req.user
});
}
else {
res.redirect('login');
}
});
// Forum
router.get('/forums', authController.isLoggedIn, function(req, res) {
if(req.user) {
res.render('forums');
} else {
res.redirect('login');
}
});
// English Division //
// Premier League
router.get('/Leagues/EnglishDivision', authController.isLoggedIn, function(req, res) {
if(req.user) {
res.render('PremierLeague');
} else {
res.redirect('../../login');
}
});
module.exports = router;
routes/auth.js
var express = require('express');
var authController = require('../controllers/auth');
var router = express.Router();
// Register
router.post("/register", authController.register);
// Login
router.post("/login", authController.login);
// Logout
router.get('/logout', authController.logout);
module.exports = router;
controllers/auth.js
var mysql = require('mysql');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
var {promisify} = require('util');
// Connection to MySQL
var db = mysql.createConnection({
host: process.env.DATABASE_HOST,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE
});
// Register function
exports.register = function(req, res) {
console.log(req.body);
var {name, email, password, passwordConfirm} = req.body;
db.query("SELECT email FROM users WHERE email = ?", [email], function(error, result) {
if(error){
console.log(error);
}
if(result.length > 0) {
return res.render('register', {
message: 'That email is already in use'
})
} else if(password !== passwordConfirm) {
return res.render('register', {
message: 'Passwords do not match'
});
}
let hashedPassword = bcrypt.hashSync(password, 8);
console.log(hashedPassword);
// Insert user details into MySQL
db.query('INSERT INTO users set ?', {name: name, email: email, password: hashedPassword, dateJoined: new Date()}, function(error, result) {
if(error) {
console.log(error);
} else {
console.log(result);
return res.render('register', {
message: 'User registered'
});
}
});
});
}
// Login function
exports.login = function(req, res) {
try {
var {email, password} = req.body;
if(!email || !password) {
return res.status(400).render('login', {
message: 'Please provide an email and password'
});
}
db.query('SELECT * FROM users WHERE email = ?', [email], async function(error, result) {
console.log(result);
if(!result.length > 0 || !(await bcrypt.compare(password, result[0].password))) {
res.status(401).render('login', {
message: 'The email or password is incorrect'
});
}
else {
var id = result[0].id;
// Create a token
var token = jwt.sign({id}, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN
});
console.log("The token is " + token);
// Create a cookie
var cookieOptions = {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES * 24 * 60 * 60 * 1000
),
httpOnly: true
}
// Set up a cookie
res.cookie('jwt', token, cookieOptions);
res.status(200).redirect("/");
}
});
} catch (error) {
console.log(error);
}
}
// Check if logged in
exports.isLoggedIn = async function(req, res, next) {
console.log(req.cookies);
if(req.cookies.jwt){
try {
// Verify the token
var decoded = await promisify(jwt.verify)(req.cookies.jwt, process.env.JWT_SECRET);
console.log(decoded);
// Check if user exist
db.query("SELECT id, name, email, password, date_format(datejoined, '%d/%m/%Y') as dateJoined FROM users WHERE id = ?", [decoded.id], function(error, result) {
console.log(result);
// If no result
if(!result) {
return next();
}
req.user = result[0];
return next();
});
}
catch (e) {
console.log(e);
return next();
}
} else{
next();
}
}
// Logout function
exports.logout = async function(req, res) {
res.clearCookie('jwt');
res.status(200).redirect('/');
}
Question
In my .hbs file called PremierLeague I'd like to load MySQL data in HTML format. Where in the code below I need to start?
Desired goal:
This is when the user clicks into view premier league
Foreach record in MySQL I'd like to add a new card for each record. I know how to use HandleBars {{some.data}}.
I just don't get where I code the query?
Does it needs to be in a controller or can it be in in the router.get(...?
Also how do I use {{#foreach}} correctly ?
You don't need any other specific controller, the right place to code the query is actually the route itself.
But before entering the core of your question, let's talk a while about your code.
I can see you are performing connection to database more than once, you could add database dedicated controller, something like:
controllers/db.js
var mysql = require('mysql');
var dotenv = require('dotenv');
dotenv.config({path: './.env'});
// Connection to MySQL
var db = mysql.createConnection({
host: process.env.DATABASE_HOST,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE
});
function connect(done) {
db.connect(done);
}
module.exports = { db: db, connect: connect };
this let you access to the database instance from every file with just one line:
var db = require('./controllers/db').db;
than you could use the connect function in your app:
app.js
var express = require('express');
var db = require(./controllers/db);
var path = require('path');
var cookieParser = require('cookie-parser');
// set up your server
var app = express();
// Parse URL-Encoded bodies
app.use(express.urlencoded({extended: false}));
// Parse JSON bodies
app.use(express.json());
// Initialize a cookie
app.use(cookieParser());
// View engine to control HTML
app.set('view engine', 'hbs');
// Public dir
var publicDir = path.join(__dirname, './public');
app.use(express.static(publicDir));
// Define routes
app.use('/', require('./routes/pages'));
app.use('/auth', require('./routes/auth'));
// finally run your server only if you can connect to the database
db.connect(function(error) {
if(error) return console.log("Error connecting to the database:", error);
app.listen(3000, function() {
console.log("Server is running on port 3000");
});
});
you could also simplify you controllers/auth.js removing database connection stuff and using only the line to require your database controller.
Finally you can code your query:
routes/pages.js
var express = require('express');
var authController = require('../controllers/auth');
var db = require('../controllers/db').db;
var router = express.Router();
// Omissis... other routes
// Premier League
router.get('/Leagues/EnglishDivision', authController.isLoggedIn, function(req, res) {
// a good practice is first to handle possible exit cases to reduce nesting levels
if(! req.user) return res.redirect('../../login');
// this is actually the right place to perform queries
db.query('SELECT ...', [...], function(error, results) {
// once again first possible exit cases
if(error) return res.status(500).end(error.message)
res.render('PremierLeague', { results: results });
});
});
module.exports = router;
Last in your PremierLeague.hbs file you can handle the results in a #foreach directive.
Just pass your data when you render the view
router.get('/Leagues/EnglishDivision', authController.isLoggedIn, function(req, res) {
if(req.user) {
connection.query('SELECT * FROM EnglishDivision',function (err,results) {
if (err) throw err;
res.render('PremierLeague',{data: results});
});
} else {
res.redirect('../../login');
}
});
then in the .hbs file
{{#each data}}
<div class="card">
<h3>{{this.someData}}</h3>
<h2>{{this.someData}}</h2>
</div>
{{/each}}

Get JSON Object from URL using Express

In the express users.js file:
router.get('/', function(req, res, next) {
fetch('https://www.somwhere.com/users')
.then(res => res.json())
.catch(error => console.log(error));
});
module.exports = router;
In my App.js file for my React App I use
componentDidMount() {
fetch('/users')
.then(res => res.json())
.then(users => this.setState({ users }));
}
Right now it throws a 500 error and its not catching the error
Can I get some help fixing this
You can use axios in your FrontEnd("React") and BackEnd("Express"). This code below only an example code that you can follow:
🔴 Backend: Express Server Using axios
const express = require('express');
const app = express();
const axios = require('axios');
const cors = require('cors');
app.use(cors( { origin: '*'}));
const END_POINT = 'https://jsonplaceholder.typicode.com/users';
app.get('/users', async (req, res) => {
try {
const { data } = await axios.get(END_POINT);
res.status(200).send(data);
} catch(ex) {
res.status(500).send(ex.data);
}
})
app.listen(3000, () => {
console.log('Server is up');
});
The code above only an example if you want to using axios in your backend.
📤 Updated: Using fetch
If you still want to using fetch, then you can use code below 👇:
router.get('/', async (req, res) => {
try {
const result = await fetch('https://jsonplaceholder.typicode.com/users');
const json = await result.json();
res.status(200).send(json);
} catch(ex) {
console.log(ex);
res.status(500).send(ex.message);
}
})
module.exports = router;
🔵 FrontEnd: React Using axios
async componentDidMount() {
try {
// change the endpoint with yours
const { data } = await axios.get('http://localhost:3000/users');
console.log(data);
// do some stuff here: set state or some stuff you want
} catch(ex) {
console.log(ex);
}
}
💡 Dont Forget to install and import axios in your React App.
📤 Updated: If you still want to using fetch in your React App, than you can use this code below:
async componentDidMount() {
try {
// change the endpoint with yours
const result = await fetch('http://localhost:3000/users');
const json = await result.json();
console.log(json);
// do some stuff here: set state or some stuff you want
} catch(ex) {
console.log(ex);
}
}
I hope it's can help you 🙏.

can´t make your two or more methods in same route

the link to the project
https://github.com/Kammikazy/project
i can´t make work my get two or more methods in same route
i have the code 404
i using mysql nodejs and express
my code
controller alliances
const User = require('../models/Alliances')
const findAlianca = async (connection, req, res) => {
const Allianca = await User.find(connection, req.session.user.username)
if (!Allianca) {
res.status(404).send('Nenhuma cidade encontrada.');
return;
}
console.log("dddd");
req.session.Allianca = Allianca
res.locals.Allianca = Allianca
res.render('Administration/Alliances')
}
module.exports = {
findAlianca
}
route aliance
const express = require('express')
const router = express.Router()
const connection = require('../../Config/database')
const controllerAdmin = require('../../controllers/Administration')
const controlleruser = require('../../controllers/Alliances')
router.get('/Administration/Alliances', (req, res) => controllerAdmin.findcidade3(connection, req, res))
router.get('/Administration/Alliances/limitado', (req, res) => controlleruser.findAlianca(connection, req, res))
module.exports = app => app.use('/', router)
models aliance
const find = (connection,username) => {
return new Promise((resolve, reject) => {
connection.query(SELECT alianca.nome,alianca.N_membros,alianca.TAG FROM user INNER JOIN alianca ON user.cod_alianca=alianca.id WHERE user.username='${username}', (err, result) => {
if(err){
reject(err)
}else{
resolve(result)
}
})
})
}
module.exports = {
find
}
alliance.jade
extends layout
block title
.col-xs-6.col-xs-offset-3.col-sm-6.col-sm-offset-3
.col-sm-4(style='width:76%')
div.panel.panel-primary(style='height:50px') Alliances Page
div.panel.panel-primary(style='height:700px') fdssdklfsdklfjskldfjkldsjfl
if locals.user.cod_alianca==null
p You Dont Have Alliances
else
br
span Your Aliance
span= locals.Allianca.nome
.col-xs-2.panel-red(style='width:24%;height:100%;text-align:center')
my app
require('./routes/Administration/Alliances')(app)
my connection db
const mysql = require('mysql')
const config = require( "./config.json" )
const connection =mysql.createConnection({
host:config.host,
user:config.user,
password:config.password,
database:config.database,
// port:config.port
});
connection.connect((err) =>{
if(err){
console.log(err)
process.exit(0)
}else{
console.log('database on')
}
})
what i doing wrong i can´t find the solution for my problem
Not sure what you are asking however if you want to call multiple function in same route/API you can do following:
Using expressJs you can use next function like:
app.get('/Administration/Alliances', (req, res, next) => {
//Do something here and to add data to your request use
req.body.newData = 'newData';
//after this just call next function
next();
}, (req, res, next) => {
//Can continue this cycle of calling next function until last `sendResponse` function is reached.
//Can even set `error` in request for `sendResponse`
req.error = "Some error";
next();
}, (req, res) => {
if(req.error) {
res.status(400).send(req.error);
} else {
res.status(200).send(req.body.result);
}
});
the soluction for my problem
const express = require('express')
const router = express.Router()
const connection = require('../../Config/database')
const controllerAdmin = require('../../controllers/Administration')
const controlleruser = require('../../controllers/Alliances')
router.get('/Administration/Alliances', (req, res, next) => {
//Do something here and to add data to your request use
controllerAdmin.findcidade3(connection, req, res)
next();
}, (req, res, next) => {
//Can continue this cycle of calling next function until last `sendResponse` function is reached.
//Can even set `error` in request for `sendResponse`
controlleruser.findAlianca(connection, req, res)
})
module.exports = app => app.use('/', router)

Nodejs & sequalize mysql query to the database happens only once

I have the following node code,i am trying to query the database based on a request,i use sequelize orm with mysql
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const axios = require("axios");
const models = require("./models");
const jsonParser = bodyParser.json();
app.post("/auth/change", jsonParser, (req, res) => {
let phoneNumber = req.body.phone;
let password = req.body.password;
console.log("phone number", phoneNumber);
models.users
.findOne({
where: {
phone: phoneNumber
}
})
.then(user => {
console.log(user.name);
}).catch(error => {
console.log(error);
});
});
app.listen(3000, () => {
console.log("Listening on port 3000");
});
I use react on the front end, and when i send a request with data for example
{phone:777,password:123} it works, but if i do a second request with same or different data it fails.What am i missing here!!?
you are not returning any data to the front side when it call the API , so the server will be waiting to return a response to the caller.
try to change your code to this :
models.users
.findOne({
where: {
phone: phoneNumber
}
})
.then(user => {
res.status(200).send('ok')
}).catch(error => {
res.status(400).send('not ok')
});

nodejs - stub module.exports functions with sinon

I have an expressjs app with the following routes and middleware modules. I am trying to test the routes module using mocha, chai, http-chai and sinonjs.
The API uses mysql and in order to test the routes module, I have it all modularized so that I can stub out the mysql module.
However when I try to stub middleware/index, I am having trouble. If I try to require index normally, the module doesn't actually get stubbed. If I try to require it using require.cache[require.resolve('./../../lib/routes/middleware/index')];, it seems to stub something, but indexStub.returns(indexObj) returns an error TypeError: indexStub.returns is not a function and TypeError: indexStub.restore is not a function.
How do I stub out index.js properly in order to control the code flow and keep it from trying to connect to mysql?
routes.js
'use strict';
const express = require('express');
const router = express.Router();
const configs = require('./../config/configs');
const middleware = require('./middleware/index');
const bodyParser = require('body-parser');
const useBodyParserJson = bodyParser.json({
verify: function (req, res, buf, encoding) {
req.rawBody = buf;
}
});
const useBodyParserUrlEncoded = bodyParser.urlencoded({extended: true});
// creates a new post item and return that post in the response
router.post('/posts', useBodyParserUrlEncoded, useBodyParserJson, middleware.validatePostData, middleware.initializeConnection, middleware.saveNewPost, middleware.closeConnection, function(req, res) {
if (res.statusCode === 500) {
return res.send();
}
if (res.statusCode === 405) {
return res.send('Item already exists with slug ' + req.body.slug + '. Invalid method POST');
}
res.json(res.body).end();
});
module.exports = router;
middleware/index.js
'use strict';
const configs = require('./../../config/configs');
const database = require('./../../factories/databases').select(configs.get('STORAGE'));
const dataV = require('./../../modules/utils/data-validator');
module.exports = {
initializeConnection: database.initializeConnection, // start connection with database
closeConnection: database.closeConnection, // close connection with database
saveNewPost: database.saveNewPost, // creates and saves a new post
validatePostData: dataV.validatePostData, // validates user data
};
spec-routes.js
'use strict';
var chai = require('chai');
var chaiHttp = require('chai-http');
var sinonChai = require("sinon-chai");
var expect = chai.expect;
var sinon = require('sinon');
chai.use(sinonChai);
chai.use(chaiHttp);
var app = require('./../../app');
describe('COMPLEX ROUTES WITH MIDDLEWARE', function() {
var indexM = require.cache[require.resolve('./../../lib/routes/middleware/index')];
describe('POST - /posts', function() {
var indexStub,
indexObj;
beforeEach(function() {
indexStub = sinon.stub(indexM);
indexObj = {
'initializeConnection': function(req, res, next) {
return next();
},
'closeConnection': function(req, res, next) {
return next();
},
'validatePostData': function(req, res, next) {
return next();
}
};
});
afterEach(function() {
indexStub.restore();
});
it('should return a 500 response', function(done) {
indexObj.saveNewPost = function(req, res, next) {
res.statusCode = 500;
return next();
};
indexStub.returns(indexObj);
chai.request(app)
.post('/posts')
.send({'title': 'Hello', 'subTitle': 'World', 'slug': 'Example', 'readingTime': '2', 'published': false})
.end(function(err, res) {
expect(res).to.have.status(500);
done();
});
});
});
});
You don't use Sinon at all, as it doesn't deal with module loading at all. I see you have started doing this manually using the internal Node API's, but I suggest you do it the way we advise in the Sinon docs regarding this usecase: juse use proxyquire.
It enables you to substitute require calls to ./middleware/index.js for a mock object of your own liking (possibly made using sinon).
You would use it something like this:
var myIndex = {
initializeConnection: sinon.stub(),
closeConnection: sinon.stub(),
saveNewPost: sinon.stub()
};
var app = proxyquire('./../../app', {'./middleware/index': myIndex});