I am using Meteor and people can connect to the site via Facebook. I'm using people username to identify them. But, some of them don't have a username. For example, a new user has null as a username. What I'm trying to do is, if the person has a username then OK we use their username. If not, I wanna use their Facebook id as their username. The problem is, my if condition is not working properly. If the person has a username, the if condition considers that the person doesn't. The weird thing is, if I do a console.log of the username before the if condition, it will show the username. But once in the if, it considers that the username is null. Here is the code :
Accounts.onCreateUser(function(options, user) {
var fb = user.services.facebook;
var token = user.services.facebook.accessToken;
if (options.profile) {
options.profile.fb_id = fb.id;
options.profile.gender = fb.gender;
options.profile.username = fb.username
console.log( 'username : ' + options.profile.username);
if ( !(options.profile.username === null || options.profile.username ==="null" || options.profile.username === undefined || options.profile.username === "undefined")) {
console.log('noooooooo');
options.profile.username = fb.id;
} else {
console.log('yessssssss');
options.profile.username = fb.username;
}
options.profile.email = fb.email;
options.profile.firstname = fb.first_name;
user.profile = options.profile;
}
sendWelcomeEmail(options.profile.name, options.profile.email);
return user;
});
With this code, if I log in with my Facebook that has a username. The condition will show 'noooooooo' but the console.log( 'username : ' + options.profile.username); will show my username. Why does it do that? :l
It's because creating is called before logging and logging is asynchronous .. so you cannot ensure that your if will be or will not true/false. Your putting information from fb service is redundant, because all these informations are already saved with user.
http://docs.meteor.com/#meteor_user
You should obtain information about user after he is loggedIn because in that moment you will be able to recognise what kind of identifier you can use username/id.
//Server side
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId});
// You can publish only facebook id..
/*return Meteor.users.find({_id: this.userId},
{
fields: {
'services.facebook.id': true
}
}
);*/
});
//Client side
Meteor.subscribe("userData");
// .. you can see more informations about logged user
console.log(Meteor.users.find({}).fetch());
Related
I have an Application that uses Firebase for Authentification.
I allow users to sign in with either Google/Facebook/Twitter or using an Email and Password.
Additionally I have activated within Firebase that Users can only create one account per email.
I want to cover the Following case:
A users signs up with facebook and gets a user account created with their facebook email (e.G. facebookuser#gmail.com).
A few days later the user comes back to the app, but forgot that he signed up using facebook and tries loging in with their email adress facebookuser#gmail.com and their usual password instead.
The firebase.auth().signInWithEmailAndPassword(email, password) method throws an auth/wrong-password error, as no password was given using the facebook login method but the email does exist.
Instead of showing a useless "Wrong password or the account corresponding to the email does not have a password set." error I would like to check instead which provider was used for signing up and reminding the user to sign in with the provider instead.
Unfortunately there doesn't seem to exist a method to get the User.ProviderID for a given email or to understand if the auth/wrong-password error was given because the user typed in a wrong password or if there was no password given in first place as the user signed up with an OAuthProvider instead.
Okay, I have missed the fetchProvidersForEmail method.
Just for Reference: (not the final code I will be using)
Uses signInWithEmailAndPassword to check if a User exists
If it doesn't it creates it using createUserWithEmailAndPassword
If a user exits, it tries to login and catches errors
If the error is auth/wrong-password it checks with fetchProvidersForEmail if the user has used a Provider to sign up
firebase.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
// User not found? Create user.
if ( errorCode === 'auth/user-not-found' ) {
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if ( errorCode == 'email-already-in-use' ) {
alert('You already have an account with that email.');
} else if ( errorCode == 'auth/invalid-email' ) {
alert('Please provide a valid email');
} else if ( errorCode == 'auth/weak-password' ) {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
console.log(error);
});
// Wrong Password Error
} else if ( errorCode === 'auth/wrong-password' ) {
// Check if User has signed up with a OAuthProvider
firebase.auth().fetchProvidersForEmail(email).then(function( result ){
// … show OAuthProvider Login Button
});
alert('Wrong password. Please try again');
} else {
alert( errorMessage );
}
console.log( error );
});
There are some irreversible actions that user can do in my app. To add a level of security, I'd like to verify that the person performing such an action is actually the logged in user. How can I achieve it?
For users with passwords, I'd like a prompt that would ask for entering user password again. How can I later verify this password, without sending it over the wire?
Is a similar action possible for users logged via external service? If yes, how to achieve it?
I can help with the first question. As of this writing, meteor doesn't have a checkPassword method, but here's how you can do it:
On the client, I'm going to assume you have a form with an input called password and a button called check-password. The event code could look something like this:
Template.userAccount.events({
'click #check-password': function() {
var digest = Package.sha.SHA256($('#password').val());
Meteor.call('checkPassword', digest, function(err, result) {
if (result) {
console.log('the passwords match!');
}
});
}
});
Then on the server, we can implement the checkPassword method like so:
Meteor.methods({
checkPassword: function(digest) {
check(digest, String);
if (this.userId) {
var user = Meteor.user();
var password = {digest: digest, algorithm: 'sha-256'};
var result = Accounts._checkPassword(user, password);
return result.error == null;
} else {
return false;
}
}
});
For more details, please see my blog post. I will do my best to keep it up to date.
I haven't done this before, but I think you will need something like this on your server
Accounts.registerLoginHandler(function(loginRequest) {
console.log(loginRequest)
var userId = null;
var username = loginRequest.username;
// I'M NOT SURE HOW METEOR PASSWORD IS HASHED...
// SO YOU NEED TO DO A BIT MORE RESEARCH ON THAT SIDE
// BUT LET'S SAY YOU HAVE IT NOW
var password = loginRequest.password;
var user = Meteor.users.findOne({
$and: [
{username: username},
{password: password}
]
});
if(!user) {
// ERROR
} else {
// VERIFIED
}
});
then you can call this function from the client side like this:
// FETCH THE USERNAME AND PASSWORD SOMEHOW
var loginRequest = {username: username, password: password};
Accounts.callLoginMethod({
methodArguments: [loginRequest]
});
I have a project on github for different purpose, but you can get a sense of how it is structured: https://github.com/534N/apitest
Hope this helps,
I have found the best way to validate the users password is to use the Accounts.changePassword command and
pass in the same password for old and new password. https://docs.meteor.com/api/passwords.html#Accounts-changePassword
Accounts.changePassword(this.password, this.password, (error) => {
if(error) {
//The password provided was incorrect
}
})
If the password provided is wrong, you will get an error back and the users password will not be changed.
If the password is correct, the users password will be updated with the same password as is currently set.
In Meteor, I'd like to add a referrer field to new user signups. I can do this for users that register with a password, by passing in extra options to be used with Accounts.onCreateUser(function (options, user).
But this doesn't seem to work with social login. Any ideas how I can get this working?
I think I had a similar issue, and ended doing something like
create the user without the field you would like to pass as an option
ask the client for the value of that field
update the user
So:
//server
Accounts.onCreateUser(function(options, user) {
if (user.services.facebook)
user.askReferer = true;
retur user;
});
//client
var userId;
Tracker.autorun(function() {
var user = Meteor.user();
if (!user || user.id === userId || !user.askReferer) {return;}
userId = user.id;
var referer = ...;
Users.update({_id: user.id}, {$set: {referer: referer}}, {$unset: {askReferer: 1}});
});
Would love if someone comes up with a better solution!
As the title suggests I would like to provide functionality to allow a user to update the email they use to login to my app using Firebase Simple Login. Cannot figure out an elegant way to do this. App uses AngularFire if that is relevant.
Does one exist or do I need to create a new account and delete the old one using the $removeUser() and $createUser() methods?
Update for Firebase 2.1.x
The Firebase SDK now provides a changeEmail method.
var ref = new Firebase('https://<instance>.firebaseio.com');
ref.changeEmail({
oldEmail: 'kato#domain.com',
newEmail: 'kato2#kato.com' ,
password: '******'
}, function(err) {
console.log(err ? 'failed to change email: ' + err : 'changed email successfully!');
});
Historical answer for Firebase 1.x
In Simple Login, this is equivalent to changing the user's ID. So there is no way to do this on the fly. Simply create the new account, remove the old one as you have already suggested.
If you're user profiles in Firebase, you'll want to move those as well. Here's brute force, safe method to migrate an account, including user profiles. You could, naturally, improve upon this with some objectification and futures:
var ref = new Firebase('URL/user_profiles');
var auth = new FirebaseSimpleLogin(ref);
// assume user has logged in, or we obtained their old UID by
// looking up email address in our profiles
var oldUid = 'simplelogin:123';
moveUser( auth, ref, oldUid, '123#abc.com', '456#def.com', 'xxx' );
function moveUser( auth, usersRef, oldUid, oldId, newId, pass ) {
// execute activities in order; first we copy old paths to new
createLogin(function(user) {
copyProfile(user.uid, function() {
// and once they safely exist, then we can delete the old ones
removeOldProfile();
removeOldLogin();
});
});
function copyProfile(newId, next) {
ref.child(oldUid).once('value', function(snap) {
if( snap.val() !== null ) {
ref.child(newId, snap.val(), function(err) {
logError(err);
if( !err ) { next(); }
});
}
});
}
function removeOldProfile() {
ref.child(oldId).remove(logError);
}
function createLogin(next) {
auth.createUser(newId, pass, function(err, user) {
logError(err);
if( !err ) { next(user); }
});
}
function removeOldLogin() {
auth.removeUser(oldId, pass, logError);
}
}
function logError(err) {
if( err ) { console.error(err); }
}
I'm using the accounts-ui package and would like to process some javascript as soon as the user is logged in/and or registered.
Is there an event that gets called as soon as the user signs in?
The raw login API (eg loginWithFacebook, loginWithPassword, etc) has a callback that fires when login is complete, but this is not currently exposed through accounts-ui. This may change.
A potential workaround, inspired by Werner's suggestion, but also taking page load into account:
var oldUserId = undefined;
Meteor.autorun(function() {
var newUserId = Meteor.userId();
if (oldUserId === null && newUserId) {
console.log('The user logged in');
} else if (newUserId === null && oldUserId) {
console.log('The user logged out');
}
oldUserId = Meteor.userId();
});
You could check the result of the Meteor.userId() function inside Meteor.autorun():
Meteor.autorun(function() {
if (Meteor.userId()) {
console.log('The user logged in');
}
});
Just to give an alternative; I monkey patched the callback function. It looks a bit more complex because the credentialRequestCompleteHandler requires a function that returns a function but besides that its a plain monkey patch. Stick this in main.js or something that gets processed late and only once. I hope it helps for future reference.
var orgCallback = Accounts.oauth.credentialRequestCompleteHandler;
Accounts.oauth.credentialRequestCompleteHandler = function(callback){
return function (credentialTokenOrError) {
var tmpFunc = orgCallback(callback);
tmpFunc(credentialTokenOrError);
alert("do your own thing here");
}
}