Linking Google account with existing account created using email in Parse.com - javascript

I have implemented google login in parse. Here is my code:
var querystring = require('querystring');
var _ = require('underscore');
var Buffer = require('buffer').Buffer;
var googleValidateEndpoint = 'https://www.googleapis.com/oauth2/v1/userinfo';
var TokenStorage = Parse.Object.extend("TokenStorage");
var restrictedAcl = new Parse.ACL();
restrictedAcl.setPublicReadAccess(false);
restrictedAcl.setPublicWriteAccess(false);
Parse.Cloud.define('accessGoogleUser', function(req, res) {
var data = req.params;
var token = data.code;
/**
* Validate that code and state have been passed in as query parameters.
* Render an error page if this is invalid.
*/
if (!(data && data.code)) {
res.error('Invalid auth response received.');
return;
}
Parse.Cloud.useMasterKey();
Parse.Promise.as().then(function() {
// Validate & Exchange the code parameter for an access token from Google
return getGoogleAccessToken(data.code);
}).then(function(httpResponse) {
var userData = httpResponse.data;
if (userData && userData.id) {
return upsertGoogleUser(token, userData, data.email);
} else {
return Parse.Promise.error("Unable to parse Google data");
}
}).then(function(user) {
/**
* Send back the session token in the response to be used with 'become/becomeInBackground' functions
*/
res.success(user.getSessionToken());
}, function(error) {
/**
* If the error is an object error (e.g. from a Parse function) convert it
* to a string for display to the user.
*/
if (error && error.code && error.error) {
error = error.code + ' ' + error.error;
}
res.error(JSON.stringify(error));
});
});
var getGoogleAccessToken = function(code) {
var body = querystring.stringify({
access_token: code
});
return Parse.Cloud.httpRequest({
url: googleValidateEndpoint + '?access_token=' + code
});
}
var upsertGoogleUser = function(accessToken, googleData, emailId) {
var query = new Parse.Query(TokenStorage);
query.equalTo('accountId', googleData.id);
//query.ascending('createdAt');
// Check if this googleId has previously logged in, using the master key
return query.first({ useMasterKey: true }).then(function(tokenData) {
// If not, create a new user.
if (!tokenData) {
return newGoogleUser(accessToken, googleData, emailId);
}
// If found, fetch the user.
var user = tokenData.get('user');
return user.fetch({ useMasterKey: true }).then(function(user) {
// Update the access_token if it is different.
if (accessToken !== tokenData.get('accessToken')) {
tokenData.set('accessToken', accessToken);
}
/**
* This save will not use an API request if the token was not changed.
* e.g. when a new user is created and upsert is called again.
*/
return tokenData.save(null, { useMasterKey: true });
}).then(function(obj) {
// Reset password
password = new Buffer(24);
_.times(24, function(i) {
password.set(i, _.random(0, 255));
});
password = password.toString('base64')
user.setPassword(password);
return user.save();
}).then(function(user) {
// ReLogin
// This line is what I am talking about
return Parse.User.logIn(user.get('username'), password);
}).then(function(obj) {
// Return the user object.
return Parse.Promise.as(obj);
});
});
}
var newGoogleUser = function(accessToken, googleData, email) {
var user = new Parse.User();
// Generate a random username and password.
var username = new Buffer(24);
var password = new Buffer(24);
_.times(24, function(i) {
username.set(i, _.random(0, 255));
password.set(i, _.random(0, 255));
});
var name = googleData.name;
// name = name.split(" ");
// var fullname = name;
// if(name.length > 1)
// var lastName = name[name.length-1];
user.set("username", username.toString('base64'));
user.set("password", password.toString('base64'));
user.set("email", email);
user.set("fullName", name);
// user.set("last_name", lastName);
user.set("accountType", 'google');
// Sign up the new User
return user.signUp().then(function(user) {
// create a new TokenStorage object to store the user+Google association.
var ts = new TokenStorage();
ts.set('user', user);
ts.set('accountId', googleData.id);
ts.set('accessToken', accessToken);
ts.setACL(restrictedAcl);
// Use the master key because TokenStorage objects should be protected.
return ts.save(null, { useMasterKey: true });
}).then(function(tokenStorage) {
return upsertGoogleUser(accessToken, googleData);
});
}
It works perfectly fine. Now the problem I am facing is that I want to link google account with an existing parse account created using email or username & password. The problem in doing so is that to login/signup using google I have to reset the password of the user to login so as to get the session token. See this line in the code -> [This line is what I am talking about]. So if I do so an existing user who had earlier used username/email & password to login won't be able to login again using email since I have reset his/her password. I have seen this and all the other links related to this but none of which solves this problem.
Can somebody here guide me in the right direction?
Log added as response to one of the comments:
{"accountType":"google","createdAt":"2016-01-07T17:30:57.429Z","email":"skdkaney#gmail.com","fullName":"ashdakhs basdkbney","updatedAt":"2016-01-07T17:30:57.429Z","username":"owt3h0ZZEZQ1K7if55W2oo3TBLfeWM6m","objectId":"lSlsdsZ9"}
Added upsert function as per comment request:
var upsertGoogleUser = function(accessToken, googleData, emailId) {
var query = new Parse.Query(TokenStorage);
query.equalTo('accountId', googleData.id);
//query.ascending('createdAt');
// Check if this googleId has previously logged in, using the master key
return query.first({ useMasterKey: true }).then(function(tokenData) {
// If not, create a new user.
if (!tokenData) {
return newGoogleUser(accessToken, googleData, emailId);
}
// If found, fetch the user.
var userw = tokenData.get('user');
var users_id = userw.id;
var query2 = new Parse.Query(Parse.User);
query2.equalTo('objectId',users_id);
// The new query added
return query2.first({ useMasterKey: true }).then(function(user) {
// Update the access_token if it is different.
// if (accessToken !== tokenData.get('accessToken')) {
// tokenData.set('accessToken', accessToken);
// }
console.log(user);
console.log("******");
/**
* This save will not use an API request if the token was not changed.
* e.g. when a new user is created and upsert is called again.
*/
// return tokenData.save(null, { useMasterKey: true });
}).then(function(obj) {
console.log(obj);
// console.log(user);
var result = user ;
// Return the user object.
return Parse.Promise.as(result); // this is the user object acquired above
});

After a discussion with OP, there are possible solutions to this matter but each of them have pros and cons.
Disabling Revocable Session
Since the introduction of Revocable Session, getSessionToken will always return undefined even with master key. To turn it off, go to App Settings >> Users >> Turn off Require revocable sessions.
Then, in upsertGoogleUser method, you just need to return the user object from tokenData.get('user'). It is enough to call user.getSessionToken() in your main cloud function. The final method should look like:
var upsertGoogleUser = function(accessToken, googleData, emailId) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query(TokenStorage);
query.equalTo('accountId', googleData.id);
//query.ascending('createdAt');
// Check if this googleId has previously logged in, using the master key
return query.first().then(function(tokenData) {
// If not, create a new user.
if (!tokenData) {
return newGoogleUser(accessToken, googleData, emailId);
}
// If found, fetch the user.
var userw = tokenData.get('user');
var users_id = userw.id;
var query2 = new Parse.Query(Parse.User);
query2.equalTo('objectId',users_id);
return query2.first().then(function(user) {
console.log(user);
console.log(user.getSessionToken());
console.log("******");
return Parse.Promise.as(user);
});
});
};
User Password Input
In order not to change user's password, we can ask user to input his password once we successfully authenticated Google data. We then use the input password to log user in. This is not a good UX, since the purpose of Google Login is to increase usability by letting users not entering password.
Query on Parse.Session
This is a possible solution if you want to use "Revocable Session" feature. In the code above, instead of querying on Parse.User, we can look for any revocable session in Parse.Session class. Then we can call getSessionToken on returned object. This is not optimal solution in cases that we need to know which devices the user is logged in.
Reference:
Parse's Enhanced Session: http://blog.parse.com/announcements/announcing-new-enhanced-sessions/

Related

Retrieving Firebase Child Value in Javascript

function sontinue() {
var user = firebase.auth().currentUser;
var uid = user.uid;
var adaRef = firebase.database().ref("User/" + uid);
if (adaRef.orderByChild("Role").equalTo("admin")) {
location.href = "DB.html";
} else {
location.href = "index.html";
}
}
I would like to link my "admin" account to DB.html and "user" account to index.html but i think i always failed in Retrieving the Child Value.
You're not retrieving the data from the server. Remember you need to call .once('value') to get your query and then iterate through the remaining code based onw hether their value is of admin or user. Firebase Docs has more explination I've amended your code and put it below
function sontinue() {
var user = firebase.auth().currentUser;
var uid = user.uid;
var adaRef = firebase.database().ref("User/" + uid).orderByChild("Role");
//you now need to retrieve the data
return adaRef.once('value').then((snapshot)=>{
return snapshot.forEach(snapshot=>{
if (snapshot.child("Role").val()==="admin") {
location.href = "DB.html";
} else {
location.href = "index.html";
}
return console.log("added");
})
})
}
If you just wanted to find out who was of the user type admin...i'd use this below...much more efficient.
function sontinue() {
var user = firebase.auth().currentUser;
var uid = user.uid;
var adaRef = firebase.database().ref("User/" + uid).orderByChild("Role").equalTo("admin");
//you now need to retrieve the data
return adaRef.once('value').then((snapshot)=>{
//you now have all the users that are just admins
return snapshot.forEach(snapshot=>{
location.href = "DB.html";
return console.log("added");
})
})
}

ErrorAccessDenied delete attachments with microsoft graph

I'm trying to remove attachments with Microsoft Graph. I have the following function:
getAccessToken(function(accessToken) {
if (accessToken) {
// Create a Graph client
var client = MicrosoftGraph.Client.init({
authProvider: done => {
// Just return the token
done(null, accessToken);
}
});
console.log(files);
files.forEach(function(file) {
client
.api(
"/me/messages/" +
file.msg +
"/attachments/" +
file.attachment
)
.delete((err, res) => {
if (err) {
console.log(err);
return;
} else {
console.log(res);
$("#attachment_" + file.list).remove();
}
});
});
} else {
var error = {
responseText: "Could not retrieve access token"
};
}
});
// OAUTH FUNCTIONS =============================
function buildAuthUrl() {
// Generate random values for state and nonce
sessionStorage.authState = guid();
sessionStorage.authNonce = guid();
var authParams = {
response_type: "id_token token",
client_id: appId,
redirect_uri: redirectUri,
scope: scopes,
state: sessionStorage.authState,
nonce: sessionStorage.authNonce,
response_mode: "fragment"
};
return authEndpoint + $.param(authParams);
}
function handleTokenResponse(hash) {
// If this was a silent request remove the iframe
$("#auth-iframe").remove();
// clear tokens
sessionStorage.removeItem("accessToken");
sessionStorage.removeItem("idToken");
var tokenresponse = parseHashParams(hash);
// Check that state is what we sent in sign in request
if (tokenresponse.state != sessionStorage.authState) {
sessionStorage.removeItem("authState");
sessionStorage.removeItem("authNonce");
// Report error
window.location.hash =
"#error=Invalid+state&error_description=The+state+in+the+authorization+response+did+not+match+the+expected+value.+Please+try+signing+in+again.";
return;
}
sessionStorage.authState = "";
sessionStorage.accessToken = tokenresponse.access_token;
// Get the number of seconds the token is valid for,
// Subract 5 minutes (300 sec) to account for differences in clock settings
// Convert to milliseconds
var expiresin =
(parseInt(tokenresponse.expires_in) - 300) * 1000;
var now = new Date();
var expireDate = new Date(now.getTime() + expiresin);
sessionStorage.tokenExpires = expireDate.getTime();
sessionStorage.idToken = tokenresponse.id_token;
validateIdToken(function(isValid) {
if (isValid) {
// Re-render token to handle refresh
renderTokens();
// Redirect to home page
window.location.hash = "#";
} else {
clearUserState();
// Report error
window.location.hash =
"#error=Invalid+ID+token&error_description=ID+token+failed+validation,+please+try+signing+in+again.";
}
});
}
function validateIdToken(callback) {
// Per Azure docs (and OpenID spec), we MUST validate
// the ID token before using it. However, full validation
// of the signature currently requires a server-side component
// to fetch the public signing keys from Azure. This sample will
// skip that part (technically violating the OpenID spec) and do
// minimal validation
if (
null == sessionStorage.idToken ||
sessionStorage.idToken.length <= 0
) {
callback(false);
}
// JWT is in three parts seperated by '.'
var tokenParts = sessionStorage.idToken.split(".");
if (tokenParts.length != 3) {
callback(false);
}
// Parse the token parts
var header = KJUR.jws.JWS.readSafeJSONString(
b64utoutf8(tokenParts[0])
);
var payload = KJUR.jws.JWS.readSafeJSONString(
b64utoutf8(tokenParts[1])
);
// Check the nonce
if (payload.nonce != sessionStorage.authNonce) {
sessionStorage.authNonce = "";
callback(false);
}
sessionStorage.authNonce = "";
// Check the audience
if (payload.aud != appId) {
callback(false);
}
// Check the issuer
// Should be https://login.microsoftonline.com/{tenantid}/v2.0
if (
payload.iss !==
"https://login.microsoftonline.com/" +
payload.tid +
"/v2.0"
) {
callback(false);
}
// Check the valid dates
var now = new Date();
// To allow for slight inconsistencies in system clocks, adjust by 5 minutes
var notBefore = new Date((payload.nbf - 300) * 1000);
var expires = new Date((payload.exp + 300) * 1000);
if (now < notBefore || now > expires) {
callback(false);
}
// Now that we've passed our checks, save the bits of data
// we need from the token.
sessionStorage.userDisplayName = payload.name;
sessionStorage.userSigninName =
payload.preferred_username;
// Per the docs at:
// https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-implicit/#send-the-sign-in-request
// Check if this is a consumer account so we can set domain_hint properly
sessionStorage.userDomainType =
payload.tid === "9188040d-6c67-4c5b-b112-36a304b66dad"
? "consumers"
: "organizations";
callback(true);
}
function makeSilentTokenRequest(callback) {
// Build up a hidden iframe
var iframe = $("<iframe/>");
iframe.attr("id", "auth-iframe");
iframe.attr("name", "auth-iframe");
iframe.appendTo("body");
iframe.hide();
iframe.load(function() {
callback(sessionStorage.accessToken);
});
iframe.attr(
"src",
buildAuthUrl() +
"&prompt=none&domain_hint=" +
sessionStorage.userDomainType +
"&login_hint=" +
sessionStorage.userSigninName
);
}
// Helper method to validate token and refresh
// if needed
function getAccessToken(callback) {
var now = new Date().getTime();
var isExpired =
now > parseInt(sessionStorage.tokenExpires);
// Do we have a token already?
if (sessionStorage.accessToken && !isExpired) {
// Just return what we have
if (callback) {
callback(sessionStorage.accessToken);
}
} else {
// Attempt to do a hidden iframe request
makeSilentTokenRequest(callback);
}
}
Access token
eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDNXVuYTBFVUZnVElGOEVsYXh0V2pUUEF3aGltT2hjOXAxUkdiSnVjTDcyd0pjSFdTd1lpUXNFdDdqXzgxRVd6UXhvaWRaWnVXU2d5VS1HWXRqNFFNa3JjMUNpeFFTWElNRVpYQWhSUlJXZENBQSIsImFsZyI6IlJTMjU2IiwieDV0Ijoid1VMbVlmc3FkUXVXdFZfLWh4VnRESkpaTTRRIiwia2lkIjoid1VMbVlmc3FkUXVXdFZfLWh4VnRESkpaTTRRIn0.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9iNmFjNDlmNy1iMTNlLTQyOWMtYmI4NS0wODQ4OTY5NTA2OTkvIiwiaWF0IjoxNTQ0MTkyODUxLCJuYmYiOjE1NDQxOTI4NTEsImV4cCI6MTU0NDE5Njc1MSwiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IjQyUmdZT0JiMFhGQjBNSHFySW5FbVU5aEc4T1p6MTllOHpIRld1S0sxbldqQzVrOFAvd0IiLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6IkZvcm1wcmVzcyBjbGVhciBhdHRhY2htZW50cyIsImFwcGlkIjoiNTY0NzBlMjctZTAxNC00Zjg4LWEzY2QtZjQxODNlZjBhMjkxIiwiYXBwaWRhY3IiOiIwIiwiZmFtaWx5X25hbWUiOiJLYXJsc3NvbiIsImdpdmVuX25hbWUiOiJDaHJpc3RvcGhlciIsImlwYWRkciI6IjE1NS40LjE5NC4xMzQiLCJuYW1lIjoiQ2hyaXN0b3BoZXIgS2FybHNzb24iLCJvaWQiOiI5Y2JlMjBlYy1iMTk0LTRjZTYtOTU0Zi1hMTgzNTlmNGYzYTAiLCJwbGF0ZiI6IjMiLCJwdWlkIjoiMTAwMzAwMDA5QzI0MTI0NyIsInNjcCI6Ik1haWwuUmVhZCBvcGVuaWQgcHJvZmlsZSBVc2VyLlJlYWQgZW1haWwiLCJzaWduaW5fc3RhdGUiOlsia21zaSJdLCJzdWIiOiI5NVhONDhua08zbDJIeGYyTmdVTFo2YjA3WWpVV0lHOTRUQjA2bVNRUk9vIiwidGlkIjoiYjZhYzQ5ZjctYjEzZS00MjljLWJiODUtMDg0ODk2OTUwNjk5IiwidW5pcXVlX25hbWUiOiJjaHJpc3RvcGhlckBpdHBhcnRuZXJhYi5zZSIsInVwbiI6ImNocmlzdG9waGVyQGl0cGFydG5lcmFiLnNlIiwidXRpIjoiVmJGRW9EQ0RqMEMxVkFfSTNITWRBQSIsInZlciI6IjEuMCIsInhtc19zdCI6eyJzdWIiOiJka1FicDc3c2Y5ZmJlSnVYM1NqUml3aVhPa1FYOV9MZTF6MHp4X3Q4SU5BIn0sInhtc190Y2R0IjoxNDc4ODg0NzA1fQ.YQlKVdhXQcVwkcmbpY9Wx6ENro2DL7yH1rWwwkDZLD1inUrfbLRVb67lWKzgK9GnYP81d58Fp_2CZBw8C2E4X1eo02vog6_Qga9kVb8GF2-Ue0VP0KUv8EtRpEty_DBK7Re3iOkJR9yFSPQgf11Gf15l5O2mcEifrwny5nkRvab4_ssRt6hNf53V99uTFJ3_yKycGHPTobVbyQT5ZyDKxXRwoZVprFU70qrHGcBgo5emO8HbziYCUiQ9vGMpmtz61tE0U-c0E20FPC82i3zgLfMgmhNqmljZOpkOe85PFrxoep7fYkpZpWowCozugDW0E2A3SxBLZ_JHpci2R4irxg
files is a global variable that contains an array of objects, the data seems to be correct.
Here is a sample response I get when running the code:
{
statusCode: 403,
code: "ErrorAccessDenied",
message: "Access is denied. Check credentials and try again.",
requestId: "0739c0d9-38f2-45f7-a57d-c25dfbf92f75",
date: Fri Dec 07 2018 14:54:32 GMT+0100 (centraleuropeisk normaltid), …
}
API URL
me/messages/{id}/attachments/{id}
At https://apps.dev.microsoft.com/#/application/ I have given the application Mail.ReadWrite and Mail.ReadWrite.Shared access, though it only mentions read when signing up for the application which I find odd.
It looks like my app doesn't have the correct access. Is there something else I have to do that I have missed? Is my client query incorrect?
You need to check your scopes again. The Access Token you provided only includes the following scopes: Mail.Read openid profile User.Read email.
The code sample you provided references a scopes variable but it isn't clear what you've set this to:
var authParams = {
response_type: "id_token token",
client_id: appId,
redirect_uri: redirectUri,
scope: scopes,
state: sessionStorage.authState,
nonce: sessionStorage.authNonce,
response_mode: "fragment"
};
The value of this property should be something along the lines of openid profile email Mail.ReadWrite Mail.ReadWrite.Shared.

Sign in user if is register (Firebase facebook/google auth)

i´m want to check if the user who want to sign in using the facebook or google auth in to my web app is register on the real time database of firebase, so the idea is after the user press the button of sign in with facebook/google, first check in to the real time database if the uid is already on the real time database before redirect the user to another URL, for this i´m using the next function:
app.auth = function(){
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
var users_ref = firebase.database().ref('dream_wedding_users');
var register_user;
var register = false;
users_ref.on('value', function(snapshot) {
register_user = snapshot.val();
});
$.each(register_user, function(index, val){
if(user.uid === index){
register = true;
}
})
if(!register){
firebase.auth().signOut();
$('#login').modal('hide');
$('#modal_register').modal('show');
return false;
}else{
$(window).attr('location', 'http://localhost/wedding/en/gallery_en.php');
}
}
});
}
and for the auth function just the regular auth function for facebook and google that attach to a button.
app.facebook_login = function(){
var provider = new firebase.auth.FacebookAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
}).catch(function(error) {
console.log(error)
});
}
app.google_login = function(){
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Google Access Token. You can use it to access the Google API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
firebase.auth().signInWithRedirect(provider);
}).catch(function(error) {
console.log(error);
});
}
my problem is the next one, when i click sign in facebook or google, first login the user, then redirect the user, then check is the user is register on the real time data base and then logout the user if is not register and then show the modal. i don´t want that redirect the user i want that check if the user is register and then show the modal of "register" if the user is not register without redirect, and redirect if the user is register.
You want to add a unique user , if it is already not registered right ?
here is the valid way:
var usernew = result.additionalUserInfo.isNewUser
console.log('Is this a new user ? => ' + usernew );
complete code is as follows:
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result){
var usernew = result.additionalUserInfo.isNewUser;
console.log('Is this a new user ? => ' + usernew );
if (usernew == true) {
NewUser();
}
var token = result.credential.accessToken;
user = result.user;
// alert(JSON.stringify(user.photoURL))
}).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
var email = error.email;
var credential = error.credential;
});
add user if it is not registered:
function NewUser(){
firebase.database().ref('/users').push({
Name : 'Johnson'
Age : '22'
})
}

How to get current user's id in cloud code?

This cloud code to make friends worked in Parse.com.
Parse.Cloud.define("friend", function(request, response) {
var userToFriend = new Parse.User();
userToFriend.id = request.params.friendId;
var roleName = "friendsOf_" + request.user.id;
var roleQuery = new Parse.Query("_Role");
roleQuery.equalTo("name", roleName);
roleQuery.first().then(function(role) {
role.getUsers().add(userToFriend);
return role.save();
}).then(function() {
response.success("Success!");
});
});
But this wouldn't work anymore in the cloud code of parse-server, as there is no global current user.
I believe all I have to do to work with session tokens. But I don't quite understand how to get the current user's id.
for example, in order to make the function above work I need to build the roleName with current user id. var roleName = "friendsOf_" + request.user.id;
I believe I need to use the sessionToken to filter for a query that belongs to the current user.
var token = request.user.getSessionToken();
var query = new Parse.Query('???');
query.find({ sessionToken: token }).then( ... );
Can I just do this:
var token = request.user.getSessionToken();
var query = new Parse.Query('User');
query.find({ sessionToken: token }).then( function(user) {
var roleName = "friendsOf_" + user.id;
} );
Any advice would be appreciated,

Node Fibers Trouble with Meteor

I'm trying to write a simple authentication backend for Meteor that authenticates against an LDAP server. I need the function registered as login handler (the input to Accounts.registerLoginHandler) to return the id the of the user just logged in.
The problem I think lies in the Fiber I've created, getUserId, and that it's not returning id like I want it to. I know it has to be in a Fiber or else meteor gets angry and throws errors. Even though the log right before the yield shows me the id isn't undefined, getUserId.run() always returns undefined.
Any help would be greatly appreciated, thanks!
Accounts.registerLoginHandler(function(loginRequest) {
console.log("In login handler");
return auth.authenticate(loginRequest.username, loginRequest.password, function(err, ldap_user) {
if (err){
// ldap authentications was failed
console.log("Login failed");
return undefined;
}
else {
// authentication was successful
console.log("Login success");
// extracting team name from ldap record
var equals = ldap_user.memberOf.indexOf("=");
var comma = ldap_user.memberOf.indexOf(",");
var team_name = ldap_user.memberOf.slice(equals+1,comma);
// add user if they don't already exist
var getUserId = Fiber( function() { // Meteor code must be ran within a fiber
var id = null;
var user = Meteor.users.findOne({username: loginRequest.username});
if(!user) {
// insert user and kick back id
id = Meteor.users.insert({username: loginRequest.username,
profile : {team : team_name}
});
console.log('no user found, creating' + id);
} else {
id = user._id;
console.log('user found, returning id' + id);
}
console.log('id: '+id);
Fiber.yield(id); // return id
});
// send logged in users if by executing the fiber
return {id: getUserId.run()};
}
});
});
I think the problem is related to instead needing to use Meteor.bindEnvironment to control the scope of the (environment) variables and fibers in use.
A good three-step tutorial on the subject is found here:
https://www.eventedmind.com/feed/nodejs-introducing-fibers
https://www.eventedmind.com/feed/meteor-dynamic-scoping-with-environment-variables
https://www.eventedmind.com/feed/meteor-what-is-meteor-bindenvironment
My take on your code would be something like this (which in a similar problem worked for me):
Accounts.registerLoginHandler(function(loginRequest) {
console.log("In login handler");
var boundAuthenticateFunction = Meteor.bindEnvironment(function(err, ldap_user) {
if (err){
// ldap authentications was failed
console.log("Login failed");
return undefined;
}
else {
// authentication was successful
console.log("Login success");
// extracting team name from ldap record
var equals = ldap_user.memberOf.indexOf("=");
var comma = ldap_user.memberOf.indexOf(",");
var team_name = ldap_user.memberOf.slice(equals+1,comma);
// add user if they don't already exist
var id = null;
var user = Meteor.users.findOne({username: loginRequest.username});
if(!user) {
// insert user and kick back id
id = Meteor.users.insert({username: loginRequest.username,
profile : {team : team_name}
});
console.log('no user found, creating' + id);
} else {
id = user._id;
console.log('user found, returning id' + id);
}
console.log('id: '+id);
return {id: id};
}
}, function(e){throw e;});
return auth.authenticate(loginRequest.username, loginRequest.password, boundAuthenticateFunction);
});
Mind, the code sample above is untested...

Categories