问题
I am setting up a meteor app that involves signing up with a username and password, then hopefully connecting that account with facebook and twitter.
I have the first part up and running easily, just with the accounts package. But when I have a logged in user call Meteor.loginWithFacebook(), it logs them out and creates a new account. What I want is something that adds the facebook credentials to the currently logged in user.
The meteor docs have this:
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId()
username: "cool_kid_13", // unique name
emails: [
// each email address can only belong to one user.
{ address: "cool@example.com", verified: true },
{ address: "another@different.com", verified: false }
],
createdAt: Wed Aug 21 2013 15:16:52 GMT-0700 (PDT),
profile: {
// The profile is writable by the user by default.
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}
which appears to be an account with a username that's also authenticated with Facebook. But I'm not sure if that's just an example that you can't actually achieve with basic Meteor stuff.
What I am trying to make is basically
Meteor.connectWithExternalAccount();
which runs the same process as Meteor.loginWithExternalAccount(), but just adds the information to the currently logged in user.
If someone could just explain the accounts-base package a bit so I could know where to start for myself that would be great.
Also, does anyone know if this will be included in any future versions of Meteor?
回答1:
So there's been work to solve this problem, but unfortunately the pull request https://github.com/meteor/meteor/pull/1133 never got merged. Probably your best bet is check the meteor-core Google Groups and see if there was any comment on it and if not, see if you can get a core dev to comment on it.
If you want to still use the pull request, what you could do is remove the various Meteor accounts-*
packages, then in the root of your project, create a /packages
folder and copy yubozhao's patched accounts-*
packages in there (probably smart to append -custom
). You'd then meteor add accounts-base-custom
, etc. to add them to your project.
Note though, yubozhao wrote this is 6-7 months ago and you may need to stick with whatever version of Meteor that was current then for it to work.
Update April 2014: There's an Atmosphere package now that has a similar use case that might be useful: https://atmospherejs.com/package/accounts-merge
回答2:
Here is a code that worked for me ( inside server folder ):
Accounts.onCreateUser(function(options, user) {
var email, oldUser, service;
/*
user.groups = {
created: "",
invited:"",
RSVP:{
coming:"",
notComing:"",
noReplay:""
}
};
*/
if (user.profile == null) {
user.profile = options.profile
}
if (user.services != null) {
service = _.keys(user.services)[0];
email = user.services[service].email;
if (email != null) {
oldUser = Meteor.users.findOne({
"emails.address": email
});
if (oldUser != null) {
if (oldUser.services == null) {
oldUser.services = {};
}
if (service === "google" || service === "facebook") {
oldUser.services[service] = user.services[service];
Meteor.users.remove(oldUser._id);
user = oldUser;
}
} else {
if (service === "google" || service === "facebook") {
if (user.services[service].email != null) {
user.emails = [
{
address: user.services[service].email,
verified: true
}
];
} else {
throw new Meteor.Error(500, "" + service + " account has no email attached");
}
user.profile.name = user.services[service].name;
}
}
}
}
return user;
});
userAddOauthCredentials: function(token, userId, service) {
var data, oldUser, selector, updateSelector;
switch (service) {
case "facebook":
data = Facebook.retrieveCredential(token).serviceData;
break;
case "google":
data = Google.retrieveCredential(token).serviceData;
}
selector = "services." + service + ".id";
oldUser = Meteor.users.findOne({
selector: data.id
});
if (oldUser != null) {
throw new Meteor.Error(500, ("This " + service + " account has already") + "been assigned to another user.");
}
updateSelector = "services." + service;
Meteor.users.update(userId, {
$set: {
updateSelector: data
}
});
if (!_.contains(Meteor.user().emails, data.email)) {
return Meteor.users.update(userId, {
$push: {
"emails": {
address: data.email,
verified: true
}
}
});
}
}
inside client js folder :
var addUserService;
addUserService = function(service) {
if (service === "email") {
} else {
switch (service) {
case "facebook":
return Facebook.requestCredential({
requestPermissions: ["email", "user_friends", "manage_notifications"]
}, function(token) {
return Meteor.call("userAddOauthCredentials", token, Meteor.userId(), service, function(err, resp) {
if (err != null) {
return Meteor.userError.throwError(err.reason);
}
});
});
case "google":
return Google.requestCredential({
requestPermissions: ["email", "https://www.googleapis.com/auth/calendar"],
requestOfflineToken: true
}, function(token) {
return Meteor.call("userAddOauthCredentials", token, Meteor.userId(), service, function(err, resp) {
if (err != null) {
return Meteor.userError.throwError(err.reason);
}
});
});
}
}
};
same js file inside template events:
"click a": function(e) {
var service;
e.preventDefault();
service = $(event.target).data("service");
return addUserService(service);
}
and for the html just done this:
<div class="social"><a id="fb" data-service="facebook"><img src="/../facebook.png"></a></div>
<div class="social"><a id="go" data-service="google"><img src="/../googleplus.png"></a></div>
mainly you need to have the data-service set to your service, then the template click event takes the data-service and execute the addUserService(data-passed).
hope it will work, please let me know.
来源:https://stackoverflow.com/questions/20391689/connect-service-to-existing-meteor-account