Cannot access Facebook access token - facebook-graph-api

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?

Related

hapi authentication strategy karma test with sinon with async function

I am trying to test the authentication scheme with hapi server. I have two helper function within the same file where I put my authentication scheme. I want to test when this successfully authenticate the user. But in my test case I always get 401 which is the unauthenicated message.
export const hasLegitItemUser = async (request, email, id) => {
const {
status,
payload: {users}
} = await svc.getRel(request, email);
if (status !== STATUS.OK) {
return false;
}
return users.includes(user)
};
export const getUser = async request => {
const token = request.state._token;
const res = await svc.validateToken({request, token});
const {
userInfo: {email}
} = res;
const id = extractId(request.path);
const isLetgitUser = await hasLegitItemUser(
request,
email,
id
);
res.isLegitUser = isLegitUser;
return res;
};
const scheme = (server, options) => {
server.state("my_sso", options.cookie);
server.ext("onPostAuth", (request, h) => {
return h.continue;
});
return {
async authenticate(request, h) {
try {
const {
tokenValid,
isLegitUser,
userInfo
} = await getUser(request);
if (tokenValid && isLegitUser) {
request.state["SSO"] = {
TOKEN: request.state._token
};
return h.authenticated({
credentials: {
userInfo
}
});
} else {
throw Boom.unauthorized(null,"my_auth");
}
} catch (err) {
throw Boom.unauthorized(null, "my_auth");
}
}
};
};
My Test file:
import Hapi from "hapi";
import sinon from "sinon";
import auth, * as authHelpers from "server/auth";
import {expect} from "chai";
import pcSvc from "server/plugins/services/pc-svc";
describe("Authentication Plugin", () => {
const sandbox = sinon.createSandbox();
const server = new Hapi.Server();
const authHandler = request => ({
credentials: request.auth.credentials,
artifacts: "boom"
});
before(() => {
server.register({
plugin: auth,
});
const route = ["/mypage/{id}/home"];
route.forEach(path => {
server.route({
method: "GET",
path,
options: {
auth: auth,
handler:{}
}
});
});
});
afterEach(() => {
sandbox.restore();
});
it("should authorize user if it is a validated user", async () => {
sandbox
.stub(authHelpers, "getUser")
.withArgs(request)
.resolves({
tokenValid: true,
isLegitUser: true,
userInfo: {}
});
return server
.inject({
method: "GET",
url:
"/mypage/888/home"
})
.then(res => {
expect(res.statusCode).to.equal(200);
expect(res.result).to.eql({
userInfo: {
email: "abc#gmail.com",
rlUserId: "abc",
userId: "abc#gmail.com"
}
});
});
});
});
I always get the 401 error for unauthenticated. It seems like my "getUser" function in my test is not triggering for some reason, it goes straight to the throw statement in the catch phase in my code. Please help.

How to POST data for evaluation in middleware in loopback?

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 });
};
};

How to use graph API with react-native-fbsdk?

I read the document, both on github and Facebook developers docs.
There is only sample, nothing more. No API document.
The code to make a Graph API request is
const infoRequest = new GraphRequest(
'/me',
null,
this._responseInfoCallback,
);
And the callback
_responseInfoCallback(error: ?Object, result: ?Object) {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
alert('Success fetching data: ' + result.toString());
}
}
And here is the function to make a Graph API request
testRequestGraphAPI(){
const infoRequest = new GraphRequest(
'/me',
null,
this._responseInfoCallback,
);
new GraphRequestManager().addRequest(infoRequest).start();
}
However, I can't find any further document. I have no idea what each parameters do.
The result for these codes above is this.
I also don't know how to get the result.
However, when I try to modify '\me' to 'me?fields=id,name', It failed.
Although I have asked for permission
<LoginButton
publishPermissions={["publish_actions,user_birthday, user_religion_politics, user_relationships, user_relationship_details, user_hometown, user_location, user_likes, user_education_history, user_work_history, user_website, user_managed_groups, user_events, user_photos, user_videos, user_friends, user_about_me, user_status, user_games_activity, user_tagged_places, user_posts, user_actions.video, user_actions.news, user_actions.books, user_actions.music, user_actions.fitness, public_profile, basic_info"]}
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
meow_accesstoken = data.accessToken
alert(meow_accesstoken.toString())
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
But it does not print out what error, just object Object.
So, the problem is that I don't understand the sample code which Facebook provide with no explanation.
Here is my question that I really need you help me:
First at all, please check the javascript code that I currently looking at?
How to use graph API in react-native-fbsdk to retrieve some user information (example: full name) and successfully display it (use alert) ?
What each parameters in GraphRequest() do ?
What is the structure of error object and result object in _responseInfoCallback ?
SOLUTION
Thanks to #Samuel answer, I have updated my code
testRequestGraphAPI: function(){
const infoRequest = new GraphRequest(
'/me',
{
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name' // what you want to get
},
access_token: {
string: meow_accesstoken.toString() // put your accessToken here
}
}
},
this._responseInfoCallback // make sure you define _responseInfoCallback in same class
);
new GraphRequestManager().addRequest(infoRequest).start();
}
And the callback
_responseInfoCallback: function(error: ?Object, result: ?Object) {
alert("meow response");
if (error) {
alert('Error fetching data: ' + error.toString());
console.log(Object.keys(error));// print all enumerable
console.log(error.errorMessage); // print error message
// error.toString() will not work correctly in this case
// so let use JSON.stringify()
meow_json = JSON.stringify(error); // error object => json
console.log(meow_json); // print JSON
} else {
alert('Success fetching data: ' + result.toString());
console.log(Object.keys(result));
meow_json = JSON.stringify(result); // result => JSON
console.log(meow_json); // print JSON
}
}
*Note: For console.log(), you need to use "Debug JS remotely" then open Chrome developer tools to see the log.
Unfortunately the react-native-fbsdk documentation is not updated and the examples do not work well.
I got the same problem and I solved it by try and error.
To solve your problem you'll need to change your GraphRequest adding params and fields to it like this:
<LoginButton
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
alert('Success fetching data: ' + result.toString());
}
}
const infoRequest = new GraphRequest(
'/me',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
},
responseInfoCallback
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
You'll need to enable the Remote JS Debug to see the console.log() info.
https://facebook.github.io/react-native/docs/debugging.html
And probably you need to get some permissions to get more info than names and email so it's a good idea to look the Facebook Graph API Documentation: https://developers.facebook.com/docs/graph-api/overview/
Reference:
https://github.com/facebook/react-native-fbsdk/issues/105#issuecomment-206501550
Here is an example of a custom button if you want to make one :)
FbLoginButton() {
LoginManager
.logInWithReadPermissions(['public_profile'])
.then(function (result) {
if (result.isCancelled) {
alert('Login cancelled');
} else {
AccessToken
.getCurrentAccessToken()
.then((data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
alert('Success fetching data: ' + result.toString());
}
}
const infoRequest = new GraphRequest('/me', {
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
}, responseInfoCallback);
// Start the graph request.
new GraphRequestManager()
.addRequest(infoRequest)
.start()
})
}
}, function (error) {
alert('Login fail with error: ' + error);
});
}
Thank you #Samuel.
I finally succeed to get user information from Facebook login because of your help!
But I struggled to figure out how can I get username and email literally from the result object cause I am a newbie in React & Javascript.
P.S. result["name"] is the point because it is object!!
So I added some code to yours for other people like me.
If you don't like using your code, just tell me that.
<LoginButton
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
// Here's my code
alert('Success fetching data: ' + result["name"].toString() +
", " + result["email"].toString());
/*
if(your DB already got this email or something unique) {
// SignIn()
}
// when your DB doesn't have this email
else {
// Do signUp() with this infomation and SignIn()
}
*/
}
}
const infoRequest = new GraphRequest(
'/me',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
},
responseInfoCallback
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
My code was not retriving the user email, if you are having the same problem, just put 'email' in parameter's logInWithPermission
Not Working
LoginManager.logInWithPermissions(['public_profile']).then(...)
Working
LoginManager.logInWithPermissions(['public_profile', 'email']).then(...)
All Function
loginWithFacebook = () => {
LoginManager.logInWithPermissions(['public_profile', 'email']).then(
login => {
if (login.isCancelled) {
console.log('Login Cancelado');
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
const accessToken = data.accessToken.toString()
this.getInfoFromToken(accessToken)
})
}
},
error => {
console.log('Erro no login ', console.error(error)
)
}
)
}
getInfoFromToken = token => {
const PROFILE_REQUEST_PARAMS = {
fields: {
string: 'id, name, first_name, last_name, birthday, email'
},
}
const profileRequest = new GraphRequest('/me', { token, parameters: PROFILE_REQUEST_PARAMS },
(error, result) => {
if (error) {
console.log('Login Info has an error:', error)
} else {
console.log(result)
}
},
)
new GraphRequestManager().addRequest(profileRequest).start()
}
try this
import { GraphRequest, GraphRequestManager } from 'react-native-fbsdk';
export const GetInfoUSer = () => {
return new Promise((resolve, reject) => {
const infoRequest = new GraphRequest('/me', null, ((error, result) => {
if (error) {
reject(error)
} else {
resolve(result)
}
}))
new GraphRequestManager().addRequest(infoRequest).start();
})
}
and then
onLoginConFacebook = () => {
LoginManager.logInWithReadPermissions(['public_profile']).then(result => {
if (result.isCancelled) {
console.log(':(')
} else {
AccessToken.getCurrentAccessToken().then((data) => {
let myAccessToken = data.accessToken.toString();
GetInfoUSer().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
).catch(error => {
console.log(':(')
})
}
})
}

AWS Lambda Test Cases

I am using the Github project here: https://github.com/danilop/LambdAuth/blob/master/LambdAuthCreateUser/index.js
I want to write tests so I can see if things get stored in the database correctly and to see the results of the functions.
I copied this Lambda function from the project above:
console.log('Loading function');
// dependencies
var AWS = require('aws-sdk');
var crypto = require('crypto');
var util = require('util');
var config = require('./config.json');
// Get reference to AWS clients
var dynamodb = new AWS.DynamoDB();
var ses = new AWS.SES();
function computeHash(password, salt, fn) {
// Bytesize
var len = 128;
var iterations = 4096;
if (3 == arguments.length) {
crypto.pbkdf2(password, salt, iterations, len, fn);
} else {
fn = salt;
crypto.randomBytes(len, function(err, salt) {
if (err) return fn(err);
salt = salt.toString('base64');
crypto.pbkdf2(password, salt, iterations, len, function(err, derivedKey) {
if (err) return fn(err);
fn(null, salt, derivedKey.toString('base64'));
});
});
}
}
function storeUser(email, password, salt, fn) {
// Bytesize
var len = 128;
crypto.randomBytes(len, function(err, token) {
if (err) return fn(err);
token = token.toString('hex');
dynamodb.putItem({
TableName: config.DDB_TABLE,
Item: {
email: {
S: email
},
passwordHash: {
S: password
},
passwordSalt: {
S: salt
},
verified: {
BOOL: false
},
verifyToken: {
S: token
}
},
ConditionExpression: 'attribute_not_exists (email)'
}, function(err, data) {
if (err) return fn(err);
else fn(null, token);
});
});
}
function sendVerificationEmail(email, token, fn) {
var subject = 'Verification Email for ' + config.EXTERNAL_NAME;
var verificationLink = config.VERIFICATION_PAGE + '?email=' + encodeURIComponent(email) + '&verify=' + token;
ses.sendEmail({
Source: config.EMAIL_SOURCE,
Destination: {
ToAddresses: [
email
]
},
Message: {
Subject: {
Data: subject
},
Body: {
Html: {
Data: '<html><head>'
+ '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'
+ '<title>' + subject + '</title>'
+ '</head><body>'
+ 'Please click here to verify your email address or copy & paste the following link in a browser:'
+ '<br><br>'
+ '' + verificationLink + ''
+ '</body></html>'
}
}
}
}, fn);
}
exports.handler = function(event, context) {
var email = event.email;
var clearPassword = event.password;
computeHash(clearPassword, function(err, salt, hash) {
if (err) {
context.fail('Error in hash: ' + err);
} else {
storeUser(email, hash, salt, function(err, token) {
if (err) {
if (err.code == 'ConditionalCheckFailedException') {
// userId already found
context.succeed({
created: false
});
} else {
context.fail('Error in storeUser: ' + err);
}
} else {
sendVerificationEmail(email, token, function(err, data) {
if (err) {
context.fail('Error in sendVerificationEmail: ' + err);
} else {
context.succeed({
created: true
});
}
});
}
});
}
});
}
How do I test this?
I think you can test it with Jest.

Express Passport unable to get facebook friends list

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.