This is the source code that requests the permissions and try to list the circles,
What I want to make is to be able to list the circle's person, add/remove people from the circles but I get a forbidden error when I try to do it, and I get the access to those scopes.
var express, router, nconf, OAuth2, oauth2Client, scopes, google;
scopes = [
'https://www.googleapis.com/auth/plus.me',
'https://www.googleapis.com/auth/plus.circles.read',
'https://www.googleapis.com/auth/plus.circles.write'
];
google = require('googleapis')
nconf = require('nconf');
OAuth2 = google.auth.OAuth2;
express = require('express');
router = express.Router();
oauth2Client = new OAuth2(CLIENT_ID, SECRET_ID, REDIRECT_URL);
router.get('/', function(req, res) {
var url = oauth2Client.generateAuthUrl({
access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
scope: scopes // If you only need one scope you can pass it as string
});
res.redirect(url);
});
router.get('/auth', function(req, res) {
console.log("code >> " + req.query.code);
oauth2Client.getToken(req.query.code, function(err, tokens) {
if(!err) {
console.log("TOKENS >> " + JSON.stringify(tokens));
oauth2Client.setCredentials(tokens);
var plusDomains = google.plusDomains('v1');
plusDomains.circles.list({ userId: 'me', auth: oauth2Client }, function(err, circles) {
console.log('Result circles: ' + (err ? err.message : JSON.stringify(circles)));
});
}
res.send({tokens: tokens});
});
});
module.exports = router;
Related
I have created a login form that should redirect the user to a dashboard page in case he enters the right password and username. If the user tries to navigate to the dashboard url without being logged in the page should not display as it is a protected route. I am trying to send a jwt token when the user logs in, but that doesn't work I just get the Forbidden message when I log in so it seems that the token is not sent correctly, how can I send the jwt token and access the protected route once the user logs in successfully?
Here is my server.js:
const express = require('express');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
let Post = require('./models/post.model.js');
const app = express();
const cors = require('cors');
require('dotenv').config();
app.use(cors());
app.use("/assets", express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({ extended: true }));
const BASE_URL = process.env.BASE_URL;
const PORT = process.env.PORT || 1337;
mongoose.connect(BASE_URL, { useNewUrlParser: true, useUnifiedTopology: true })
const connection = mongoose.connection;
connection.once('open', function () {
console.log('Connection to MongoDB established succesfully!');
});
app.set('view-engine', 'ejs');
app.get('/', (req, res) => {
res.render('index.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
}
jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
res.json({
token
})
});
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard')
}
});
app.get('/dashboard', verifyToken, (req, res) => {
jwt.verify(req.token, process.env.SECRET_KEY, (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
res.sendStatus(200);
}
});
res.render('dashboard.ejs');
});
app.get('/dashboard/createPost', verifyToken, (req, res) => {
res.render('post.ejs');
});
app.post('/dashboard/createPost', async (req, res) => {
let collection = connection.collection(process.env.POSTS_WITH_TAGS);
res.setHeader('Content-Type', 'application/json');
let post = new Post(req.body);
collection.insertOne(post)
.then(post => {
res.redirect('/dashboard')
})
.catch(err => {
res.status(400).send(err);
});
});
// TOKEN FORMAT
// Authorization: Bearer <access_token>
//Verifing the Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization'];
// Check if bearer is undefined
if (typeof bearerHeader !== 'undefined') {
// Spliting the bearer
const bearer = bearerHeader.split(' ');
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbid the route
res.sendStatus(403);
}
}
app.listen(PORT);
see this example, i use middleware(checkAuthLogin), this code contains all thing for your question:
index.js:
const express = require('express');
const app = express();
require('./db/mongoose');
const userRouter = require('./routers/user');
app.use(express.json());
app.use(userRouter);
app.listen(3000, ()=> {
console.log('Server is up on port ', 3000)
});
db/mongoose.js:
const mongoose = require('mongoose');
mongoose.connect("mongodb://127.0.0.1:27017/db-test" {
useNewUrlParser : true,
useCreateIndex : true,
useFindAndModify : false,
useUnifiedTopology: true
});
routers/user.js:
const express = require('express');
const router = new express.Router();
const RootUser = require('../models/root-user');
const {checkRootLogin} = require('../middleware/checkAuthLogin');
router.post('/createrootuser', async (req, res) => {
const updates = Object.keys(req.body);
const allowedUpdatesArray = ['name', 'password'];
const isValidOperation = updates.every((update) => allowedUpdatesArray.includes(update));
if (!isValidOperation) {
return res.status(400).send({error: 'Invalid Request Body'})
}
const rootUser = new RootUser(req.body);
try {
await rootUser.save();
// sendWelcomeEmail(user.email, user.name)
const token = await rootUser.generateAuthToken();
//console.log(user)
res.status(201).send({rootUser, token});
} catch (e) {
res.status(400).send(e)
}
});
//use this middleware(checkRootLogin) for check root user can access this function
router.post('/rootconfig', checkRootLogin, async (req, res) => {
res.status(200).send({success: 'success add root config'})
});
module.exports = router;
model/root-user.js:
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const userRootSchema = new mongoose.Schema({
name: {
type : String,
required: true,
unique : true,
trim : true,
lowercase : true,
},
password: {
type : String,
required: true,
unique : true,
trim : true,
lowercase : true,
minlength : 6,
validate (value) {
//if (validator.contains(value.toLowerCase(), 'password')){
if (value.toLowerCase().includes('password')){
throw new Error('Password can not contained "password"')
}
}
},
tokens : [{
token : {
type : String ,
required : true
}
}],
}, {
timestamps: true
});
userRootSchema.methods.generateAuthToken = async function(){
const root = this;
// generate token
try {
// const token = jwt.sign({ _id : user._id.toString()}, process.env.JWT_SECRET);
const token = jwt.sign({ _id : root._id.toString()}, "test");
// add token to user model
root.tokens = root.tokens.concat({ token });
await root.save();
return token
} catch (e){
throw new Error(e)
}
};
userRootSchema.pre('save', async function(next){
// this give ccess to individual user
const user = this;
if (user.isModified('password')){
user.password = await bcrypt.hash(user.password, 8)
}
next()
});
const UserRoot = mongoose.model('UserRoot', userRootSchema);
module.exports = UserRoot;
middleware/checkAuthLogin.js:
const jwt = require('jsonwebtoken');
const RootUser = require('../models/root-user');
const checkRootLogin = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '');
// const decoded = jwt.verify(token, process.env.JWT_SECRET);
const decoded = jwt.verify(token, "test");
const rootUser = await RootUser.findOne({_id: decoded._id, 'tokens.token': token});
if (!rootUser) {
throw new Error("User cannot find!!");
}
req.token = token;
req.rootUser = rootUser;
req.userID = rootUser._id;
next()
} catch (e) {
res.status(401).send({error: 'Authentication problem!!'})
}
};
module.exports = {checkRootLogin};
Your issue is that your token variable is only accessible inside of the callback to the jwt.sign call, so when you try to do this here res.setHeader('Authorization', 'Bearer '+ token);, it won't know what variable you're referring to, hence the undefined error. By the way, if you're going to use jwt.sign asynchronously, then the code that uses it needs to also be inside of the callback, otherwise synchronous code outside of the callback will likely execute first (and thus not be able to access any results of the asynchronous code) as the asynchronous callback executes in the background. The solution here is to either switch your usage to a synchronous usage or place your response code inside of the callback. Also, calling res.json will end the response so I'm not sure what exactly you're trying to accomplish with the multiple response calls
Synchronous version:
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
};
let token = undefined;
try {
token = jwt.sign({ user }, process.env.SECRET_KEY);
} catch (e) {
// handle error
}
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard');
}
});
Asynchronous version:
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
}
jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard')
}
});
});
In these examples, I took out res.json({ token }) because you can't use res.json and then perform a redirect, but modify those parts however best fits your code. On another note, you probably don't want to include the password in your token because while JWTs (when using the default/standard algorithms which do not include encryption) are cryptographically guaranteed to be unmodifiable, they are still readable
I have one solution to send jwt token, but you will need to install one more package. If you think it worth maybe you can follow.
I use express only for backend api. But you can use the same logic applied here to your application.
The lib you will need to install is the express-jwt
It handles routes to block access to endpoint that need authentication.
server.js
require('dotenv').config()
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
const app = express();
cors({ credentials: true, origin: true });
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/secure', expressJwt({ secret: process.env.SECRET }));
app.use(require('./server/index'));
app.get('/secure/dashboard') => {
//now you can only access this route with authorization header
//prependending the '/secure/ to new routes should make them return 401 when accessed without authorization token
//accessing this route without returns 401.
//there is no need to validate because express-jwt is handling.
console.log(res.user)//should print current user and pass signed with token
res.render('dashboard.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
//jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
// res.json({
// token
// })
//});
//shouldn't sign json here, because there is no guarantee this is a valid
//username and password it can be an impostor
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const user = {
username: username,
password: password
};
const tk = {};
tk.token = 'Bearer ' + jwt.sign(user, process.env.SECRET_KEY, { expiresIn: 1800 });//expires in 1800 seconds
res.status(200).json(tk);
}
});
Now in your frontend put the authorization token sent by this route in cookies or store in client-side.
Do the next request with the header authorization for the secure dashboard route.
I think the problem in the sign in controller function
you must check first if the user have the correct password before attempting to send him a token
you should save the result of jwt sign function in a variable to send back to the user in case he has the right credintials.
It make no sense to send the password again to the user , only the username is needed
you can try this :
app.post('/', (req, res) => {
const {username , password} = req.body;
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
return res.json('Invalid credentials');
}
const token = jwt.sign({username:username }, SECRET)
res.setHeader('Authorization', token);
res.redirect('/dashboard')
});
I am trying to load one of my google sheets with node. I am able to get my access tokens from google but when I try and load the sheet I get the error:
Error: No access to that spreadsheet, check your auth.spreadsheet
Here is my app.js file:
var express = require('express')
var app = express()
CLIENT_ID = "MY_CLIENT_ID";
CLIENT_SECRET = "MY_SECRET";
REDIRECT_URL = "http://127.0.0.1:3000/oauth/callback";
var oauth2Client = new OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
var google = require('googleapis');
var GoogleSpreadsheets = require('google-spreadsheets');
var oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
var url = oauth2Client.generateAuthUrl({
access_type: 'online',
scope: 'https://www.googleapis.com/auth/drive'
});
app.get('/', function (req, res) {
res.redirect(url);
res.send('Hello World');
});
app.get('/oauth/callback', function (req, res) {
var code = req.param('code');
GoogleSpreadsheets({
key: 'MY_SHEET_KEY',
auth: oauth2Client.getToken(code, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
if(!err) {
oauth2Client.setCredentials(tokens);
}
console.log(tokens); //this prints my tokens as an array
})
}, function(err, spreadsheet) {
console.log(err + "spreadsheet");
});
res.send('Hello World');
});
app.listen(3000)
I need a little help figuring out how to get this working -- I've tested and have working JWT authentication and SSL on my '/user' routes. I'm trying to safely allow the user to upload an audio file, also using the JWT and SSL route.
The authentication middleware works, and multer works to let me upload files when I comment out the authentication middleware. However, when I leave the middleware in, the uploaded file is created on my system, but the file fails to upload properly and I get a 404 error.
Thanks for any help!
server.js (main file)
var express = require('express')
, app = express()
, passport = require('passport')
, uploads = require('./config/uploads').uploads
, user_routes = require('./routes/user')
, basic_routes = require('./routes/basic')
, jwt = require('jwt-simple');
// get our request parameters
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Use the passport package in our application
app.use(passport.initialize());
require('./config/passport')(passport);
//double check we have an ssl connection
function ensureSec(req, res, next) {
if (req.headers['x-forwarded-proto'] == 'https') {
return next();
} else {
console.log('NOT SSL PROTECTED! rejected connection.');
res.redirect('https://' + req.headers.host + req.path);
}
}
app.use(ensureSec);
//authenticate all user routes with passport middleware, decode JWT to see
//which user it is and pass it to following routes as req.user
app.use('/user', passport.authenticate('jwt', {session:false}), user_routes.middleware);
//store info on site usage- log with ID if userRoute
app.use('/', basic_routes.engagementMiddleware);
// bundle our user routes
var userRoutes = express.Router();
app.use('/user', userRoutes);
userRoutes.post('/upload', uploads:q, function(req,res){
res.status(204).end("File uploaded.");
});
// Start the server
app.listen(port);
routes/basic_routes.js (tracks engagement middleware)
var db = require('../config/database')
, jwt = require('jwt-simple')
, getIP = require('ipware')().get_ip
, secret = require('../config/secret').secret;
exports.engagementMiddleware = function(req, res, next){
if (typeof(req.user) == 'undefined') req.user = {};
var postData = {};
var ip = getIP(req).clientIp;
var fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
if (req.method=="POST") postData = req.body;
var newEngagement = new db.engagementModel({
user_id: req.user._id,
ipAddress: ip,
url: fullUrl,
action: req.method,
postData: postData
});
//log the engagement
newEngagement.save(function(err) {
if (err) {
console.log('ERROR: engagement middleware db write failed');
next();
}
console.log('LOG: user ' + req.user._id +' from ipAddress: ' + ip + ': ' + req.method + ' ' + fullUrl);
next();
});
next();
}
config/passport.js (passport authentication middleware)
var JwtStrategy = require('passport-jwt').Strategy;
// load up the user model
var db = require('../config/database'); // get db config file
var secret = require('../config/secret').secret;
module.exports = function(passport) {
var opts = {};
opts.secretOrKey = secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
db.userModel.findOne({id: jwt_payload.id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
routes/user_routes.js (user route middleware, user add to header)
var jwt = require('jwt-simple');
var db = require('../config/database');
var secret = require('../config/secret').secret;
//expose decoded userModel entry to further routes at req.user
exports.middleware = function(req, res, next){
var token = getToken(req.headers);
if (token) req.user = jwt.decode(token, secret);
else res.json({success: false, msg: 'unable to decode token'});
//should be unnecessary, double checking- after token verification against db
db.userModel.findOne({email: req.user.email}, function (err, user) {
if( err || !user ) {
console.log('something has gone horribly wrong. Token good, no user in db or access to db.');
return res.status(403).send({success: false, msg: 'unable to find user in db'});
}
});
//end unnecessary bit
next();
}
//helper function
getToken = function (headers) {
if (headers && headers.authorization) {
var parted = headers.authorization.split(' ');
if (parted.length === 2) return parted[1];
else return null;
} else { return null; }
};
config/uploads.js (finally where we try to upload)
var moment = require('moment');
var multer = require('multer');
var jwt = require('jwt-simple');
var uploadFile = multer({dest: "audioUploads/"}).any();
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'audioUploads/')
},
filename: function (req, file, cb) {
cb(null, req.user._id + '_' + moment().format('MMDDYY[_]HHmm') + '.wav')
}
});
exports.uploads = multer({storage:storage}).any();
in your server.js do this:
const authWare = passport.authenticate('jwt', {session:false});
userRoutes.post('/upload', authWare, uploads:q, function(req,res){
res.status(204).end("File uploaded.");
});
works for me!
I'd try to get Extended User Access Token from OAuth2 with Nodejs
Actually, the program send out a message (on Browser) with:
XMLHttpRequest cannot load https://www.facebook.com/dialog/oauth?redirect_uri=http%3A%2F%2Flocalhost%3…2Fcallback&scope=user_about_me%2Cpublish_actions&client_id=487932668044758. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
Using standard express generator, I modified a code in a index.js as below:
var express = require('express');
var request = require('request');
var OAuth2 = require('oauth').OAuth2;
var router = express.Router();
var oauth2 = new OAuth2("487932668044758",
"0793918b3ab637b2096787e10643980a",
"", "https://www.facebook.com/dialog/oauth",
"https://graph.facebook.com/oauth/access_token",
null);
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', {
// title: 'Express'
});
});
router.post('/userToken', function(req, res) {
if (!req.body) {
return
res.sendStatus(400)
}
var uid = req.body.uid;
var token = req.body.token;
validate_uid_token(uid, token, res);
console.log('req:', req.body);
console.log("uid: ", uid);
console.log('token: ', token);
// res.send("okie");
});
router.get("/callback", function(req, res) {
console.log('here..form callback');
res.header('Access-Control-Allow-Origin', 'http://localhost3000');
res.header('Access-Control-Allow-Origin', 'https://www.facebook.com/dialog/oauth');
res.header('Access-Control-Allow-Origin', 'https://graph.facebook.com/oauth/access_token');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
if (req.error_reason) {
res.send(req.error_reason);
}
if (req.query.code) {
var loginCode = req.query.code;
var redirect_uri = "/"; // Path_To_Be_Redirected_to_After_Verification
// For eg. "/facebook/callback"
oauth2.getOAuthAccessToken(loginCode, {
grant_type: 'authorization_code',
redirect_uri: redirect_uri
},
function(err, accessToken, refreshToken, params) {
if (err) {
console.error(err);
res.send(err);
return;
}
var access_token = accessToken;
console.log('access_token : ', access_token);
// var expires = params.expires;
// console.log('expires : ', expires);
req.session.access_token = access_token;
// req.session.expires = expires;
}
);
}
// res.send('okie');
});
function validate_uid_token(uid, token, res) {
var redirect_uri = "http://localhost:3000" + "/callback";
// For eg. "http://localhost:3000/facebook/callback"
var params = {
'redirect_uri': redirect_uri,
'scope': 'user_about_me,publish_actions'
};
res.redirect(oauth2.getAuthorizeUrl(params));
}
module.exports = router;
Click on a url in error message. Browser throws out ma JSON message as
{"statusCode":400,"data":"{\"error\":{\"message\":\"redirect_uri isn't an absolute URI. Check RFC 3986.\",\"type\":\"OAuthException\",\"code\":191}}"}
Could someone help me please...Thank to much..
I'd recommend to use Passport.js together with the passport-facebook strategy. This hides away most of the complexity involved.
See
http://passportjs.org/
https://github.com/jaredhanson/passport-facebook
Information on the express-jwt module can be found here:
https://github.com/auth0/express-jwt
https://www.npmjs.com/package/express-jwt
In my main.js server file, I have the following:
import ExpressJwt from 'express-jwt';
// import other crap...
let token = ExpressJwt({
secret: 'whatever',
audience: 'whatever',
issuer: 'whatever'
});
app.all('/apiv1', token.unless({ path: ['apiv1/user/create', '/apiv1/auth/login']}));
app.use('/apiv1/user', user);
app.use('/apiv1/auth', auth);
Where user and auth are the middlewares that handle my routes. What I want to do is obvious; deny API access to all unauthenticated users, except when they attempt to create a new user via apiv1/user/create and/or login via apiv1/auth/login.
Any time I try to make a request to the aforementioned unprotected paths however, I get the error:
UnauthorizedError: No authorization token was found
It's still protecting the routes I specified to be unprotected! I also tried:
app.use('/apiv1/user', token.unless({ path: ['/apiv1/user/create'] }), user);
app.use('/apiv1/auth', token.unless({ path: ['/apiv1/auth/login'] }), auth);
But that didn't work. I also tried using regex for the unless paths, but that didn't work either.
I arrived at app.all('/apiv1', token...) via this answer, but that solution does not yield me the desired functionality.
Instead of using all:
app.all('/apiv1', token.unless({ path: ['apiv1/user/create', '/apiv1/auth/login']}));
Try using use and adding in the path route a slash / at the beginning:
app.use('/apiv1', token.unless({ path: ['/apiv1/user/create', '/apiv1/auth/login']}));
Here it is an example that is working:
app.js:
var express = require('express');
var app = express();
var expressJwt = require('express-jwt');
var jwt = require('jsonwebtoken');
var secret = 'secret';
app.use('/api', expressJwt({secret: secret}).unless({path: ['/api/token']}));
app.get('/api/token', function(req, res) {
var token = jwt.sign({foo: 'bar'}, secret);
res.send({token: token});
});
app.get('/api/protected', function(req, res) {
res.send('hello from /api/protected route.');
});
app.use(function(err, req, res, next) {
res.status(err.status || 500).send(err);
});
app.listen(4040, function() {
console.log('server up and running at 4040 port');
});
module.exports = app;
test.js:
var request = require('supertest');
var app = require('./app.js');
describe('Test API', function() {
var token = '';
before(function(done) {
request(app)
.get('/api/token')
.end(function(err, response) {
if (err) { return done(err); }
var result = JSON.parse(response.text);
token = result.token;
done();
});
});
it('should not be able to consume /api/protected since no token was sent', function(done) {
request(app)
.get('/api/protected')
.expect(401, done);
});
it('should be able to consume /api/protected since token was sent', function(done) {
request(app)
.get('/api/protected')
.set('Authorization', 'Bearer ' + token)
.expect(200, done);
});
});