In my iOS application on the initial launch user can sign in with 3 different ways: email/pass, Facebook or Twitter. Registering with each of them will cause to create 3 different users.
Later in my app user should have the possibility to link accounts. For example, if he signed in with email/pass, then he should be able to link Facebook and Twitter.
Parse native -linkInBackground works great in case we have only one user, which is email/pass. But if we will already have Facebook or Twitter account created, the Parse SDK will reply with error, saying that this account is already linked to the social.
At first I thought that I've figured out the workaround:
Save the old user data
Delete the old user
Sign in (not link) with the social (say, Facebook)
Fill all the data from the saved user to the new one.
Everything went well with one social, but it appeared that the "authData" field, which is very important in my approach, is not contained in the [PFUser currentUser]. And so despite the fact that all other data is transferred successfully, the authData is not transferred, so that we don't have authentication data of the old user.
The question is: Is there a way to get authData from Parse, or is there any "legal" way to achieve my initial goal of merging (linking) two (three) users?
No, you can not access the auth data, just like you can't access the password when the user authenticates directly with email/pass. This is a security requirement and will not change.
Ideally you should add some logic which checks if the new user should be created or if a user with the specified details (probably email address) already exists. If it does then you can reject the new user creation and return a suggestion as to how the user should login.
Well, I've got it working in a bit different way than I wanted, but looks like it's a quite stable solution.
I've created a custom Parse Class, called, say, MyUser. This class has several fields, like: facebookUser, twitterUser, regularUser, anyOtherSocialUserYouWant. Each of them is a pointer to the _User class.
Now if we are signed as, for example, a regular user, the regular user is created. The same happens with the rest like Twitter or Facebook. By signing in with three different socials we will get three different parse users. That's okay.
Next somewhere in the app, user will press "Link to Facebook" button. We need to save the current user. Next we will make a query for both users if there is a MyClass object with pointers to one of those two users (first is the current user, the second is the new user after sign in). If there is no such MyClass object, we will create one and fill in two fields: regularUser and FacebookUser.
Later if user will try to link to Twitter, we will do the same, and the query will return existing MyClass object, which will have two fields with pointers to regular and Facebook users. We will just add a pointer to the newly signed in Twitter user to that MyClass object.
The only trick is with the data, which should be common for all of the users. It is easy enough with the appropriate query. We will need to do two queries (or one "OR" query, but looks like "OR" query is not working with pointers): first query for required object with "user" field of current user, and the second query (inner query) is for objects with "linkedUser" equal to current MyClass object.
And thus to maintain objects we will need to have two fields on each object: "user" and "linkedUser". First field will be used always, and the second field will be used only after linking.
And yes, after MyClass user is created you will need to check if objects for current user and newly registered user has this fields "linkedUser" and fill it with the MyClass pointer in case of that field is empty.
Hope this will help someone.
P.S.: Dear Parse.com developers, I really appreciate your work, but you should really think about creating the kind of mechanism to merge users.
Related
I am writing an app, where a signed up user should be able to see which of his contacts have signed up, too. What is the most elegant way to do this?
I was planning to create an array of all locally saved email addresses extracted from the user's local iOS addressbook and create a query for those. Is there any better way to do this?
Edit: Is this actually possible without downloading the whole user list? I could use a for loop with queryStartingAtValue(emailAddress) and queryEndingAtValue(emailAddress). But this could possibly lead to hundreds of queries at the same time.
In NoSQL databases you'll often end up modeling the data in ways that your application wants to consume it.
In this case it seems your app needs to look up whether a user exists, based on their email address. For that purpose I'd add a list of email-to-uid data:
emailToUid
"test#mail,com": "P0...wklsh1"
"MJQZ1347": "Aj1278a..."
This is essentially a self-created index that allows you to check whether an email address is used without having to run a query.
Now you can loop over the contact and look whether there is a user for that email address with a:
ref.child("emailToUid").child(email).observeSingleEventOfType(.Value
This is going to be very fast. Because of the way Firebase communicates with the back-end, there's going to be very little difference between a single request with 100 email addresses or 100 requests with a single email address. See my answer here for more on that: Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly
You could have something like this
user
-$user_id
-email
-username
-contacts
-contact_uid1:email1,
-contact_uid2:email2,
-contact_uid3:email3,
And then do:
Download the contact child from firebase and save it to a var
Create the loop to check every contact in the address book
If the email is in the contact child don't do anything (it means you already verified the user once)
If the email is not in the contact child launch a single event query to find the uid of an specific email
in the callback of the query if it is nsnull the user is not in the app
if the user exist, add the user to your contacts child node in firebase
This way you only launch the queries of the contacts you haven't checked
I know that to delete a Parse user, we have to call the "delete" method on the authenticated Parse User object. However, that only deletes the user in the User table, but not all data related to that user in other tables. Is there a way to trigger this automatically or do we have to implement this manually via some code in the "before_save" or "after_save"?
Also, should I log out the user after deleting his account?
You can implement it in before_save or after_save, but I would go with a hardcoded version of this. It strongly depends on your data structure, but generally, you would have to query for all the objects related to a user and then delete them separately (in background).
I would also log out the user, because due to caching, the app often doesn't realise that the user doesn't exist anymore, so I would go for logging the user out from Parse (even if that logout may fail because there is no user to logout anymore) and log him out of the app by showing him the login screen or whatever you're usually doing.
You see, deleting every information related to a user is a quite time and resource expensive task to do, so usually one simply deletes the user and his private information, and leaves the rest (for example in a messenger app, you would not delete the messages, as you want the other users still to be able to see what they wrote to the - now deleted - user).
I have two type of models in the application I'm working on: User and Account.
Every account has many users. Every user has one account.
When I download the user object from an API, I get the account_id, but not the actual account object. The account object will be downloaded after the user object.
What is the best practice for establishing the relationship between the user and his account in this situation?
Should I insert an empty row into the Accounts table with just its account_id field filled in? And then later, when I download the account, update that row?
First, Core Data centric definitions, you have 2 entities (User and Account) and no tables (because this is an object store, not a SQLite database).
So, you wouldn't have empty rows, you would have stub objects (partially complete objects that will be filled in later).
There is no best practice when it comes to stub objects. Whether you should create them is entirely dependent upon your use case. In some cases it helps to have the basic information about an item so that you have something to show the user while you go and get the details. In your case, you only have an identity so the benefit of stub objects seems very low.
I'm currently working on a rails app where I have two model classes, Account and User. I want to allow users to link their User with account information from other places.
What I want the user to be able to do is, under my edit user form, I have a list displaying the names of all linked accounts, and a text input for them to add more.
All I want them to do is fill in one field (account name) and hit a button, and the back end will hit a restful API for that account name, pull back relevant info, and populate an Account model object, linking to the current user.
For example, from the user page I would like the user to be able to type in their twitter handle, and in the back end I'd look up that twitter account id and associate it with that User in my database.
Where is the right place to have a hook so that I can take an input (twitter handle), fire off a helper method that populates a Account object, and then link that to my current user?
I'm not sure I understood exactly what you were asking but I'll try my best : If your account 'belongs_to' your User model, it would seem logical to use the create or update method (depending on your standard case) of your Accounts controller, to retrieve a JSON with the accounts datas, and that the client parse this answer (with some JS).
i have something in mind, i have some user types, Building owner, building manager.
I want to create user as building manager, but i dont want they have access to login system. this user are only for some selectbox in my website, but i need to show them in my user index page.
what i think i can do is create normal user and with a before_save i create a new data in another table.
In a request i need to be able to setup in my building form more than one building manager. maybe the best are with nested form.. I think i will need to add building id to my user table. maybe they can be assigned more than one building.
for now, my db structure are like this :
table users with user data (username, password, email, first and last name, phone)
table usertype have userid, typename and accesslvl
But this problem give me some managing problem. They will not be associated with user data.
How can i resolve this? Does Device can block some user? I searched in the Devise docs, but nothing found.
Thanks for your help.
There is an approach where admin users can approve other user accounts for login. You could use a similar approach but programmatically approve the accounts you actually want to allow logins for. Details are here:
https://github.com/plataformatec/devise/wiki/How-To:-Require-admin-to-activate-account-before-sign_in