I'm developing with express a web page that when the client clicks in "Send E-mail" redirect to google asking for permission to send email through the client email and after the client gave the permission redirect back and send the email.
The code so far:
'use strict';
const express = require('express');
const googleAuth = require('google-auth-library');
const request = require('request');
let router = express.Router();
let app = express();
const SCOPES = [
'https://mail.google.com/'
,'https://www.googleapis.com/auth/gmail.modify'
,'https://www.googleapis.com/auth/gmail.compose'
,'https://www.googleapis.com/auth/gmail.send'
];
const clientSecret = '***********';
const clientId = '**************';
const redirectUrl = 'http://localhost:8080/access-granted';
const auth = new googleAuth();
const oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
function sendEmail(auth, content, to , from, subject) {
let encodedMail = new Buffer(
`Content-Type: text/plain; charset="UTF-8"\n` +
`MIME-Version: 1.0\n` +
`Content-Transfer-Encoding: 7bit\n` +
`to: ${to}\n` +
`from: ${from}\n` +
`subject: ${subject}\n\n` +
content
)
.toString(`base64`)
.replace(/\+/g, '-')
.replace(/\//g, '_');
request({
method: "POST",
uri: `https://www.googleapis.com/gmail/v1/users/me/messages/send`,
headers: {
"Authorization": `Bearer ${auth}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"raw": encodedMail
})
},
function(err, response, body) {
if(err){
console.log(err); // Failure
} else {
console.log(body); // Success!
}
});
}
app.use('/static', express.static('./www'));
app.use(router)
router.get('/access-granted', (req, res) => {
sendEmail(req.query.code, 'teste email', 'teste#gmail.com', 'teste#gmail.com', 'teste');
res.sendfile('/www/html/index.html', {root: __dirname})
})
router.get('/request-access', (req, res) => {
res.redirect(authUrl);
});
router.get('/', (req, res) => {
res.sendFile('/www/html/index.html', { root: __dirname });
});
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
module.exports = app;
Every time I try to send a simple E-mail I receive the error 401: Invalid Credentials. But I'm passing in Authorization the client code auth that google sends just gave me...
The recommended way to use google APIs is to use Google API nodejs client which also provides Google OAuth flow. You can then use google.gmail('v1').users.messages.send to send the email :
const gmail = google.gmail('v1');
gmail.users.messages.send({
auth: oauth2Client,
userId: 'me',
resource: {
raw: encodedMail
}
}, function(err, req) {
if (err) {
console.log(err);
} else {
console.log(req);
}
});
auth is OAuth2, the OAuth object that can be populated with your token. You can get the token in the express session :
var oauth2Client = getOAuthClient();
oauth2Client.setCredentials(req.session["tokens"]);
which you've already stored in your OAuth callback endpoint :
var oauth2Client = getOAuthClient();
var session = req.session;
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
if (!err) {
oauth2Client.setCredentials(tokens);
session["tokens"] = tokens;
res.send(`<html><body><h1>Login successfull</h1><a href=/send-mail>send mail</a></body></html>`);
} else {
res.send(`<html><body><h1>Login failed</h1></body></html>`);
}
});
Here is a complete example inspired from this google API oauth for node.js example :
'use strict';
const express = require('express');
const google = require('googleapis');
const request = require('request');
const OAuth2 = google.auth.OAuth2;
const session = require('express-session');
const http = require('http');
let app = express();
app.use(session({
secret: 'some-secret',
resave: true,
saveUninitialized: true
}));
const gmail = google.gmail('v1');
const SCOPES = [
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.send'
];
const clientSecret = 'CLIENT_SECRET';
const clientId = 'CLIENT_ID';
const redirectUrl = 'http://localhost:8080/access-granted';
const mailContent = "test";
const mailFrom = "someemail#gmail.com";
const mailTo = "someemail#gmail.com";
const mailSubject = "subject";
function getOAuthClient() {
return new OAuth2(clientId, clientSecret, redirectUrl);
}
function getAuthUrl() {
let oauth2Client = getOAuthClient();
let url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
//use this below to force approval (will generate refresh_token)
//approval_prompt : 'force'
});
return url;
}
function sendEmail(auth, content, to, from, subject, cb) {
let encodedMail = new Buffer(
`Content-Type: text/plain; charset="UTF-8"\n` +
`MIME-Version: 1.0\n` +
`Content-Transfer-Encoding: 7bit\n` +
`to: ${to}\n` +
`from: ${from}\n` +
`subject: ${subject}\n\n` +
content
)
.toString(`base64`)
.replace(/\+/g, '-')
.replace(/\//g, '_');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: encodedMail
}
}, cb);
}
app.use('/send-mail', (req, res) => {
let oauth2Client = getOAuthClient();
oauth2Client.setCredentials(req.session["tokens"]);
sendEmail(oauth2Client, mailContent, mailTo, mailFrom, mailSubject, function(err, response) {
if (err) {
console.log(err);
res.send(`<html><body><h1>Error</h1><a href=/send-mail>send mail</a></body></html>`);
} else {
res.send(`<html><body><h1>Send mail successfull</h1><a href=/send-mail>send mail</a></body></html>`);
}
});
});
app.use('/access-granted', (req, res) => {
let oauth2Client = getOAuthClient();
let session = req.session;
let code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
if (!err) {
oauth2Client.setCredentials(tokens);
session["tokens"] = tokens;
res.send(`<html><body><h1>Login successfull</h1><a href=/send-mail>send mail</a></body></html>`);
} else {
res.send(`<html><body><h1>Login failed</h1></body></html>`);
}
});
})
app.use('/', (req, res) => {
let url = getAuthUrl();
res.send(`<html><body><h1>Authentication using google oAuth</h1><a href=${url}>Login</a></body></html>`)
});
let port = process.env.PORT || 8080;
let server = http.createServer(app);
server.listen(port);
server.on('listening', function() {
console.log(`listening to ${port}`);
});
Related
I'm trying to implement JWT authentication, and I can't figure out why in Postman protected-route is available, while in my browser protected-route is Unauthorized.
I'm a beginner and its first time implementing jwt authentication in my project following this tutorial https://www.youtube.com/playlist?list=PLYQSCk-qyTW2ewJ05f_GKHtTIzjynDgjK
Problem
/login and /register work fine and I can see from the log they issue a JWT token in the req.headers
like 'Authorization':'Bearer <token>', however when I try to access GET /protected-route in my browser, it returns Unauthorized, and I can see no logging from JWTStrategy.
I think req.headers.Authorization is not set to all HTTP requests in the app, but only to POST /login, /register routes So my questions are:
Is this req.headers["Authorization"] = 'Bearer <toekn>'; correct way to set req headers to all GET and POST request in the app?
Does the jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() checks for Authorization property in req.headers or res.headers?
I have provided the relevant code snippets please have a look!
Code:
app.js
//...
require('./config/database'); // require the db
// ...
const passport = require('passport');
const strategy = require('./config/passport').strategy;
// ...
app.use(passport.initialize());
passport.use(strategy);
// ...
module.exports = app
router/index.js
const router = express.Router();
//...
const User = require('../config/database').User;
const passport = require('passport');
const utils = require('../lib/utils');
// register route
router.post('/register', function(req, res, next){
const saltHash = utils.genPassword(req.body.password);
console.log("req.body.password is: " + req.body.password)
const salt = saltHash.salt;
const hash = saltHash.hash;
const newUser = new User({
username: req.body.username,
hash: hash,
salt: salt
})
newUser.save()
.then((user) => {
const jwt = utils.issueJWT(user);
if (jwt){
req.headers["Authorization"] = jwt.token;
}
res.redirect('/login')
})
.catch(err => next(err))
});
// login route
router.post('/login', function(req, res, next){
User.findOne({username: req.body.username})
.then((user) => {
if(!user) {
res.status(401).json({success: false, msg: "Could not find user "})
}
// validate the user
const isValid = utils.validPassword(req.body.password, user.hash, user.salt)
if(isValid) {
// issue a JWT
const jwt = utils.issueJWT(user);
if (jwt){
req.headers["Authorization"] = jwt.token;
}
res.redirect("/")
} else {
res.status(401).json({success: false, msg: "you entered the wrong password"})
}
})
.catch(err => next(err))
});
// GET protected route
router.get("/protectd-route", passport.authenticate('jwt', {session: false}), async (req, res, next) => {
res.render("user/getuser.pug")
next()
})
passport.js
const User = require('../config/database').User;
// ...
const Strategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
/// PUB_KEY
const pathToKey = path.join(__dirname, '..', 'id_rsa_pub.pem');
const PUB_KEY = fs.readFileSync(pathToKey, 'utf8');
// options
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: PUB_KEY,
algorithms: ['RS256']
};
// strategy
const strategy = new Strategy(options, (payload, done) => {
User.findOne({_id: payload.sub})
.then((user) => {
if(user) {
return done(null, user)
} else {
return done(null, false)
}
})
.catch((err) => done(err, null))
});
module.exports.strategy = strategy;
utils.js
genPassword() - Creating a salt and hash out of it to store in db
validPassword() - re-hashing user salt and hash to verify
issueJWT() - signing user with jsonwebtoken
const crypto = require('crypto');
const jsonwebtoken = require('jsonwebtoken');
const User = require('../config/database').User;
//...
const pathToKey = path.join(__dirname, '..', 'id_rsa_priv.pem');
const PRIV_KEY = fs.readFileSync(pathToKey, 'utf8');
// validate in /login
function validPassword(password, hash, salt) {
var hashVerify = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
return hash === hashVerify;
}
// generate in /register
function genPassword(password) {
var salt = crypto.randomBytes(32).toString('hex');
var genHash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
return {
salt: salt,
hash: genHash
};
}
// sign
function issueJWT(user) {
const _id = user._id;
const expiresIn = '86400';
const payload = {
sub: _id,
iat: Date.now()
};
const signedToken = jsonwebtoken.sign(payload, PRIV_KEY, { expiresIn: expiresIn, algorithm: 'RS256' });
return {
token: "Bearer " + signedToken,
expires: expiresIn
}
}
module.exports.validPassword = validPassword;
module.exports.genPassword = genPassword;
module.exports.issueJWT = issueJWT;
Postman
In Postman, the protected route is showing successfully, with Headers set as above.
The browser network is showing this, there is no Authorization property both in response and request headers
Clearly, I'm missing something, but I can't figure it out. Any help is appreciated
here is also my db
database.js
const uri = process.env.MONGO_URI
const connection = mongoose.connection;
mongoose.connect(uri, {
useUnifiedTopology: true,
serverSelectionTimeoutMS: 5000
})
connection.on('error', console.error.bind(console, "connection error"))
connection.once('open', () => {
console.log('MongoDB database connection has been established successfully')
})
const userSchema = new mongoose.Schema({
username: String,
hash: String,
salt: String
});
const User = mongoose.model('User', userSchema )
module.exports.User = User;
Trying to create a login authentication system as an entry into web development but the fetch I have to access my login functionality doesn't work. Morgan shows "POST -- ms --". (Works through Postman). As far as I can see my cors system is set up as expected. The API will respond if the passport.authenticate('local') is removed.
authenticate.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./models/user');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var config = require('./config.js');
exports.local = passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
exports.getToken = function(user) {
return jwt.sign(user, config.secretKey,
{expiresIn: 3600});
};
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secretKey;
exports.jwtPassport = passport.use(new JwtStrategy(opts,
(jwt_payload, done) => {
console.log("JWT payload: ", jwt_payload);
User.findOne({_id: jwt_payload._id}, (err, user) => {
if (err) {
return done(err, false);
}
else if (user) {
return done(null, user);
}
else {
return done(null, false);
}
});
}));
routes
var express = require('express');
var UserRouter = express.Router();
var passport = require('passport');
var User = require('../models/user')
var authenticate = require('../authenticate');
const cors = require('./cors');
const bodyParser = require('body-parser');
UserRouter.use(bodyParser.json());
UserRouter.route('/login')
.options(cors.corsWithOptions, (req, res) => { res.sendStatus(200); })
.post(cors.cors, passport.authenticate('local'), (req, res) => {
console.log(req.body);
var token = authenticate.getToken({_id: req.user._id});
res.setHeader('Content-Type', 'application/json');
res.json({success: true, token: token, status: 'You are successfully logged in!'});
res.status(200).send()
});
module.exports = UserRouter;
mongoose schema file
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var User = new Schema ({
firstname : {
type: String,
default: ""
},
lastname: {
type: String,
default: ''
},
admin: {
type: Boolean,
default: false
}
});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
cors file
const express = require('express');
const cors = require('cors');
var whitelist = ['http://localhost:3000', 'http://localhost:3443']
var corsOptionsDelegate = (req, callback) => {
var corsOptions;
console.log("Validating origin");
console.log(req.header('Origin'));
if(whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = { origin: true };
}
else {
corsOptions = { origin: false };
}
callback(null, corsOptions);
};
exports.cors = cors();
exports.corsWithOptions = cors(corsOptionsDelegate);
Front end API call
export const loginUser = (creds) => (dispatch) => {
console.log("test")
dispatch(requestLogin(creds))
console.log("attempting login")
return fetch('http://localhost:3443/users/login', {
method: 'POST',
headers: {
'Content-Type':'application/json',
},
body: JSON.stringify(creds)
})
.then(response => {
console.log("got response 1");
if(response.ok) {
return response
} else {
console.log("response errored");
var error = new Error('Error ' + response.status + ': ' + response.statusText);
error.response = response;
throw error;
}
},
error => {
console.log("errored")
throw error;
})
.then(response => response.json())
.then(response => {
console.log("got response 2");
if(response.success) {
// Successful login
localStorage.setItem('token', response.token);
localStorage.setItem('creds', JSON.stringify(creds));
// TODO dispatch success
dispatch(receiveLogin(response));
} else {
var error = new Error('Error ' + response.status);
error.response = response;
throw error;
}
})
.catch(error => dispatch(loginError(error.message)))
}
Does anyone know where I'm going wrong with this? I'm not really getting any useful error messages from my front-end so haven't included.
Terminal output upon login attempt
OPTIONS /users/login 204 3.287 ms - 0
POST /users/login - - ms - -
I have this code on my client side:
sendMail(e) {
e.preventDefault();
var name = document.getElementById('name').value;
var contactReason = document.getElementById('contactReason').value;
var email = document.getElementById('email').value;
var additionalInfo = document.getElementById('additionalInfo').value;
var body = {
name: name,
contactReason: contactReason,
email: email,
additionalInfo: additionalInfo,
};
console.log(body);
fetch('http://localhost:4000/', {
method: 'POST',
body: body.toString(),
}).then(r => console.log(r)).catch(e => console.log(e));
}
And this kind of works. It logs the object to the console, and sends something to the back end.
Here's my Node call:
var express = require('express');
var router = express.Router();
var cors = require('cors');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({extended: true}));
app.use(cors());
app.options('*', cors());
var a = '=';
router.post('/', (req, res, next) => {
console.log('mailing');
console.log(a);
console.log(req.body);
a += '=';
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
host: "smtp.gmail.com", // hostname
auth: {
user: '******',
pass: '******'
}
});
let mailOptions = {
from: `${req.body.name} ${req.body.email}`, // sender address
to: 'alexander.ironside#mygeorgian.ca', // list of receivers
subject: 'Email from UczSieApp contact form', // Subject line
text: 'Hello world ', // plaintext body
html: `
<h4>Imie: ${req.body.name}</h4>
<h4>Email: ${req.body.email}</h4>
<h4>Powod kontaktu: ${req.body.contactReason}</h4>
<p>Wiadomosc: ${req.body.additionalInfo}</p>
`
};
// send mail with defined transport object
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
}
);
module.exports = router;
So what the code does right now:
The object is being created, something (not sure what exactly) is being send to Node back-end, and an email is being sent. But req.body is logged as {}.
What I want to do:
Read the values sent to the back-end as body and send an email with this data.
What am I missing?
I used GET instead of POST and this solved my problems. It is kind of a cheat but it works.
should add to fetch
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(body),
all correct code
frontend
sendMail(e) {
e.preventDefault();
var name = document.getElementById('name').value;
var contactReason = document.getElementById('contactReason').value;
var email = document.getElementById('email').value;
var additionalInfo = document.getElementById('additionalInfo').value;
var body = {
name: name,
contactReason: contactReason,
email: email,
additionalInfo: additionalInfo,
};
console.log(body);
fetch('http://localhost:4000/', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(body),
method: 'POST',
}).then(r => console.log(r)).catch(e => console.log(e));
}
backend
var express = require('express');
var router = express.Router();
var cors = require('cors');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({extended: true}));
app.use(cors());
app.options('*', cors());
var a = '=';
router.post('/', (req, res, next) => {
console.log('mailing');
console.log(a);
console.log(req.body);
a += '=';
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
host: "smtp.gmail.com", // hostname
auth: {
user: '******',
pass: '******'
}
});
let mailOptions = {
from: `${req.body.name} ${req.body.email}`, // sender address
to: 'alexander.ironside#mygeorgian.ca', // list of receivers
subject: 'Email from UczSieApp contact form', // Subject line
text: 'Hello world ', // plaintext body
html: `
<h4>Imie: ${req.body.name}</h4>
<h4>Email: ${req.body.email}</h4>
<h4>Powod kontaktu: ${req.body.contactReason}</h4>
<p>Wiadomosc: ${req.body.additionalInfo}</p>
`
};
// send mail with defined transport object
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
}
);
module.exports = router;
I am successfully authenticating and logging in with the google OAuth API.
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const passportInit = require('./app/routes/auth')
const session = require('express-session');
module.exports = (app, db, passport) => {
app.use(session({secret: "ahskfjdhkjshadkjfhlajsdhlfkj"}));
passportInit(passport)
app.use(passport.initialize());
app.use(passport.session())
app.get('/', (req, res) => {
if (req.session.token) {
res.cookie('token', req.session.token);
res.json({
status: 'session cookie set'
});
console.log(req.session.token);
console.log(JSON.stringify(req.user))
} else {
res.cookie('token', '')
res.json({
status: 'session cookie not set'
});
}
});
app.get('/auth/google', passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/youtube']
}));
app.get('/auth/google/callback',
passport.authenticate('google', {
failureRedirect: '/' // Fail: To err Page
}),
(req, res) => {
req.session.token = req.user.token;
userString = JSON.stringify(req.user);
userObjectValue = JSON.parse(userString);
userId = userObjectValue['profile'].id;
userName = userObjectValue['profile'].name;
userGivenName = userName.givenName;
const details = {'userId': userId};
db.collection('users').findOne(details, (err, item) => {
if (item == null) {
res.redirect('http://localhost:80/Register.html');
} else {
if(item['rolle'] == 'yt') {
res.redirect('http://localhost:80/YT_Welcome.html');
} else {
res.redirect('http://localhost:80/WT_Welcome.html');
}
}
});
}
);
app.get('/logout', (req, res) => {
req.logout();
req.session.token = null;
res.redirect('/');
});
}
Now I want to make a POST request from my frontend to my NodeJS backend.
Frontend-Request:
function registerWT() {
console.log('registerWT started...')
var rolle = 'wt';
var json = {"rolle": rolle};
$.ajax({
url: 'http://localhost:8000/user',
type: 'POST',
data: JSON.stringify(json),
contentType: 'application/json; charset=utf-8',
dataType: 'JSON',
async: false,
success: function (msg) {
var js = JSON.stringify(msg);
var state = msg['state'];
if (state == true) {
console.log('successfully created new user')
} else {
console.log('failed to create new user')
}
}
});
Backend-API:
var ObjectID = require('mongodb').ObjectID;
const passport = require('passport');
const passportInit = require('./auth');
module.exports = (app, db) => {
app.post('/user', (req, res) => {
console.log("POST USER REACHED!"); // This is printed out...
console.log(req.body.rolle); //is printed out correctly
console.log(req.user); // Is undefined...
if (!req.isAuthenticated()) { return res.send({'error':'unauthorized'}) } //Authentication fails...
console.log(req.user.userId);
console.log(req.userGivenName);
console.log(req.body.rolle);
userId = req.user.userId;
userGivenName = req.user.userGivenName;
userRolle= req.body.rolle;
const details = { userId: userId, userGivenName: userGivenName, rolle: userRolle };
db.collection('users').insert(details, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occurred' });
} else {
res.send(result.ops[0]);
}
});
});
}
From my understanding the user authentication data should be send automaticly with every request I am doing from my frontend to my backend, since I logged in via google before. Is this correct or do I miss to include something in my frontend JS request code?
What is interessting is that after I logged in, I have to trouble with navigating to /user. So there is no problem with manualy doing a get request to this API, where I am also checking for authentication.
app.get('/user', (req, res) => {
if (!req.isAuthenticated()) { return res.send({'error':'unauthorized'}) }
db.collection('users').find({}).toArray((err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.json(item)
}
});
});
But when I am making a Get request with JavaScript, the authentication fails again...
JavaScript get request:
function getTest() {
console.log('YT');
$.ajax({
url: 'http://localhost:8000/user',
type: 'GET',
async: false,
success: function (msg) {
var state = msg['state']; //nehmen den Wert von state aus der JSON
if (state == true) {
console.log('successfully created new user')
} else {
console.log('failed to create new user')
}
}
});
}
Does someone know what I am doing wrong here?
Edit:
My passportInit:
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const keys = require('../../config/keys')
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new GoogleStrategy({
clientID: keys.google.clientID,
clientSecret: keys.google.cientSecret,
callbackURL: keys.google.callback
},
(token, refreshToken, profile, done) => {
return done(null, {
profile: profile,
token: token
});
}));
};
Edit2: Added cors package:
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const keys = require('./config/keys')
const passport = require('passport');
const express = require('express');
const app = express();
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const cors = require('cors')
const port = 8000;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors({ origin: 'http://localhost' })); //configuring cors
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
app.options('*', cors()) // enable pre-flight across-the-board
require('./authenticate')(app, database,passport);
require('./app/routes')(app, database, passport);
app.listen(port, () => {
console.log('We are live on ' + port);
});
});
I finally was able to solve the problem.
No, Browser Plug-in or something like this is needed!
Please see my Code below.
Frontend-Request:
function register() {
var role = 'wt';
var json = {"role": role};
$.ajax({
url: 'http://localhost:8000/user',
type: 'POST',
data: JSON.stringify(json),
contentType: 'application/json; charset=utf-8',
dataType: 'JSON',
xhrFields: {
withCredentials: true
},
crossDomain: true,
async: false,
success: function (msg) {
var state = msg['state'];
if (state == true) {
console.log('successfully created new WT')
location.href = 'http://localhost:80/WT_Welcome.html'
} else {
console.log('failed to create new WT')
location.href = 'http://localhost:80/index.html'
}
}
});
}
Backend Server.js
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const keys = require('./config/keys')
const passport = require('passport');
const express = require('express');
const app = express();
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const port = 8000;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', "http://localhost");
res.header('Access-Control-Allow-Methods','GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Credentials', true)
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
})
require('./authenticate')(app, database,passport);
require('./app/routes')(app, database, passport);
app.listen(port, () => {
console.log('We are live on ' + port);
});
});
Backend API:
app.post('/user', (req, res) => {
if (!req.isAuthenticated()) { return res.send({'error':'unauthorized'}) } //Authentication fails...
userString = JSON.stringify(req.user);
userObject = JSON.parse(userString)
userId = userObjectValue['profile'].id;
userName = userObjectValue['profile'].name; //not used
userGivenName = userName.givenName;
userRolle= req.body.rolle;
const details = { userId: userId, userGivenName: userGivenName, rolle: userRolle };
console.log(details);
db.collection('users').insert(details, (err, result) => {
if (err) {
res.send({ 'state': false });
} else {
res.send({'state' : true});
}
});
});
I have the following code for handling & subscribing to the push notification on front-end which runs on port 4000:
var endpoint;
var key;
var authSecret;
// We need to convert the VAPID key to a base64 string when we subscribe
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function determineAppServerKey() {
var vapidPublicKey = 'BAyb_WgaR0L0pODaR7wWkxJi__tWbM1MPBymyRDFEGjtDCWeRYS9EF7yGoCHLdHJi6hikYdg4MuYaK0XoD0qnoY';
return urlBase64ToUint8Array(vapidPublicKey);
}
export default function registerServiceWorker() {
if('serviceWorker' in navigator) {
navigator.serviceWorker.register(`${process.env.PUBLIC_URL}\sw.js`).then(function(register){
console.log("worked", register)
return register.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
// We already have a subscription, let's not add them again
return;
}
return register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: determineAppServerKey()
})
.then(function(subscription) {
var rawKey = subscription.getKey ? subscription.getKey('p256dh') : '';
key = rawKey ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : '';
var rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : '';
authSecret = rawAuthSecret ?
btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : '';
endpoint = subscription.endpoint;
alert("came here")
return fetch('http://localhost:3111/register', {
method: 'post',
headers: new Headers({
'content-type': 'application/json'
}),
body: JSON.stringify({
endpoint: subscription.endpoint,
key: key,
authSecret: authSecret,
}),
})
});
});
}).catch(function(err){
console.log("Error",err)
})
}
}
and the server code looks like this:
const webpush = require('web-push');
const express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
const app = express();
// Express setup
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
function saveRegistrationDetails(endpoint, key, authSecret) {
// Save the users details in a DB
}
webpush.setVapidDetails(
'mailto:contact#deanhume.com',
'BAyb_WgaR0L0pODaR7wWkxJi__tWbM1MPBymyRDFEGjtDCWeRYS9EF7yGoCHLdHJi6hikYdg4MuYaK0XoD0qnoY',
'p6YVD7t8HkABoez1CvVJ5bl7BnEdKUu5bSyVjyxMBh0'
);
// Home page
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/index.html'));
});
// Article page
app.get('/article', function (req, res) {
res.sendFile(path.join(__dirname + '/article.html'));
});
// Register the user
app.post('/register', function (req, res) {
var endpoint = req.body.endpoint;
var authSecret = req.body.authSecret;
var key = req.body.key;
// Store the users registration details
saveRegistrationDetails(endpoint, key, authSecret);
const pushSubscription = {
endpoint: req.body.endpoint,
keys: {
auth: authSecret,
p256dh: key
}
};
var body = 'Thank you for registering';
var iconUrl = 'https://raw.githubusercontent.com/deanhume/progressive-web-apps-book/master/chapter-6/push-notifications/public/images/homescreen.png';
webpush.sendNotification(pushSubscription,
JSON.stringify({
msg: body,
url: 'https://localhost:3111',
icon: iconUrl,
type: 'register'
}))
.then(result => {
console.log("came here ")
console.log(result);
res.sendStatus(201);
})
.catch(err => {
console.log(err);
});
});
// The server
app.listen(3111, function () {
console.log('Example app listening on port 3111!')
});
server runs on 3111. When I navigate to 4000 port, I could able to see the Allow/Block pop up comes up and if I give Allow, I expect the server sends Thank you for registering messages as I have done in the server. However the Thank you for registering pop up doesn't comes up and there are no error in the console.
Note: I'm hitting 3111 by enabling CORS using chrome-extension.
I think that that could depend on your server, as i have a python server at home, and when i use the notification api it doesn't notify, unless I am on https sites. If that is not the problem then i would assume there is a code error, but I believe that you could use the broadcast channels and xhr such as : let b = new BroadcastChannel;b.onmessage(function(){XmlHTTPRequest('POST', {Data: "yes"})}; ) and use the xhr to push notifications.