Everything is becoming user-centric these days. Signing on, the usual prerequisite to access additional features of a website is not as prescriptive as it used to be. Sites are now less disposed to demand user registration prior to signing on - a typical usability barrier. Instead, users can opt to log in by designating an existing identity provided by another site. Getting rid of the potentially disruptive registration process makes sense and in tune with creating flowing user experience. For example, users are more likely to comment on blog posts if their actions (and thoughts!) are not punctuated with lengthy form filling and authentication processes. Indeed, better 'flow' is a key goal in many emerging interaction design patterns, e.g. 'stay on the page'.
Federated identity represents the cutting edge of this user-centric trend which also coincides with Web 2.0 in many ways. First, digital content and services from heterogeneous and autonomous websites are increasingly intertwined and federated, in what could be described as diffusion user experience. Second, the plethora of APIs made available by services or sites such as Google, Yahoo!, Twitter and Facebook have really fostered the developments of federated use scenarios in which user identities and data are becoming interoperable.
The following provides a technical account of integrating Twitter sign on into a social networking platform, as a prerequisite for the UX2.0 project work in developing Twitter services for digital library use scenarios.
Digital Library, Social Networking, Twitter
A remit of UX2.0 deals with the concept of digital library as places where human interactions also take place (in addition to resource discovery). This naturally dovetails with the development of social networking - how it can enhance an existing digital library based on Blacklight, an open source library catalog featuring faceted searching.
In order to assimilate social networking capability, I opted for tog because it is:
- based on Ruby on Rails the same underpinning technology as Blacklight
- open source, modular, extensible and can be integrated with Blacklight
- engine-based which fits in with UX2.0's later work on developing the visual elements of user experience (see 'surface', 'skeleton', 'structure' planes), based on the Progressive Enhancement design goal (HTML first, AJAX next)
The project is developing federated use scenarios with existing social networking sites - Twitter. Examples of the scenarios include federated sign on using the social network ID, escalating comments to the networks (as 'tweets'). However, tog doesn't support Twitter off-the-shelf. But Twitter provides a range of APIs, enabling other web and desktop application to build interoperable services with Twitter.
Twitter OAuth Authentication
To enable Twitter support in tog, it is necessary deal with the OAuth authentication API. The requirement not only underpins identity services (sign on, user profile and site preference import), it is also mandatory for some Twitter API methods, e.g. 'status update'. OAuth is a token-based protocol that enables users to authorise cross-domain access to their data without revealing passwords. The use of OAuth is becoming widespread. Google for example, also use the protocol to control the access to a range of API services. This includes Google Book Search.
The rest of this post provides details on how to integrate Twitter authentication in tog as a primary sign on mechanism, and how to import user profile data from Twitter to automatically create a local user account.
User Experience and Demo Application
You may want to read more about the typical OAuth workflow from the end user perspective and try out an online demo of the tog-Twitter integration described below - select 'Sign in with Twitter'. You can also try signing in at the UX2.0 project website which is based on the same tog implementation described here.
To install the application (incorporates the integration described here) on a Ruby on Rails enabled machine, run:
rails twitter_tog_demo -m http://github.com/boonious/tog_core/raw/master/tog_template.rb
Then edit the config/initializers/oauth_consumers.rb file, inserting the Twitter consumer service credentials (unique key, secret) for the application. The credentials can be obtained by registering the application with Twitter.
Plugins Involved in the Integration
Just like any other Ruby on Rails (RoR) application, tog is underpinned by plugins each provides unique functionality (e.g. blog,). The plugins are based on Model-View-Controller (MVC) system architecture and provide default views and interactions which can be customised (overwritten).
The Twitter integration work described below involves 4 plugins: tog_user, tog_social, rest-authentication (generic plugin required by tog_user) and oauth-plugin (new plugin needed for OAuth authentication - NOT part of tog). The first three plugins can be found in the standard 'vendor/plugins' application folder if you install tog from the official distribution. To install oauth-plugin, run the following at tog application root:
script/plugin install git://github.com/pelle/oauth-plugin.git
1. tog_user
This plugin facilitates standard password-based authentication processes (including activation, password reminder). It provides basic UI and user administration. At the core of this plugin, is a User model which includes functionality of another plugin: rest-authentication. It also interoperates with tog_social which automatically creates a default user profile post signing up.
Model (tog_user)
To provide federated login, implement a new subclass - a FederatedUser model inheriting all of the User model functionality:
class FederatedUser < User
attr_accessible :service_provider
after_create :update_profile
has_many :consumer_tokens, :dependent => :destroy, :foreign_key => :user_id
# credentials - user profile data fetched from Twitter etc
# during federated login process
def initialize(credentials, attributes = {})
super(attributes)
@credentials = credentials
end
....
protected
# import user profile data fetched from Twitter
def import_profile_from_twitter
self.profile.first_name = @credentials['name']
self.profile.website = @credentials['url']
self.profile.federated_profile_image_url=@credentials['profile_image_url']
self.profile.save
end
..
end
Full details and comments of the FederatedUser model
The FederatedUser model links to (has_many) the oauth-plugin tokens which are required for consuming OAuth services such as the Twitter APIs. The model contains an initialise method that marshals user data (credentials) from Twitter (passed from oauth-plugin after the Twitter sign on process). The data is subsequently used to update the default local user profile. The model also contains hooks to provide the same mechanisms for other service providers such as Google.
Both the User and FederatedUser models use the same database table through Single Table Inheritance (STI). In order to distinguish local and federated users who signed up with services such as Twitter, Facebook, Google, two addition attributes (type, service_provider) are required in the User table via a new database migration:
View (tog_user) Modify the login page and add a sign on link for Twitter, pointing to the appropriate sign-on controller provided by oauth-plugin (see 'oauth_consumers' below): views/session/new.html.erb
Controller (tog_user) No change since the Twitter OAuth sign on mechanism is addressed by a controller in oauth-plugin.
2. rest-authentication
This generic plugin underpins the authentication functionality of tog_user which includes post sign up activation (user clicking on a link in email). The activation typically sets an internal user profile status to 'active', enabling the registered user in tog's social networking services. In federated login scenarios, there is no signing up requirement. Post authentication, a local tog (Twitter) user account (with empty password) will be created and activated automatically without emailing and further user actions. The following code, added to the authorisation module in rest-authentication (in lib/authorization/aasm_roles.rb) will enable the auto-activation process which is invoked during OAuth sign in (see oauth-plugin below).
aasm_event :federated_user_activate do
transitions :from => :passive, :to => :active
end
This is a stopgap until a better solution is devised, to eliminate the need of modifying this generic plugin.
3. tog_social
The user data retrieved from Twitter contains a profile image URL ('profile_image_url'). This image can be incorporated in the local profile administered by tog_social since the Twitter image provides better user representation than the existing default anonymous user icon. Moreover, this image can still be applicable if it is not superseded by another picture uploaded by user (via tog).
Minor modification is required to enable the use of this Twitter user image:
- Add a new federated_profile_image_url attribute to the Profile database table, via a new migration. The attribute stores the Twitter profile image URL: db/migrate/007_add_federated_profile_image_url_to_profiles.rb
- Modify the icon_for_profile in the Profile helper (app/helpers/profiles_helper.rb) so that the method displays Twitter profile image (via the stored URL) if a local version is not available: app/helpers/profiles_helper.rb
4. oauth-plugin
This is a key plugin which facilitates system interactions with the Twitter OAuth API. It is a generic plugin which can be adapted for other federated sign on services (e.g. UX2.0 works on Google OpenID/OAuth service, to be featured in a future post). Currently there seemed to be two common models for federated sign-on:
- Master & Slaves: users designate federated identities (slaves) for a primary local account (master). Example: Vimeo, the video sharing site requires users to create a password-based user account. User can then link the account with a Facebook identity.
- Federated Primary: users designate a federated identity for an alternative primary user account. Example: Stack Overflow and TypePad. The former does not provide local password accounts at all and relies solely on user to have an existing identity from other providers. It allows users to link several federated IDs together.
Off-the-shelf, oauth-plugin supports the master and slaves model. It requires user to sign on first with a local account before authenticating with Twitter. This contrasts with UX2.0's aim to develop user experience scenarios based primarily on Twitter (and other) ID. As a result, it is necessary to modify the plugin so that it supports the federated primary model.
Controller (oauth-plugin) Remove the dependency on a local (signed on) user (modifying codes containing 'login_required' and 'current_user' references):
- generators/oauth_consumer/templates/controller.rb, changing the @consumer_tokens line to return tokens of a signed in user OR an empty array (if no 'current_user').
- lib/oauth/controllers/consumer_controller.rb, getting rid of the 'before_filter :login_required' line to prevent the plugin from requesting sign on session prior to Twitter login; the @token line to prevent nil 'current_user' reference.
Model (oauth-plugin) The plugin is a framework into which different OAuth services can be implemented or plugged into, via various service Token model subclasses (reside in the lib/oauth/models/consumers/services folder). The model subclasses typically provide OAuth service setup details such as URL end points, and specific software client creation and methods that deals with API requests such as fetching user profile and issuing status update.
For Twitter the service model is TwitterToken, the service model makes use of the 'twitter' gem (Ruby library) to interact with Twitter APIs.
Since the Twitter sign on is based on the 'Federated Primary' model, a local federated user account needs to be generated automatically if the Twitter user signs on for the first time. The user creation code can be incorporated within the 'create_from_request_token' class method. Hence, override the class method in TwitterToken:
def self.create_from_request_token(user,token,secret,oauth_verifier)
logger.info "create_from_request_token"
request_token=OAuth::RequestToken.new consumer,token,secret
access_token=request_token.get_access_token :oauth_verifier=>oauth_verifier
unless user # if no user logged in via username/password create a new federated user (User subclass)
twitter_oauth=Twitter::OAuth.new consumer.key, consumer.secret
twitter_oauth.authorize_from_access access_token.token, access_token.secret
twitter_credentials = Twitter::Base.new(twitter_oauth).verify_credentials
twitter_login = FederatedUser.custom_login(twitter_credentials, service_name)
twitter_user = User.find_by_login twitter_login # this user login before?
unless twitter_user # create a new federated user if sign on first time
twitter_user = FederatedUser.new(twitter_credentials, {:login=>twitter_login,:service_provider=>service_name.to_s})
twitter_user.save(perform_validation=false)
twitter_user.federated_user_activate!
end
user = twitter_user
end
logger.info self.inspect
logger.info user.inspect
existing_token = TwitterToken.find_by_user_id(user.id)
if existing_token
existing_token.destroy
end
create :user_id=>user.id,:token=>access_token.token,:secret=>access_token.secret
end
Note that the above creates a FederatedUser (User) subclass in tog_user.
Full details and comments of the TwitterToken service model.
this great thanks for posting!
Posted by: online writing | 03/24/2011 at 01:46 PM
Thank you for posting it! for me, a common man which is trying to get more information about networking, it was very informative. even though it is hard for me to understand all the terms, I get by.. thanks !
Posted by: forum widget | 04/17/2011 at 02:21 PM
Very honoured to see your blog, I benefited a lot here, and it brings me a great deal of enjoyment.
I sincerely hope your blog continually up to date. I hope you can write more post better in the future
Thanks for post and i think i will learn something more here.
Posted by: Air Yeezy | 08/01/2011 at 08:36 AM
The first three plugins can be found in the standard 'vendor/plugins' application folder if you install tog from the official distribution. To install oauth-plugin, run the following at tog application root
Posted by: tattoo supply | 08/11/2011 at 10:32 AM