Meteor login with facebook - facebook

I have a meteor app, with packages :
useraccounts:bootstrap;
service-configuration;
accounts-facebook;
The facebook side was configured to allow requests from localhost. When using the register/signin with facebook from the atForm, a popup appears
EDIT
The popup is actually not empty but displays a message 'login complete, click to close' but closed fast because that's how I configured it on the facebook side.
And the console logs an error:
Exception while invoking method 'login' undefined
EDIT
Here is the service conf:
ServiceConfiguration.configurations.remove({
service: 'facebook'
});
ServiceConfiguration.configurations.insert({
service: 'facebook',
appId: 'removed',
secret: 'removed'
});
EDIT
Here is the client side code :
Template.atForm.events({
'click #at-facebook': function(event) {
console.log("facebook click");
Meteor.loginWithFacebook({
requestPermissions: ['email', 'user_friends']
}, function (err) {
if (err)
Session.set('errorMessage', err.reason || 'Unknown error');
});
}
});
EDIT
When registering with facebook the user created is as follow :
{ createdAt: Thu Jan 21 2016 16:00:08 GMT+0100 (CET),
I20160121-16:00:08.108(1)? _id: 'Kgs3WswMdB9hsxMfp',
I20160121-16:00:08.108(1)? services:
I20160121-16:00:08.108(1)? { facebook:
I20160121-16:00:08.108(1)? { accessToken: 'removed',
I20160121-16:00:08.108(1)? expiresAt: 1458562023670,
I20160121-16:00:08.109(1)? id: 'removed',
I20160121-16:00:08.109(1)? email: 'removed',
I20160121-16:00:08.109(1)? name: 'Mathieu Kudla',
I20160121-16:00:08.109(1)? first_name: 'Mathieu',
I20160121-16:00:08.110(1)? last_name: 'Kudla',
I20160121-16:00:08.110(1)? link: 'https://www.facebook.com/app_scoped_user_id/removed/',
I20160121-16:00:08.110(1)? gender: 'male',
I20160121-16:00:08.110(1)? locale: 'fr_FR',
I20160121-16:00:08.110(1)? age_range: [Object] } } }
This seems to indicate that the facebook handshake was successful?
What is causing that error? Thanks :)

Ok I kinda solved it.
I digged in the log-in attempts and noticed that the oauth service was not properly paired with the user. I don't know why that is or if there is a better solve but I added the line :
if (user.services) { var service = _.pairs(user.services)[0]; }
to my onCreateUser hook and appended the user object properly before returning it. It seems to have done the trick but I don't know what I was not doing properly the first time...

Related

Can't login by Goggle accounts connect or by Google auth API in Cypress 10x

Current behavior
I've tried to connect to Google account when my tested application redirects to Google accounts connect for let the end-user send emails by the application but I'm not able to do it not by Google Auth API according to your guidelines:
https://docs.cypress.io/guides/end-to-end-testing/google-authentication#Custom-Command-for-Google-Authentication
and not by cy.origin() from the UI.
In the first attempt by the API it's ignore of these authentication and popup the dialog to connect by google account as usually even all the credentials and token are valid and return 200 ok.
In the second attempt by cy.origin() it's keep to load the page after the redirect and always reach to timeout and yell about to increase the timeout even the page seems like it was fully loaded after a few seconds.
I've tried to increase the timeout to 90 seconds and use wait() before and after the redirect and look for some hidden iframes and tried every versa of google domain but nothing help.
it always return errors over there.
all the examples are below.
This is the error when trying to use cy.origin()::
Timed out after waiting 30000ms for your remote page to load on origin(s):
- https://google.com
A cross-origin request for https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https%3A%2F%2Fmail.google.com&include_granted_scopes=true&state=%7B%22redirectUri%22%3A%22https%3A%2F%2Fmyappurl.com%2Fapp%2Fpipeline%2F9some-token-here-b96b599154ac%3Ftab%3Doverview%22%2C%22clientToken%22%3A%mytokenishere-1234567890%22%7D&prompt=consent&response_type=code&client_id=1234567890-aehhht36f7a01d38bmsvvpjrh915i86v.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fmyredreictedappurl.com%2FusersManagerSrvGoogleLogin was detected.
A command that triggers cross-origin navigation must be immediately followed by a cy.origin() command:
cy.origin('https://google.com', () => {
<commands targeting https://accounts.google.com go here>
})
If the cross-origin request was an intermediary state, you can try increasing the pageLoadTimeout value in Users/myname/repos/myreponame/cypress.config.ts to wait longer.
Browsers will not fire the load event until all stylesheets and scripts are done downloading.
When this load event occurs, Cypress will continue running commands.[Learn more](https://on.cypress.io/origin)
Desired behavior
No response
Test code to reproduce
commands.ts
Cypress.Commands.add('loginByGoogleApi', () => {
cy.log('Logging in to Google')
cy.request({
method: 'POST',
url: 'https://www.googleapis.com/oauth2/v4/token',
body: {
grant_type: 'refresh_token',
client_id: Cypress.env('googleClientId'),
client_secret: Cypress.env('googleClientSecret'),
refresh_token: Cypress.env('googleRefreshToken'),
},
}).then(({ body }) => {
const { access_token, id_token } = body
cy.request({
method: 'GET',
url: 'https://www.googleapis.com/oauth2/v3/userinfo',
headers: { Authorization: `Bearer ${access_token}` },
}).then(({ body }) => {
cy.log(body)
const userItem = {
token: id_token,
user: {
googleId: body.sub,
email: body.email,
givenName: body.given_name,
familyName: body.family_name,
imageUrl: body.picture,
},
}
window.localStorage.setItem('googleCypress', JSON.stringify(userItem))
cy.visit('/')
})
})
})
test-file.cy.ts
it.only('Send email to a user - is shown in the activity', () => {
cy.loginByGoogleApi();
cy.get(loc.sideNavBar.buyersPipeline).should('be.visible').click();
cy.get(loc.pipelineBuyer.nameColumn)
.eq(4)
.should('be.visible')
.click({ force: true });
cy.get(loc.buyerDetails.basicCard).should('be.visible');
cy.get(loc.buyerDetails.timelineSendEmailIcon)
.should('be.visible')
.click();
cy.get('div[role="dialog"]').find('button.MuiButton-root').should('be.visible').click();
})
})
By cy.origin() by the UI:
test-file.cy.ts
it.only('Send email to a user - is shown in the activity', () => {
// cy.loginByGoogleApi();
cy.get(loc.sideNavBar.buyersPipeline).should('be.visible').click();
cy.get(loc.pipelineBuyer.nameColumn)
.eq(4)
.should('be.visible')
.click({ force: true });
cy.get(loc.buyerDetails.basicCard).should('be.visible');
cy.get(loc.buyerDetails.timelineSendEmailIcon)
.should('be.visible')
.click();
cy.get('div[role="dialog"]').find('button.MuiButton-root').should('be.visible').click();
cy.wait(5000);
cy.origin('https://accounts.google.com', () => {
cy.wait(5000);
expect(window.origin).contains('google.com')
cy.get('input[type="email"]', {timeout: 60000}).should('be.visible', {timeout: 60000}).type('111');
})
});
````
### Cypress Version
10.7.0
### Node version
v14.19.1
### Operating System
macOS Montery 12.3.1

Error trying to get authenticated user email with googleapis and node.js

I'm implementing auth on my website using googleapis. The function plus.people.get doesn't work. I have seen it is deprecated on some forums but it's still documented at google which has me confused. The error I get is "Legacy People API has not been used in project 328985958128 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/legacypeople.googleapis.com then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." The webpage doesn't even load. My code is
if (!req.body.token) return res.status(500).json({ type: 'error', message: 'No access token provided.' })
const OAuth2 = google.auth.OAuth2
const oauth2Client = new google.auth.OAuth2(keys.client_id, keys.client_secret)
google.options({ auth: oauth2Client });
const plus = google.plus('v1')
oauth2Client.setCredentials({
access_token: req.body.token
})
plus.people.get({
userId: 'me',
auth: oauth2Client
}, (error, response) => {
if (error)
console.log(error)
return res.status(500).json({ type: 'error',error })
const emails = (response.data || {}).emails
You are using google.plus('v1'), which has been deprecated
Instead you should use
const service = google.people({version: 'v1', auth: oauth2Client})
to create a service object.
To perform a request an additional auhtorization is not required anymore, so:
service.people.get({
userId: 'me'
}, (error, response) => {
...
})
Further information:
Creating a service account client with node.js
People API quide for node.js

Facebook Login is returning 'Undefined' Fields in user profile and it doesn't return email. MEANJs + Passport-facebook

I am using Meanjs.org boilerplate and Facebook Signup returns me to the Signup page.
Following are the steps that I have taken so far.
1) Setting up the Facebook App Site URL
http://localhost:3000/
and the callback URI of OAuth
http://localhost:3000/auth/facebook/callback
2) Placing the APP_ID and APP_Secret in as Client_ID and Client_Secret
facebook: {
clientID: process.env.FACEBOOK_ID || '*****',
clientSecret: process.env.FACEBOOK_SECRET || '*****',
callbackURL: 'http://localhost:3000/auth/facebook/callback',
profileFields: ['id','emails', 'first_name', 'last_name', 'displayName', 'link', 'about_me', 'photos' ]
},
3) Code is as follows
--Routes
// Setting the facebook oauth routes
app.route('/auth/facebook').get(passport.authenticate('facebook', {
scope: ['email']
}));
app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));
-- The oauthCallback function,
exports.oauthCallback = function(strategy) {
return function(req, res, next) {
passport.authenticate(strategy, function(err, user, redirectURL) {
if (err || !user) {
console.log('1' + err);
//console.log(user);
return res.redirect('/#!/signin');
}
req.login(user, function(err) {
if (err) {
console.log('2' + err);
return res.redirect('/#!/signin');
}
return res.redirect(redirectURL || '/');
});
})(req, res, next);
};
};
-- Passport-Facebook Strategy
module.exports = function() {
// Use facebook strategy
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL,
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
console.log('facebook Strategy Started');
// Set the provider data and include tokens
var providerData = profile._json;
providerData.accessToken = accessToken;
providerData.refreshToken = refreshToken;
// console.log(JSON.stringify(profile));
console.log(profile);
// console.log(JSON.stringify(profile.name.givenName));
// Create the user OAuth profile
var providerUserProfile = {
firstName: profile.name.givenName,
lastName: profile.name.familyName,
displayName: profile.displayName,
email: profile.emails[0].value,
username: profile.username,
provider: 'facebook',
providerIdentifierField: 'id',
providerData: providerData
};
//console.log('provider' + providerUserProfile);
// Save the user OAuth profile
users.saveOAuthUserProfile(req, providerUserProfile, done);
}
));
};
4) Debugging
Logging err under oauthCallback function returns the following,
1TypeError: Cannot read property '0' of undefined
What Facebook returns as profile in Passport-Facebook module is as follows,
{ id: 'Id_of_the_person',
username: undefined,
displayName: 'Full_name_of_person',
name:
{ familyName: undefined,
givenName: undefined,
middleName: undefined },
gender: undefined,
profileUrl: undefined,
provider: 'facebook',
_raw: '{"name":"Full_name_of_person","id":"Id_of_the_person"}',
_json:
{ name: 'Id_of_the_person',
id: 'Id_of_the_person',
accessToken: 'access_token_value',
refreshToken: undefined } }
Can anyone be kind to guide me about getting the correct user profile from Facebook including user email?
Thank you so much.
I have my profile fields set to the following
profileFields: ['email','id', 'first_name', 'gender', 'last_name', 'picture']
Even though you set email it might return emails if the user has multiple emails. So you need to check if email was returned profile.email or profile.emails[0].value. You must also check if it is undefined, because there is people that register with facebook that never verify their email account and there is people that sign up with a phone number, in both those cases their emails will always be undefined.
you want to check that any required fields have values.
var email = profile.email || profile.emails[0].value;
if (! email){
console.log('this user has no email in his FB');
var err = {message:'this user is missing an email'};
return done(err);
}
now i can do this if they have an email
newUser.facebook.email = email;
if they don't have an email you can set a session for profile and send them to a page that asks them to enter an email.
It sounds like a pain, but you can never trust information from a third party api to have values.
Most of the passport examples I've seen online are wrong. They all assume an email is present.
First, profileFields field does not obey to Portable Contacts convention - and you can find the convention for passportjs here.
Second, in your example, after removing removed 'about_me', the Facebook signup returns no error. Before removing 'about_me', I had a different error: Tried accessing nonexisting field (about_me) on node type (User)
If the error persist, see this serie of 5 tutorials which helps me when I was doing the sign up page to authenticate with social networks accounts.

SailsJS: add a middleware before the defined express middlewares for debugging

General Problem
Sails JS is built upon express JS, for debugging reasons I would like to inject a middleware or a function in Sails JS before cookieParser/bodyParser, I expect that Sails JS wrapped the following:
app.use(cookieParser());
app.use(bodyParser());
etc...
I would like to inject my own middleware function before these injections, how can I do that? for example in order to track the initial request.
Specific problem:
I integrated with Passport JS, sensitive users info are in request (see below), I am pretty sure this is due to request parsing middleware like cookieParser/bodyParser, but I would like to know how to confirm this myself.
(I would also be happy for a confirmation from you)
When I print the request, the user information is there, specifically, the password ("password: '$2a$10$rfRptIm7o1BKD1Qdr7yPUeWVisEHyZciCdD0ebivLAm8PPVRUicES',")
Here is the partial request:
_passport:
{ instance:
{ _key: 'passport',
_strategies: [Object],
_serializers: [Object],
_deserializers: [Object],
_infoTransformers: [],
_framework: [Object],
_userProperty: 'user',
Authenticator: [Function: Authenticator],
Passport: [Function: Authenticator],
Strategy: [Object],
strategies: [Object] },
session: { user: '532ea818e6221c90251e9342' } },
user:
{ username: 'nizar',
password: '$2a$10$rfRptIm7o1BKD1Qdr7yPUeWVisEHyZciCdD0ebivLAm8PPVRUicES',
createdAt: Sun Mar 23 2014 11:23:36 GMT+0200 (Jerusalem Standard Time),
updatedAt: Sun Mar 23 2014 11:23:36 GMT+0200 (Jerusalem Standard Time),
id: '532ea818e6221c90251e9342' },
while in the model I toJSON and deleted the password:
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
1.General Problem
For Sails 0.9.x, the middlwares loaded by the sails server are defined in sails/lib/express/index.js. As you can see, the custom middlware defined in config/express.js is used after cookieParser, session, bodyParser and methodOverride. To inject a custom middleware before cookieParser and bodyParser, you can override the cookieParser or modify sails/lib/express/index.js directly.
For Sails 0.10.x, you can define a custom loadMiddleware function (default implementation is in sails/lib/hooks/http/loadMiddleware.js) in config/express.js. Scott Gress has explained this in detail.
2.Specific Problem
If you don't want the request object to include the password information, call toJSON() in the callback function of deserializeUser:
passport.deserializeUser(function(id, done) {
User.findOneById(id).done(function(err, user) {
done(err, user.toJSON());
});
});
and in the callback function of the strategy definition:
passport.use('local',
new LocalStrategy({
usernameField: 'userename',
passwordField: 'password'
},
function(username, password, done) {
User.findOne({ name: username}).done(function(err, user) {
if (err) return done(err);
if (!user) {
return done(null, false, {message: 'Unknown user'+username});
};
if (!user.validatePassword(password)) {
return done(null, false, {message: 'Invalid password!'});
};
return done(null, user.toJSON());
});
}
));
For Sails v0.10, you can follow the procedure in this answer to insert custom middleware before the body parser. In v0.9.x, you'd have to create your own bodyParser callback and add the middleware in there; see this answer for more info.
However in your case I'm not sure any of this is necessary. Passport is adding user data to the request object on the server as a convenience, but it's not actually being transmitted in the request. So, there's no sensitive data out in the open--it's just in memory. Your only concern is to keep it from being sent back to the client, which you're already doing with your toJSON method on the User model.

Can Facebook Graph API edit facebook event?

I have tried to create facebook event by graph api:
FB.api('/123/events', 'POST', {
name: 'Moon',
start_time: '2012-11-05T00:00:00+0700',
end_time: '2012-11-16T00:00:00+0700',
location: 'My country',
description: 'Mooncake',
privacy_type: 'OPEN'
}, function (res) {
console.info('create event: ', res);
});
"123" is my facebook id.
The event is created successfully with response event id = "456"
I tried to update it:
FB.api('/456', 'POST', {
name: 'HoneyMoon'
}, function (res) {
console.info('edit event: ', res);
});
But, I can not update it, this is facebook response:
Object { message="(#200) Permissions error", type="OAuthException", code=200}
I'm sure that I am admin that event.
Can you help me answer this question?
Thank you very much
Oh yeah, I found the problem.
Please enable "Events Timezone" migrations in Apps > Settings > Advanced.