Mixing in Temporary Anonymous Users with Omniauth
On a site I made with some fellow students, world-view.today, we allowed users who logged in with Twitter to customize the site by adding in their own cities. The log in was created using omniauth 2 and basically followed the steps outlined in fellow classmate Randall’s blog, substituting twitter for github.
One of the improvements we really wanted to make was to allow non logged in users to add in cities, a task that was not as daunting as we anticipated.
Our site before anonymous users
Our cities controller would associate a new city with a user whenever a logged in user added a city. Our user table simply stored the active recorded created user_id, created_at, updated_at, as well as name, provider, uid, and image. The provider column represented the provider used by omniauth, which in our case was always Twitter. The uid column was the unique id returned by the oath provider, twitter in our case, and the image was the Twitter profile image of the user.
A city_users join table kept the user city relationships.
Adding temporary anonymous users
Searching Google for creating users just based on a temporary basis using omniauth didn’t yield any results so we set off to figure it out on our own.
We settled on using the
session_id that rails automatically creates and stores in a cookie for any visitor on your site. This id string is unique for any given visitor and thus we used this session_id as the unique identifier to store in the user’s uid column.
The first step in implementing this logically fell with our cities controller which handled the task of adding new cities and associating them with the current user. (Technically probably best to move this functionality into the model) When a new city was passed to the cities controller Create method via an ajax post request, the first thing the method did was set an @user variable:
session[:user_id] was a variable the sessions controller would add to the session cookie after a successful twitter oauth negotiation, aka only logged in users would have it. This variable also corresponds to the
user_id assigned to a user when it is created with active record.
The first step was to create the temp user when the controller received a new city and the current user was not logged in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
The user would immediately get shoveled in the 5 default cities, allowing them to be treated as a normal user that should always have exactly 5 cities. Whenever a new city is added, the last city is deleted and the new city is added as the first.
Also note we set provider to anon. This allowed us to know this was a temporary user account. We also added to this temp user’s session cookie the user_id variable that now corresponded to their user_id in our users’ table.
Overall this allowed us to treat temporary users that could be treated like a real user without having to change much code.
When a temp user isn’t a real user
There were some distinctions that we decided would be practical to keep. We didn’t want our temp user’s to be able to save content from within each of their cities and these temp users still needed the option to actually log in with twitter.
These distinctions actually fit within our code base pretty well. Basically we wanted to treat a temp user as not logged in except for purposes of tracking which cities they had.
Rails was keeping track of whether or not a user was logged in via a helper method in the application controller called logged_in?
1 2 3 4 5 6 7
We added a simple conditional check that made it so
logged_in? would only return true if the provider did not equal anon in addition to their being a user_id var in the session.
1 2 3
On the front end side we created a similar global variable called
loggedIn that would get set and update as needed via ajax requests. The rails users controller sets the value of this variable based on checking the users logged in status.
This meant we had a temp user whose cities would persist, but for all other purposes they would be treated as logged out.
Transitioning a temp user to a twitter authenticated user
To make the user experience as seamless as possible we wanted to make it so that a temp user who customized their cities and then logged in to our site with their twitter account for the first time would be able to keep their cities.
To do this we modified the Sessions Controller’s Create method which we had originally made when implementing OmniAuth with Twitter. This route is called as the twitter oauth callback and thus is invoked right after someone authenticates with twitter.
We added the third nested
if that checks to see if the session contains a
user_id. This was a perfect test for finding temp users who then authenticate with twitter because only a temp user or a fully logged in user would have a session user_id; however, a user who is already logged in would never be routed to this create method, an oauth callback route.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
This third nested
if sets the @user variable to the temp user if it finds a session
user_id. It then replaces the
uid of that user with the twitter provided oauth uid and also replaces the
image provided by the twitter authentication. Effectively this converts the temp user to a twitter authenticated, persistent world-view user who retains the cities they selected as a temp user.
If a user has authenticated with twitter on our site before but they come back as an anonymous temp user, when they re-authenticate with twitter, their information from their temp account is not passed on to their previous user.
A database full of temp users
One slight draw back to our temp user approach is that our database fills up with temp users who will never be accessed after a temp users session cookie is lost or expires.
On solution to this is to create a cron job that periodically checks for users with provider anon who were created over 30 days ago and then purges them.