When attempting to use the Facebook api to get the friends list of a verified account it seems to work except that the friends list returned is empty.
facebook.js
var https = require('https');
exports.getFbData = function(accessToken, apiPath, callback) {
var options = {
host: 'graph.facebook.com',
port: 443,
path: apiPath + '?access_token=' + accessToken, //apiPath example: '/me/friends'
method: 'GET'
};
var buffer = ''; //this buffer will be populated with the chunks of the data received from facebook
var request = https.get(options, function(result){
result.setEncoding('utf8');
result.on('data', function(chunk){
buffer += chunk;
});
result.on('end', function(){
callback(buffer);
});
});
request.on('error', function(e){
console.log('error from facebook.getFbData: ' + e.message)
});
request.end();
}
app.js
app.get('/', function (req, res) {
if (req.session.myID != null && req.session.myName != null) {
User.findOne({sessionID: req.session.myID, username: req.session.myName}, function (err, doc) {
if (err) {throw err}
else if (doc != null) {
facebook.getFbData(doc.facebookToken, '/me/friends', function(data){
console.log(data);
res.render('index');
});
}
else {
//console.log("not logged in");
res.render('index');
}
});
}
else {
//console.log("not logged in");
res.render('index');
}
});
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET',
callbackURL: "http://localhost:3000/auth/facebook/callback",
profileFields: ['id', 'displayName']
},
function(accessToken, refreshToken, profile, done) {
User.findOne(..., function(err, user) {
if (err) { return done(err); }
user.facebookID = profile.id;
user.facebookToken = accessToken;
user.save();
return done(null, user);
});
}
));
app.get('/auth/facebook', passport.authenticate('facebook',
{scope: 'user_friends'})
);
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
}
);
The console.log in the facebook.getFbData callback prints:
{"data":[]}
This code actually works correctly. However it does not fetch the whole friends list, only the list of friends who also have the app.
Related
I'm having trouble accessing the access token of a Facebook user.
Here's my code
const urlToGetAccessToken =
"https://graph.facebook.com/v13.0/oauth/access_token";
const stateRootUrl = "dd";
const urlToGetOauthCode = "https://www.facebook.com/v13.0/dialog/oauth";
const urlToRedirectTo = process.env.BASE_CLIENT_URL + "/login?type=facebook";
router.get("/oauth", (req, res, next) => {
const FacebookApi = {
clientId: process.env.FACEBOOK_CLIENT_ID,
redirectUrl: urlToRedirectTo,
oauthUrl: urlToGetOauthCode,
scope: "email,public_profile",
state: `${stateRootUrl}`,
};
const {
clientId,
redirectUrl,
oauthUrl,
scope,
state
} = FacebookApi;
const url = `${oauthUrl}?response_type=code&client_id=${clientId}&scope=${scope}&state=${state}&redirect_uri=${redirectUrl}`;
return res.json({
status: "ok",
result: url,
});
});
router.get("/oauth/callback", (req, res, next) => {
if (req.query.code) {
const FacebookApi = {
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
redirectUrl: urlToRedirectTo
};
const {clientId, clientSecret, redirectUrl} = FacebookApi;
const url = urlToGetAccessToken +
"?client_id=" +
clientId +
"&client_secret=" +
clientSecret +
"&redirect_uri=" +
redirectUrl +
"&code=" +
req.query.code;
var op = {
method: "GET",
uri: url,
json: true, //Parse the JSON string in the response
};
request.get(op, async (error, response, body) => {
if (error) {
console.dir("Error " + error);
return res.json({
status: "error",
error,
});
}
if (response && response.body && response.body.access_token) {
const accessToken = response.body.access_token;
const userProfile = await FacebookService.getUserProfile(accessToken);
if (userProfile && userProfile.email && userProfile.email.length) {
User.findOrCreateFacebookUser(userProfile.id, userProfile.email, userProfile.first_name, userProfile.last_name)
.then((result) => {
if (result && result.length) {
const user = result[0];
if (user.status === 1) {
return res.json({
status: "error",
error: "Your user account has been flagged. This may happen if you missed too many calls.",
});
}
const payload = {
sub: user.id,
};
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRESIN,
});
res.clearCookie("auth");
res.cookie("auth", token);
return res.json({
status: "ok",
result: {
token,
user
}
});
} else {
return res.json({
status: "error",
error: "There was an error processing your request. Please try another login method.",
});
}
})
.catch((err) => {
console.dir("Error " + err);
return res.json({
status: "error",
error: err,
});
});
} else {
console.dir("Facebook Oauth couldn't get email address");
return res.json({
status: "error",
error: "There was an error processing your request. Please try another login method.",
});
}
} else {
console.dir("Facebook Oauth couldn't get access token");
return res.json({
status: "error",
error: "There was an error processing your request. Please try another login method.",
});
}
});
}
});
I get the error An active access token must be used to query information about the current user.
when trying to make a call to the graph api
async getUserProfile(accessToken) {
var defer = Q.defer();
var op = {
method: "GET",
uri: "https://graph.facebook.com/v13.0/me",
json: true, //Parse the JSON string in the response
params: {
fields: ['id', 'email', 'first_name', 'last_name'].join(','),
access_token: accessToken,
}
};
console.dir(op);
request.get(op, async (error, response, body) => {
if (error) {
defer.reject(error);
} else if (response && response.body) {
defer.resolve(response.body);
} else {
defer.reject();
}
});
return defer.promise;
};
}
Does anyone know what I am doing wrong?
So, I am making an e-shop app which uses Mongo DB and Express JS as the backend. I have already created the productSchema, userSchema and the categorySchema and have coded for the appropriate GET requests.
I have made a jwt.js file which handles whether the the GET request should be allowed or not based on the token.
The code for jwt.js is given below
const { expressjwt } = require("express-jwt");
function authJwt() {
const secret = process.env.secret;
const api = process.env.API_URL;
return expressjwt({
secret,
algorithms: ["HS256"],
isRevoked: isRevoked,
}).unless({
path: [
{ url: /\/api\/v1\/products(.*)/, methods: ["GET", "OPTIONS"] },
{ url: /\/api\/v1\/categories(.*)/, methods: ["GET", "OPTIONS"] },
`${api}/users/login`,
`${api}/users/register`,
],
});
}
async function isRevoked(req, payload, done) {
if (!payload.isAdmin) {
done(null, true);
}
done();
}
module.exports = authJwt;
The code for products.js which handles the GET, POST, PUT and DELETE requests for the products database is given below.
const { Product } = require("../models/product");
const express = require("express");
const { Category } = require("../models/category");
const router = express.Router();
const mongoose = require("mongoose");
router.get(`/`, async (req, res) => {
// localhost:3000/api/v1/products?categories=2342342,234234
let filter = {};
if (req.query.categories) {
filter = { category: req.query.categories.split(",") };
}
const productList = await Product.find(filter).populate("category");
if (!productList) {
res.status(500).json({ success: false });
}
res.send(productList);
});
router.get(`/:id`, async (req, res) => {
const product = await Product.findById(req.params.id).populate("category");
if (!product) {
res.status(500).json({ success: false });
}
res.send(product);
});
router.post(`/`, async (req, res) => {
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
let product = new Product({
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
});
product = await product.save();
if (!product) return res.status(500).send("The product cannot be created");
res.send(product);
});
router.put("/:id", async (req, res) => {
if (!mongoose.isValidObjectId(req.params.id)) {
return res.status(400).send("Invalid Product Id");
}
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
const product = await Product.findByIdAndUpdate(
req.params.id,
{
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
},
{ new: true }
);
if (!product) return res.status(500).send("the product cannot be updated!");
res.send(product);
});
router.delete("/:id", (req, res) => {
Product.findByIdAndRemove(req.params.id)
.then((product) => {
if (product) {
return res
.status(200)
.json({ success: true, message: "the product is deleted!" });
} else {
return res
.status(404)
.json({ success: false, message: "product not found!" });
}
})
.catch((err) => {
return res.status(500).json({ success: false, error: err });
});
});
router.get(`/get/count`, async (req, res) => {
const productCount = await Product.countDocuments((count) => count);
if (!productCount) {
res.status(500).json({ success: false });
}
res.send({
productCount: productCount,
});
});
router.get(`/get/featured/:count`, async (req, res) => {
const count = req.params.count ? req.params.count : 0;
const products = await Product.find({ isFeatured: true }).limit(+count);
if (!products) {
res.status(500).json({ success: false });
}
res.send(products);
});
module.exports = router;
Now, the codes for the users.js and categories.js are similar and thus I am not sharing it.
I am getting the problem when doing GET request for products using POSTMAN API. Even though I am passing the correct token using BEARER TOKEN field in the POSTMAN API, it is getting stuck at sending request. When I delete the isRevoked part, everything works fine, but then again I can't control the get request based on the isAdmin part. So, the problem is in the isRevoked part. But, what exactly is the issue. It seems fine to me logically.
the problem could arise from so many things, could not say without a deeper look at your code but, here are some suggestions:
should isRevoked be async?
does your payload contains isAdmin?
and if so, inside the if statement should be done(null, false) after the if statement you should get a userid or any sort of unique fields such as userEmail, ..., then use your userModel to query the user document so that your last done() be done(null, user)
I am trying to get the AccessKeyID and the SecretKey (ultimately to programatically input into a CRUD operation on dynamoDB with fine grained access control).
Anywho, there are many API calls to get the credentials like getCredentialsForIdentity(). However, these all require further params, which requires more api requests and more params and so on.
Ive found a way to log a Cognito user in, and then check if they are logged in on for a profile page, where it can display the email and username:
var data = {
UserPoolId : _config.cognito.userPoolId,
ClientId : _config.cognito.clientId
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(data);
var cognitoUser = userPool.getCurrentUser();
window.onload = function(){
if (cognitoUser != null) {
cognitoUser.getSession(function(err, session) {
if (err) {
alert(err);
return;
}
console.log('session validity: ' + session.isValid());
console.log(session.Credentials.AccessKeyId); //<--THIS DOESNT WORK
cognitoUser.getUserAttributes(function(err, result) {
if (err) {
console.log(err);
return;
}
console.log(result);
document.getElementById("email_value").innerHTML = result[2].getValue();
document.getElementById("username").innerHTML = cognitoUser.getUsername();
});
});
}
}
console.log(session.Credentials.AccessKeyId); seems to be an invalid request. Im assuming there's some good callback info in the session, like the AccessKeyID and SecretKey, which is what I'm looking for.
session.Credentials.AccessKeyId will not work since session's output is:
e {idToken: t, refreshToken: e, accessToken: t, clockDrift: 0}
whereas AWS.config.credentials's output is:
Thus your code will be:
var data = {
UserPoolId : _config.cognito.userPoolId,
ClientId : _config.cognito.clientId
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(data);
var cognitoUser = userPool.getCurrentUser();
window.onload = function(){
if (cognitoUser != null) {
cognitoUser.getSession(function(err, session) {
if (err) {
alert(err);
return;
}
console.log('session validity: ' + session.isValid());
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: _config.cognito.IdentityPoolId
});
AWS.config.credentials.get(function(err) {
console.log(AWS.config.credentials); // * see above what your response will be
console.log(AWS.config.credentials.accessKeyId);
console.log(AWS.config.credentials.secretAccessKey);
});
}
}
I want to use custom API to evaluate data which are posted by applications but remote methods are not accepted in middleware in loopback
module.exports = function () {
const http = require('https');
var request = require('request');
var { Lib } = require('Lib');
var lib = new Lib;
verification.checkID = function (ID, cb) {
cb(null, 'ID is :' + ID);
}
verification.remoteMethod('greet', {
accepts: {
arg: 'ID',
type: 'string'
},
returns: {
arg: 'OK',
type: 'string'
}
});
module.exports = function () {
const http = require('https');
var request = require('request');
var { Lib } = require('Lib');
var lib = new Lib;
verification.checkID = function (ID, cb) {
cb(null, 'ID is :' + ID);
}
verification.remoteMethod('greet', {
'http': { // add the verb here
'path': '/greet',
'verb': 'post'
},
accepts: {
arg: 'ID',
type: 'string'
},
returns: {
arg: 'OK',
type: 'string'
}
});
Update
module.exports = function(server) {
// Install a `/` route that returns server status
var router = server.loopback.Router();
router.get('/', server.loopback.status());
router.get('/ping', function(req, res) { // your middle ware function now you need to call the next() here
res.send('pong');
});
server.use(router);
};
To evaluate is something i am not getting please check this link too Intercepting error handling with loopback
Regarding to fallowing question How to make a simple API for post method?
I find my solution in fallowing way:
module.exports = function(server) {
const https = require('https');
var request = require('request');
return function verification(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Credentials', true);
var request;
var response;
var body = '';
// When a chunk of data arrives.
req.on('data', function (chunk) {
// Append it.
body += chunk;
});
// When finished with data.
req.on('end', function () {
// Show what just arrived if POST.
if (req.method === 'POST') {
console.log(body);
}
// Which method?
switch (req.method) {
case 'GET':
Verify url and respond with appropriate data.
handleGet(req, res);
Response has already been sent.
response = '';
break;
case 'POST':
// Verify JSON request and respond with stringified JSON response.
response = handlePost(body);
break;
default:
response = JSON.stringify({ 'error': 'Not A POST' });
break;
}
// Send the response if not empty.
if (response.length !== 0) {
res.write(response);
res.end();
}
// Paranoid clear of the 'body'. Seems to work without
// this, but I don't trust it...
body = '';
});
// If error.
req.on('error', function (err) {
res.write(JSON.stringify({ 'error': err.message }));
res.end();
});
//
};
function handlePost(body) {
var response = '';
var obj = JSON.parse(body);
// Error if no 'fcn' property.
if (obj['fcn'] === 'undefined') {
return JSON.stringify({ 'error': 'Request method missing' });
}
// Which function.
switch (obj['fcn']) {
// Calculate() requres 3 arguments.
case 'verification':
// Error if no arguments.
if ((obj['arg'] === 'undefined') || (obj['arg'].length !== 3)) {
response = JSON.stringify({ 'error': 'Arguments missing' });
break;
}
// Return with response from method.
response = verification(obj['arg']);
break;
default:
response = JSON.stringify({ 'error': 'Unknown function' });
break;
}
return response;
};
function verification(arg) {
var n1 = Number(arg[0]);
var n2 = Number(arg[1]);
var n3 = Number(arg[2]);
var result;
// Addem up.
result = n1 + n2 + n3;
// Return with JSON string.
return JSON.stringify({ 'result': result });
};
};
Okay so I'm trying to create a simple todo list, web api. I have the basic functions implemented and working properly but I'm trying to use a query to search by task_name as declared in my code, but no matter what I can't seem to get it functioning.
app.js
var express = require('express')
, routes = require('./routes')
, http = require('http')
, tasks = require('./routes/tasks')
, mongoose = require('mongoose');
// MongoDB Connection
mongoose.connect('mongodb://localhost/task_tracker');
var app = express();
app.configure(function(){
app.set('port', 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
app.use(express.errorHandler());
});
app.get('/', routes.index);
app.get('/tasks', tasks.index);
app.get('/tasks/:id', tasks.show);
//app.get('/tasks/tasks?', tasks.search);
app.get('/tasks?', tasks.search);
app.post('/tasks', tasks.create);
app.put('/tasks', tasks.update);
app.del('/tasks', tasks.delete);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port 3000");
});
tasks.js
var Task = require('../models/task').Task;
/*
* Tasks Routes
*/
exports.index = function(req, res) {
Task.find({}, function(err, docs) {
if(!err) {
res.json(200, { tasks: docs });
} else {
res.json(500, { message: err });
}
});
}
exports.show = function(req, res) {
var id = req.params.id;
Task.findById(id, function(err, doc) {
if(!err && doc) {
res.json(200, doc);
} else if(err) {
res.json(500, { message: "Error loading task." + err});
} else {
res.json(404, { message: "Task not found."});
}
});
}
exports.create = function(req, res) {
var task_name = req.body.task_name; // Name of task.
var description = req.body.task_description; // Description of the task
//Task.findOne({ name: task_name }, function(err, doc) { // This line is case sensitive.
Task.findOne({ name: { $regex: new RegExp(task_name, "i") } }, function(err, doc) { // Using RegEx - search is case insensitive
if(!err && !doc) {
var newTask = new Task();
newTask.name = task_name;
newTask.description = description;
newTask.save(function(err) {
if(!err) {
res.json(201, {message: "Task created with name: " + newTask.name });
} else {
res.json(500, {message: "Could not create task. Error: " + err});
}
});
} else if(!err) {
// User is trying to create a task with a name that already exists.
res.json(403, {message: "Task with that name already exists, please update instead of create or create a new task with a different name."});
} else {
res.json(500, { message: err});
}
});
}
exports.update = function(req, res) {
var id = req.body.id;
var task_name = req.body.task_name;
var task_description = req.body.task_description;
Task.findById(id, function(err, doc) {
if(!err && doc) {
doc.name = task_name;
doc.description = task_description;
doc.save(function(err) {
if(!err) {
res.json(200, {message: "Task updated: " + task_name});
} else {
res.json(500, {message: "Could not update task. " + err});
}
});
} else if(!err) {
res.json(404, { message: "Could not find task."});
} else {
res.json(500, { message: "Could not update task." + err});
}
});
}
exports.delete = function(req, res) {
var id = req.body.id;
Task.findById(id, function(err, doc) {
if(!err && doc) {
doc.remove();
res.json(200, { message: "Task removed."});
} else if(!err) {
res.json(404, { message: "Could not find task."});
} else {
res.json(403, {message: "Could not delete task. " + err });
}
});
}
exports.search = function(req, res) {
var name = req.query.name;
Task.findByName(name, function(err, doc) {
if(!err && doc) {
res.json(200, doc);
} else if(err) {
res.json(500, { message: "Error loading task." + err});
} else {
res.json(404, { message: "Task not found."});
}
});
}
task.js model
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
var taskSchema = new Schema({
name : { type: String, required: true, trim: true, index: { unique: true } }
, description : { type: String, required: true }
, date_created : { type: Date, required: true, default: Date.now }
});
var task = mongoose.model('task', taskSchema);
module.exports = {
Task: task
};
Basically i am just trying to use a similar function to that of my search by id function but i know i can't just use parameters and I can't figure out how to get the query working. Any help would be appreciated. If you can't tell I'm using Node.js, Express and Mongodb.
TL;DR: You need to merge tasks.index and tasks.search route, ie. like this:
tasks.index = function(req, res, next) {
if (req.query.name !== undefined) {
// pass on to next handler
return next();
}
// the rest of your tasks.index.
});
And adjust the Router setup like this:
app.get('/tasks', tasks.index);
app.get('/tasks', tasks.search);
Why? Query string is not part of the route. So '/tasks?' is just a regex for /tasks+1 character, but not for a query string - query string is not a part of the route match.
More specifically, you have in your routes this:
app.get('/', routes.index);
app.get('/tasks', tasks.index);
app.get('/tasks?', tasks.search);
That last, /tasks? route will not get registered like you seem to expect. The question mark isn't representing query string processing, it's a part of the route regex, and basically means that you'd catch anything that adds one character to /tasks route, ie /tasksa, /tasksb, /tasks7 etc.
So, 7 characters, first six of which are known, the last is different, query string not included.
You cannot parse query strings in the router, it's in the individual controllers, kind of like this:
tasks.search = function(req, res) {
if (req.query.name) {
// you have the name query
}
// etc.
}
Additional advice is, what is usually done on a REST API is have the global tasks.index, like you have there, and add two things on it: paging and filter/searching.
If you want just one result
Paging is page=3&limit=10 (3rd page, 10 items per page), and filtering/sorting/searching is what you want. And depending how you want it, that's how you expose it.
Ie. you might want to sort by name:
if (req.query.sort === 'name:desc') {
mongoCursor.sort = {name: -1};
}
Or something of a sort.
So you'd probably have a search, or maybe directly a name query parameter, like this:
GET /tasks?name=<search term>
And the name param is usually optional.
So your req would list all things, and if name query string is set, it would filter by name first.
Your query building process can then look like this:
tasks.index = function(req, res) {
var query = {};
if (req.query.name) {
query.name = req.query.name;
}
Tasks.find(query, ...);
In that case, you don't need helpers on the Task model.
I found this method also works.
/**
* Module dependencies.
*/
var express = require('express'),
cors = require('cors'),
routes = require('./routes'),
http = require('http'),
tasks = require('./routes/tasks'),
mongoose = require('mongoose'),
search = require('./routes/search');
var Task = require('./models/task').Task;
// MongoDB Connection
mongoose.connect('mongodb://localhost/task_tracker');
var app = express();
app.configure(function() {
app.set('port', 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.urlencoded());
app.use(express.json());
app.use(cors());
});
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
var corsOptions = {
origin: 'http://localhost:3000'
};
app.get('/', routes.index);
app.get('/tasks', tasks.index);
//app.get('/search', tasks.FindByQuery);
//app.get('/tasks/:task.:name?', task.FindByQuery);
app.get('/search', function(req, res, next) {
var query = req.query
//res.send(query['name']);
Task.findOne({name: query['name']}, function(err, doc) {
if(!err && doc) {
res.json(200, doc);
} else if(err) {
res.json(500, { message: "Error loading task." + err});
} else {
res.json(404, { message: "Task not found."});
}
});
//res.end(JSON.stringify(query));
});
app.get('/tasks/:id', tasks.show);
app.post('/tasks', tasks.create);
app.put('/tasks', tasks.update);
app.del('/tasks', tasks.delete);
http.createServer(app).listen(app.get('port'), function() {
console.log("Express server listening on port 3000");
});