External database connection passing authentication errors on Heroku app - javascript

I've been having a strange issue where I've created a basic Nodejs API that I'm trying to host on Heroku which works perfectly from a local standpoint but as soon as I try to test through Heroku I get an authentication error when a MySQL connection is attempted.
From what I can see, when doing Postman tests, Heroku is adding information to my connection information which then causes authentication failure. Authentication to the API itself works but fails the MySQL connection.
const express = require('express');
const mysql = require('mysql');
const cors = require('cors');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const utils = require('./utils');
const app = express();
const leadsdb = mysql.createPool({
host: "hostIP",
user: "username",
password: "password",
database: "database",
});
const port = process.env.PORT || 4000;
// enable CORS
app.use(cors());
// parse application/json
app.use(bodyParser.json());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/api/register", (req, res) => {
const firstName = req.body.firstName
const lastName = req.body.lastName
const email = req.body.email
const number = req.body.number
const country = req.body.country
const age = req.body.age
const occupation = req.body.occupation
const sqlInsert = "INSERT INTO Leads (FirstName, LastName, Email, Country, Phone, Age, Occupation) VALUES (?,?,?,?,?,?,?)"
leadsdb.query(sqlInsert, [firstName, lastName, email, country, number, age, occupation], (err, result) => {
if(!result) {
res.send(err)
}
else {
res.send(result)
}
})
{
"code": "ER_ACCESS_DENIED_ERROR",
"errno": 1045,
"sqlMessage": "Access denied for user 'username'#'ec2-54-170-17-28.eu-west-1.compute.amazonaws.com' (using password: YES)",
"sqlState": "28000",
"fatal": true}
If I'm not mistaken, the '#'ec2-54-170-17-28.eu-west-1.compute.amazonaws.com' is not supposed to be included in the login information and it was not passed in where the DB connection is referenced.
I don't know if this is enough information so please ask for anything that may be needed. Appreciate any assistance.

I can see that you haven't specify the port. While connecting to a remote database from Heroku you should use these fields:
host: 'localhost',
port: 3306,
user: 'root',
password: '',
database: 'db_name',
insecureAuth: true
there is a one option also that you created a connection string like
DATABASE_URL=mysql2://username:password#ip.goes.here/data_base_name --app heroku-app-name
and specify it in your .env fil.
This makes the process lot more easier.

I realised that Hostgator has IP whitelisting rules for remote access to the hosted SQL servers. So Heroku wasn't appending anything to the connection, the server was actually responding saying the connection isn't allowed from the requesting hostname.
I appreciate all the help and I'm sorry for wasting the time of those who helped out.

Related

Why can't I connect to mongoDB atlas?

I am new in MongoDB, all my life I used MySQL.
I have created an account in atlas, set the IP to my IP and created a user and saved the password.
here is my code, why doesn't it work?
app.js
const express = require('express');
const bodyParser = require('body-parser');
const mongoPractice = require('./mongo');
const app = express();
app.use(bodyParser.json());
app.post('/products', mongoPractice.createProduct);
app.get('/products');
app.listen(3000);
and the mongo.js:
const MongoClient = require("mongodb").MongoClient;
const url =
"mongodb+srv://idan:<85IwoSzeQssHMzLN>#cluster0.tpejv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority";
const createProduct = async (req, res, next) => {
const newProduct = {
name: req.body.name,
price: req.body.price,
};
const client = new MongoClient(url);
try {
await client.connect();
const db = client.db();
const result = db.collection("products").insertOne(newProduct);
} catch (error) {
return res.json(error);
}
client.close();
res.json(newProduct);
};
const getProducts = async (req, res, next) => {};
exports.createProduct = createProduct;
exports.getProducts = getProducts;
the POSTMAN output:
Your ip may have changed, (check if the current ip address has information "(includes your current IP address)". For testing(!) you can add address 0.0.0.0/0 to the whitelist - it means every ip will be accepted - this solution is good for beginners
Firstly check you connection link from mongodb connect
Check username, password again
You can change password and try again
In mongo.js
You need to remove "< >" around the password.
const url = "mongodb+srv://idan:**85IwoSzeQssHMzLN**#cluster0.tpejv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority";
I encountered the same error once and I might have solution.
The most common one is that your IP address set to access the database might not match with your current IP address in which case you need to set it to your current IP or set to allow access from anywhere.
The issue which I had : If you have recently started using an ethernet cable try going back to wireless to access the mongoDB database from your backend script.

express basic auth challenge argument not forcing popup on browser

i am trying to lock up my swagger documentation, i am using express basic auth for this, when i try to access the documentation endpoint it just throws a HTTP ERROR 401 without asking me to provide a username and password.
app.use("/api-docs",basicAuth({
challenge: true,
users: { 'me': 'openforme' } }),
swaggerUi.serve,
swaggerUi.setup(yaml.parse(swaggerfile))
);
when i remove the basicauth middleware i can access the endpoint, what i want is a popup to into the username and password.
use the Nub-auth package
const express = require('express');
const nubAuth = require('nub-auth');
app = express();
app.use(['/v1'], nubAuth({
challenge: true,
users: { 'admins': 'admin', 'users': 'user', 'guest': 'guested' }
}));
app.get('/api', (req, res) => {
res.send('Hello World welcome to API endpoints free !');
});
app.get("/v1", (req, res) => {
res.send("Hello World welcome to API endpoints closed !");
});
from my investigation edge does not have the basic auth feature enabled.

Getting nodeJS web server to send form data to my email

I am trying to get the data my nodeJS server is receiving from a form on the front end to send that data to my email. I have tried to use nodemailer and haven't succeeded much. Can someone tell me perhaps what I am doing wrong with the following code?
const express = require("express");
const app = express();
const nodemailer = require("nodemailer");
var smtpTransport = require("nodemailer-smtp-transport");
const PORT = process.env.PORT || 4000;
app.use(express.static(__dirname + "/front-end"));
app.get("/", (req, resp) => {
resp.sendFile(__dirname + "/front-end/index.html");
});
app.use(express.json());
app.use(express.urlencoded());
app.post("/formData", (req, resp) => {
const data = req.body;
var transport = nodemailer.createTransport(
smtpTransport({
service: "Gmail",
auth: {
user: "user#gmail.com",
pass: "123456",
},
})
);
transport.sendMail(
{
//email options
from: "Sender Name <email#gmail.com>",
to: "Receiver Name <receiver#email.com>", // receiver
subject: "Emailing with nodemailer", // subject
html: data, // body (var data which we've declared)
},
function (error, response) {
//callback
if (error) {
console.log(error);
} else {
console.log("Message sent:");
resp.send("success!");
}
transport.close();
}
);
});
app.listen(PORT, () => {
console.log(`server running on port ${PORT}`);
});
Your code, at a glance, looks fine to me. I think the problem is (since you’re not stating you have set that up), that you want to send email with GMail. If you want to send email from your own app or web service via Gmail, you should set up a project in the Google Cloud Platform. Read more here.
Alternatively, you could use a service like Postmark, which you can configure to send emails via a domain that you own. There’s a free trial. Mailgun is a similar service. (I’m not affiliated to either).

Deploy Node.js socket.io project on shared OVH server

I'm trying to build and deploy my app to OVH server, my client doesn't want a cloud solution, he want to host it or deploy it on OVH (they told me OVH support Node.js) and to be honest i have no idea how to do it.
my project work fine in development, its a Real-time chat with socket.io and MySql and some package as knex, in front-end i worked with React.js ( which i have no problem with it right now )
I can provide more informations if needed. thx a lot
const app = require("express")();
var cors = require("cors");
app.use(cors());
const server = require("http").createServer(app);
const mysql = require("mysql");
const knex = require("knex")({
client: "mysql",
connection: {
host: "localhost",
user: "root",
password: "",
database: "chat_message",
},
});
const io = require("socket.io")(server, {
cors: {
origin: "*",
credentials: true,
},
});
app.get("/messages", function (request, result) {
knex
.select()
.table("messages")
.then((data) => result.send(data))
});
io.on("connection", (socket) => {
socket.on("messageClient", (sms) => {
knex("messages")
.insert({
message: sms.msg,
socket_id: sms.id,
dateMsg: sms.Temps,
ip: sms.ip,
name: sms.name,
})
.then((e) => console.log("data insert succees"));
socket.broadcast.emit("messageAll", sms);
});
});
server.listen(5000, () => console.log("Port: 5000"));
OVH is a private company and I'm not sure if this would be offtopic and more suitable to ask their own support. However you should know that shared hosting in general does not support long running processes like nodejs. They only support PHP on the server.

Passport.js / Google OAuth2 strategy - How to use token on login for API access

I am logging users in via their domain Google accounts using passport.js. This works great, but now I need to give this application access to a few Google API's (drive, sheets, etc).
When a user logs in, a message appears in the logs, that makes it seem like passport has all the required info:
info: [06/Jun/2019:21:24:37 +0000] "302 GET /auth/callback?code=** USER ACCESS TOKEN HERE **&scope=email%20profile%20https://www.googleapis.com/auth/drive.file%20https://www.googleapis.com/auth/spreadsheets%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/drive HTTP/1.1" [46]
This is achieved by passing the appended scopes via passport.authenticate(), which presents the user with the "Grant access to these things on your Google account to this app?" screen :
//Initial auth call to Google
router.get('/',
passport.authenticate('google', {
hd: 'edmonds.wednet.edu',
scope: [
'email',
'profile',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets'
],
prompt: 'select_account'
})
);
However, when I go and try to call an API with something like:
const {google} = require('googleapis');
const sheets = google.sheets({version: 'v4', auth});
router.post('/gsCreate', function(req,res,next){
sheets.spreadsheets.create({
// Details here.....
});
});
I get nothing but errors (the current one is debug: authClient.request is not a function)
My question is: Is it possible for me to use a setup like this, asking the user to log in and grant permissions once, and then somehow save that to their user session via passport?
I had the same question, but I was able to access Google Gmail API functionalities along with Passport.js user authentication by specifying 'scopes' using the following process.
First, create a file to setup the passport-google-strategy in nodejs as follows.
passport_setup.js
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20')
const fs = require("fs");
const path = require('path');
//make OAuth2 Credentials file using Google Developer console and download it(credentials.json)
//replace the 'web' using 'installed' in the file downloaded
var pathToJson = path.resolve(__dirname, './credentials.json');
const config = JSON.parse(fs.readFileSync(pathToJson));
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
const query = { _id: id }
Users.findOne(query, (err, user) => {
if (err) {
res.status(500).json(err);
} else {
done(null, user)
}
})
})
//create a google startergy including following details
passport.use(
new GoogleStrategy({
clientID: config.installed.client_id,
clientSecret: config.installed.client_secret,
callbackURL: config.installed.redirect_uris[0]
}, (accessToken, refreshToken,otherTokenDetails, user, done) => {
//in here you can access all token details to given API scope
//and i have created file from that details
let tokens = {
access_token: accessToken,
refresh_token: refreshToken,
scope: otherTokenDetails.scope,
token_type: otherTokenDetails.token_type,
expiry_date:otherTokenDetails.expires_in
}
let data = JSON.stringify(tokens);
fs.writeFileSync('./tokens.json', data);
//you will get a "user" object which will include the google id, name details,
//email etc, using that details you can do persist user data in your DB or can check
//whether the user already exists
//after persisting user data to a DB call done
//better to use your DB user objects in the done method
done(null, user)
})
)
Then create your index.js file in nodejs for API route management and to call send method of Gmail API.
Also, run the following command to install "google-apis"
npm install googleapis#39 --save
index.js
const express = require("express")
//import passport_setup.js
const passportSetup = require('./passport_setup')
const cookieSeesion = require('cookie-session');
const passport = require("passport");
//import google api
const { google } = require('googleapis');
//read credentials file you obtained from google developer console
const fs = require("fs");
const path = require('path');
var pathToJson_1 = path.resolve(__dirname, './credentials.json');
const credentials = JSON.parse(fs.readFileSync(pathToJson_1));
//get Express functionalities to app
const app = express();
// **Middleware Operations**//
//cookie encryption
app.use(cookieSeesion({
name:'Reserve It',
maxAge: 1*60*60*1000,
keys: ['ranmalc6h12o6dewage']
}))
//initialize passort session handling
app.use(passport.initialize())
app.use(passport.session())
app.use(express.json());
//**API urls**//
//route to authenticate users using google by calling google stratergy in passport_setup.js
//mention access levels of API you want in the scope
app.get("/google", passport.authenticate('google', {
scope: ['profile',
'email',
'https://mail.google.com/'
],
accessType: 'offline',
prompt: 'consent'
}))
//redirected route after obtaining 'code' from user authentication with API scopes
app.get("/google/redirect", passport.authenticate('google'), (req, res) => {
try {
//read token file you saved earlier in passport_setup.js
var pathToJson_2 = path.resolve(__dirname, './tokens.json');
//get tokens to details to object
const tokens = JSON.parse(fs.readFileSync(pathToJson_2));
//extract credential details
const { client_secret, client_id, redirect_uris } = credentials.installed
//make OAuth2 object
const oAuth2Client = new google.auth.OAuth2(client_id,
client_secret,
redirect_uris[0])
// set token details to OAuth2 object
oAuth2Client.setCredentials(tokens)
//create gmail object to call APIs
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
//call gmail APIs message send method
gmail.users.messages.send({
userId: 'me',//'me' indicate current logged in user id
resource: {
raw: //<email content>
}
}, (err, res) => {
if (err) {
console.log('The API returned an error: ' + err)
throw err
}
console.log('Email Status : ' + res.status)
console.log('Email Status Text : ' + res.statusText)
})
res.status(200).json({ status:true })
} catch (err) {
res.status(500).json(err)
}
})
app.listen(3000, () => { console.log('Server Satrted at port 3000') })
You can separate the routes in the index.js file to different files for clarity using express.Router()
If you want to call another Google API service just change this code segment and code below that;
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
gmail.users.messages.send(....Send Method internal implementation given above....)
For Google Drive:
const drive = google.drive({version: 'v3', auth: oAuth2Client});
drive.files.list(...Refer "Google Drive API" documentation for more details....)
I believe you can't use passport.js for three-legged oauth for APIs like Sheets or Drive.
Have a look at the Using OAuth for web servers documentation instead.
user835611 has the correct answer, as that page explains everything quite nicely. However, if you still need more, the below link really helped me to understand how this works.
https://github.com/googleapis/google-auth-library-nodejs#oauth2

Categories

Resources