I have a sails app. I was trying to implement Facebook Login. When I click on the Login with facebook button i am getting this error:
error: A server error occurred in a request:
error: FacebookTokenError: This authorization code has been used.
Full error log looks like this:
error: A server error occurred in a request:
error: FacebookTokenError: This authorization code has been used.
at Strategy.parseErrorResponse (/home/node_modules/passport-facebook/lib/strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (/home/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:341:16)
at /home/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:166:45
at /home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:177:18
at passBackControl (/home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:123:9)
at IncomingMessage.<anonymous> (/home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:143:7)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:944:16
at process._tickDomainCallback (node.js:492:13) { [FacebookTokenError: This authorization code has been used.]
name: 'FacebookTokenError',
message: 'This authorization code has been used.',
type: 'OAuthException',
code: 100,
subcode: undefined,
status: 500 }
Middleware code looks like this:
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy
, moment= require('moment')
, momentTimeZone=require('moment-timezone')
, inflection = require('inflection')
, markdown = require('markdown').markdown
, URL =require('url')
, LocalStrategy=require('passport-local').Strategy
, config= require('./local')
, device = require('express-device')
var createUser = function (token, tokenSecret, profile, done) {
process.nextTick(function () {
User.findOne({
or: [
{uid: parseInt(profile.id)},
{uid: profile.id}
]
}
).exec(function (err, user) {
if (user) {
return done(null, user);
} else {
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName,
email: profile.email
};
if(profile.emails && profile.emails[0] && profile.emails[0].value) {
data.email = profile.emails[0].value;
}
if(profile.name && profile.name.givenName) {
data.firstname = profile.name.givenName;
}
if(profile.name && profile.name.familyName) {
data.lastname = profile.name.familyName;
}
User.create(data).exec(function (err, user) {
sails.log.info("Error",JSON.stringify(err))
return done(err, user);
});
}
});
});
};
module.exports = {
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL,
profileFields: ['name', 'emails' ],
enableProof: true
},
function (accessToken, refreshToken, email, done)
{
//console.log("Auth done");
//done(null, email);
createUser
}
//createUser
//}
))
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
/*app.get("/auth/facebook", passport.authenticate("facebook", { scope: ['email', 'public_profile'] }));*/
app.get('/auth/facebook',
passport.authenticate('facebook', { scope: ['email', 'public_profile'] }));
app.get("/auth/facebook/callback",
passport.authenticate("facebook", {
successRedirect: "/",
failureRedirect: "/login"
}),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
app.use(passport.initialize());
app.use(passport.session());
app.use(device.capture());
device.enableDeviceHelpers(app)
}
}
};
Can anyone suggest why I am getting this error and any possible solution.
This error occurs when you logged using facebook login, after that delete the user record in your database. You must delete your APP in your facebook account and try again.
Another posibility is that you already are logged in, and your middleware is trying to login again. In your code, you are not checking if the user is already logged in before send the request to "auth/facebook. There is a simple way to prove this: Open a Chrome window in private mode, so no cookie is used, and try again your facebook login. Good Luck!
Probabily not exist some attributes of profile data. Try:
console.log(profile)
For verify all attributes of profile. In my case:
{ id: 'nnnnnnnn',
username: undefined,
displayName: 'My Name',
name:
{ familyName: undefined,
givenName: undefined,
middleName: undefined },
gender: undefined,
profileUrl: undefined,
provider: 'facebook',
_raw: '{"name":"My name","id":"nnnnnnnn"}',
_json: { name: 'My name', id: 'nnnnnnnn' } }
Not exist any attribute "email" or similar, this generate the error and not complete the cicle life of the authenticate:
error: FacebookTokenError: This authorization code has been used.
The attribute email not get because I hidden this in my account Facebook.
In your code:
...
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName,
email: profile.email
};
...
The line:
email: profile.email
You are already assuming that the attribute email is already exist.
Try:
email: (profile.emails && profile.emails[0]) ? profile.emails[0].value : ''
Related
For my application(Node.js) i'm using passport-facebook and everything works well except that i can't get user email. I found a lot of issues on this subject but for some reason they not fix my problem. I would be really preciate for some help. So my configurations:
app.js
app.get('/auth/facebook', passport.authenticate('facebook', {scope: 'email'}));
// in scope i tried a different options like ['email'] or ['emails'] and so go on
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect: '/',
failureRedirect: '/login'
}));
passport.js:
passport.use('facebook', new FacebookStrategy({
clientID : secret.facebook.clientID,
clientSecret : secret.facebook.clientSecret,
callbackURL : secret.facebook.callback
},
function(access_token, refreshToken, profile, done) {
console.log(profile) // here i got all profile data except email
process.nextTick(function() {
User.findOne({ 'facebook.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
return done(null, user);
} else {
var newUser = new User();
console.log(profile.email) // here i got undefined
newUser.facebook.id = profile.id;
newUser.facebook.token = access_token;
newUser.facebook.firstName = profile.name.givenName;
newUser.facebook.lastName = profile.name.familyName;
newUser.facebook.email = profile.email;
newUser.save((err) => {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
facebook-secret.js
module.exports = {
facebook: {
clientID: '****',
clientSecret: '********',
callback: '/auth/facebook/callback',
profileFields: ['id', 'email']
}
}
in UserSchema:
facebook: {
id: String,
token: String,
email: String,
name: String,
}
facebok - settings
graph - api
in passport.js :
passport.use('facebook', new FacebookStrategy({
clientID : secret.facebook.clientID,
clientSecret : secret.facebook.clientSecret,
callbackURL : secret.facebook.callback
},
change to:
passport.use('facebook', new FacebookStrategy({
clientID : secret.facebook.clientID,
clientSecret : secret.facebook.clientSecret,
callbackURL : secret.facebook.callback,
profileFields : secret.facebook.profileFields
},
My profileField : profileFields: ['id', 'displayName', 'email', 'first_name', 'middle_name', 'last_name']
Try to pass the scopes in url as param like you used in graph API(?fields=id,email...).
I'm trying to secure my sails js rest api with the help of the passport http package but at the moment I can't figure out where the error is in my code.
I used this repo and this tutorial to get an idea of how this should work. My problem is that my code always returns a 401.
I don't really know where to look for the error. If you need more information about my code just comment.
Bruno
EDIT:
I found the source of the problem (With the help of #Viktor). I just didn't really understood how HTTP-Basic authentication works. Now the problem is how do I send my auth-credentials and my data? If I just test the routes with auth(...), they work... But how do I add the data? Or do I have to authenticate me first and send the data in the second request?
passport.js
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var bcrypt = require('bcrypt');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findOne({
id: id
}, function(err, user) {
done(err, user);
});
});
passport.use('user-authentication', new BasicStrategy(
function(mail, password, done) {
sails.log.error("hallo");
User.findOne({
mail: mail
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Incorrect email.'
});
}
// Make sure the password is correct
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) {
return done(err);
}
// Password did not match
if (!isMatch) {
return done(null, false, {
message: 'Invalid Password'
});
}
// Success
return done(null, user);
});
});
}
));
isAuthenticated.js
var passport = require("passport");
module.exports = function (req, res, ok) {
passport.authenticate("user-authentication", {
session: false
}, function (err, user, info) {
if (err || !user) {
res.set("WWW-Authenticate", "Basic realm=\"Restricted\"");
return res.send("You are not permitted to perform this action", 401);
}
req.session.user = user;
return ok(null, user);
})(req, res, ok);
};
policies.js
module.exports.policies = {
'*': true,
'UserController': {
update: 'isAuthenticated'
}
}
UserController.test.js
var request = require('supertest');
var async = require('async');
describe('UserController', function() {
describe('#new()', function() {
it('...', function (done) {
request(sails.hooks.http.app)
.post('/user/new')
.send({ own_number: '654122', password: 'test', mail: 'test#test.com', device_id: '1234', numbers: [1234567] })
.expect(200)
.end(done);
});
});
describe('#update()', function(){
it('...', function (done) {
async.series([
function(callback){
request(sails.hooks.http.app)
.post('/contact/update')
.send({ number: 1234, mail: "test#test.com", password: "test" })
.expect(200)
.end(callback);
},
function(callback){
request(sails.hooks.http.app)
.post('/user/update')
.send({ numbers: [1234], mail: "tet#test.com", password: "test" })
.expect(200)
.end(callback);
}
], done);
});
});
});
Works for me – I'm able to authenticate as well as access and manage the user data once there's a valid user in the database. With an empty user database, you would get 401 all the time, of course, as these policies don't allow you to create even the first user to authenticate as. Temporarily disabling the UserController policies in config/policies.js gives you the opportunity to create the first user.
Assuming you have at least one valid user in the database, let's narrow down the problem. What output do you get from logging err and user in isAuthenticated.js? If you get null and false, what happens in the different steps in passport.js - are you able to find the user by e-mail address in the database and does the password match?
Do you have custom routes and controller actions or do you use the blueprints?
EDIT: Your update test would look like this with HTTP Basic Authentication:
describe('#update()', function(){
it('...', function (done) {
async.series([
function(callback){
request(sails.hooks.http.app)
.post('/contact/update')
.auth('test#test.com', 'test')
.send({ number: 1234, mail: "test#test.com", password: "test" })
.expect(200)
.end(callback);
},
function(callback){
request(sails.hooks.http.app)
.post('/user/update')
.auth('test#test.com', 'test')
.send({ numbers: [1234], mail: "tet#test.com", password: "test" })
.expect(200)
.end(callback);
}
], done);
});
});
From the documentation here it shows you need userid and password. So looking at your code you have mail in place of userid and that is why you are getting 401 or at least where you can start looking. If you need to verify this you can look at passport-http basic strategy here.
passport.use(new BasicStrategy(
function(userid, password, done) {
User.findOne({ mail: userid, password: password }, function (err, user) {
done(err, user);
});
}
));
I'm working to set up Face book OAuth for user logging into my website. I am getting an object back from Facebook with my FB information but there is no sign of email anywhere in that profile response.
When I click the "Login to Facebook" button on my page it takes me to Facebook and says the app will require my profile and email as I added email to the scope of things I wanted. Here is my code:
config/routes.js:
app.get('/auth/facebook', passport.authenticate('facebook', {scope:['email']}));
app.get('/auth/facebook/callback',
passport.authenticate('facebook',
{ successRedirect: '/confirm_community',
failureRedirect: '/signin' }));
==============
passport.js
passport.use(new FacebookStrategy({
// pull in our app id and secret from our auth.js file
clientID : configAuth.facebookAuth.clientID,
clientSecret : configAuth.facebookAuth.clientSecret,
callbackURL : configAuth.facebookAuth.callbackURL
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
console.log(profile);
// console.log(profile.emails[0].value);
User.findOne({'facebook.id':profile.id}, function (error, result){
if(error){
return done(error);
}
if(user) {
return done(null, user);
}
else {
var newUser = new User();
newUser.facebook.id = profile.id;
newUser.facebook.token = accessToken;
newUser.facebook.name = profile.displayName;
newUser.facebook.email = profile.emails[0].value;
newUser.save(function (error){
if(error){
console.log(error);
throw error;
} else {
return done(null, newUser);
}
});
}
});
});
}));
Here is the FB object I get back when I console.log(profile) in the process.nextTick function with FB's response:
{ id: 'my id is coming back here',
username: undefined, (because I don't have one)
displayName: 'my name is coming back here',
name:
{ familyName: undefined, (I don't have any of these in my profile)
givenName: undefined,
middleName: undefined },
gender: undefined,
profileUrl: undefined,
provider: 'facebook',
_raw: '{"name":"my name is coming here","id":"my id is coming here"}',
_json: { name: 'my name is coming here', id: 'my id is coming here' } }
I made sure my email is public and login to Facebook normally with my email and password, so I'm not using Mobile Phone to sign to Facebook. I'm just missing that email address. I can't seem to figure out why that object isn't giving me back an email?? What am I missing? Help is very much appreciated.
I find another solution for this issue with PASSPORT-FACEBOOK node module. Hope this will help others.
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback",
profileFields: ['email']
Ref : https://github.com/jaredhanson/passport-facebook/issues/129
Search for "Declarative Fields" in the changelog: https://developers.facebook.com/docs/apps/changelog#v2_4
You now have to specify the fields you want to get in the API call: /me?fields=name,email
I'm trying to test my local-login. I've implemented with passport.js, following its guide and following this MEAN skeleton.
I'm pretty sure that the implementation is fine, but there is something wrong with the test that always fails authentication.
If authentication fails it should be redirect to "/signin"if authentication is correct it should be go to "/"
But when I test, the authentication always fails.
This is routes.js:
module.exports = function(app, passport, auth) {
var users = require('../app/controllers/users');
app.get('/signin', users.signin);
app.post('/users/session', passport.authenticate('local', {
failureRedirect: '/signin',
failureFlash: 'Invalid email or password.'
}), users.session);
var index = require('../app/controllers/index');
app.get('/', index.render);
passport.js:
var mongoose = require('mongoose'),
LocalStrategy = require('passport-local').Strategy,
User = mongoose.model('User'),
config = require('./config');
module.exports = function(passport) {
//Serialize sessions
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findOne({
_id: id
}, '-salt -hashed_password', function(err, user) {
done(err, user);
});
});
//Use local strategy
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
User.findOne({
email: email
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user'
});
}
if (!user.authenticate(password)) {
return done(null, false, {
message: 'Invalid password'
});
}
return done(null, user);
});
}
));
}
and test: api.js:
var User, app, mongoose, request, server, should, user;
should = require("should");
app = require("../server");
mongoose = require("mongoose");
User = mongoose.model("User");
request = require("supertest");
server = request.agent("http://localhost:3000");
describe("<Unit Test>", function() {
return describe("API User:", function() {
before(function(done) {
user = new User({
email : "user#user.com",
firstName: "Full Name",
lastName : "Last Name",
password : "pass11"
});
user.save();
return done();
});
describe("Authentication", function() {
return it("Local login", function(done) {
return server.post("/users/session").send({
email : "user#user.com",
password: "pass11"
}).end(function(err, res) {
res.headers.location.should.have.equal("/");
return done();
});
});
});
return after(function(done) {
User.remove().exec();
return done();
});
});
});
This is what the terminal displays:
<Unit Test>
API User:
Authentication
1) Local login
0 passing (24ms)
1 failing
1) <Unit Test> API User: Authentication Local login:
actual expected
/signin
I'm trying using this code and work as expected
var User, app, mongoose, request, server, should, user, agent;
should = require("should");
app = require("../server");
mongoose = require("mongoose");
User = mongoose.model("User");
request = require("supertest");
agent = request.agent(app)
describe('User', function () {
before(function(done) {
user = new User({
email : "user#user.com",
firstName: "Full Name",
lastName : "Last Name",
password : "pass11"
});
user.save(done)
});
describe('Login test', function () {
it('should redirect to /', function (done) {
agent
.post('/users/session')
.field('email', 'user#user.com')
.field('password', 'pass11')
.expect('Location','/')
.end(done)
})
after(function(done) {
User.remove().exec();
return done();
});
})
})
for more reference check this test-user
here my screenshot
I am using passportjs for facebook authentication. Here is my facebook strategy:
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL
}, function(accessToken, refreshToken, profile, done) {
User.findOne({ 'facebook.id': profile.id }, function (err, user) {
if (err) { return done(err); }
if (!user) {
user = new User({
name: profile.displayName,
email: profile.emails[0].value,
username: profile.username,
provider: 'facebook',
facebook: profile._json
});
user.save(function (err) {
if (err) {
console.log(err);
}
return done(err, user);
});
} else {
return done(err, user);
}
});
}));
I added the following routes:
app.get('/facebook/auth', passport.authenticate('facebook', { scope: [ 'email', 'user_about_me', 'publish_actions']}), function(req, res) { });
// I need the following fix due to this: http://stackoverflow.com/a/17015836/289246
app.get('/facebook/auth/callback', function(req, res, next) {
if (req.query && !req.query.error && req.query.error_code) {
req.query.error = true;
}
next();
},
passport.authenticate('facebook', { failureRedirect: '/facebook-auth-failure', successRedirect: '/auth-success', failureFlash: true })
);
app.get('/facebook-auth-failure', users.authFailure);
app.get('/auth-success', users.authSuccess);
My users.authFailure method is:
exports.authFailure = function (req, res) {
var error = ???
// How can I get here the error message??
res.render('auth-failure', {
error: error || 'An error has accured'
});
};
In case of facebook authentication failure, how can I get the error message (I want to display it to the user)?
Since you're using failureFlash, this should do it:
var error = req.flash('error');
I experienced many many problems and bugs and configurations issues during working with Passport. My solution was to move to Everyauth.
I don't know if this will be any use to you but I got access to flash messages this way.
When you are defining the FacebookStrategy use the passReqToCallback parameter.
passport.use(new FacebookStrategy({
clientID: facebook.getClientID(),
clientSecret: facebook.getClientSecret(),
callbackURL: facebook.getCallback(),
passReqToCallback: true
this will allow you to add the req.flash() as a parameter to the done() like so
return done(false, user, reg.flash('success', 'Sign in successfull'));
Hope that sheds some light on the situation for you or anybody else looking for help