Socialize with FBConnect
FBWhat?
According to Facebook, "Facebook Connect is a powerful set of APIs for developers that lets users bring their identity and connections everywhere." The idea is quite similar to OAuth or Yahoo's BBauth, but with a Facebook flavor.
You can read more technical stuff about it here:
http://wiki.developers.facebook.com/index.php/Getting_Started_with_Facebook_Connect
Why would you need it? The simple scenario: you can use it like a bizarro OpenID, until Facebook becomes an OpenID provider :) Some of you will just want this: "Allow people to authenticate on my website using their Facebook account."
But once the user is authenticated, your Catalyst app may also access the Facebook API using your user's credentials. Assuming she gives you permission, you can do all kinds of tricks, like getting the list of friends, avatar, and more.
I want it, what now?
Just use Catalyst::Authentication::Credential::FBConnect. Once authenticated, this will give you the Facebook user identifier and a session key you can later use with WWW::Facebook::API.
First of all, you need to signup as a developer and get an API Key, a Secret, and an Application Name. You'll need these to use Facebook Connect in Catalyst. Do your thing at http://developers.facebook.com and come back with the info.
Then you need to install Catalyst::Authentication::Credential::FBConnect from CPAN. It depends on WWW::Facebook::API, Moose, MooseX::Types::Moose and MooseX::Types::Common
Setting it up
First, set up your Catalyst application. You should be familiar with authentication realms by now; if not, take a look at Catalyst::Plugin::Authentication.
If you use the FBConnect credential to authenticate, you don't even need
a database in your app. But most of the time you'll want to associate
the Facebook user with a local user, and allow users to also authenticate
the standard, password-based way. That's why we'll need two realms,
facebook
and dbic
.
package MyApp; __PACKAGE__->config( 'authentication' => { default_realm => 'facebook', realms => { facebook => { credential => { class => 'FBConnect', api_key => 'my_api_key', secret => 'my_secret', app_name => 'my_app_name', } }, dbic => { credential => { class => 'Password', password_type => 'none', }, store => { class => 'DBIx::Class', user_class => 'DB::User', id_field => 'user_id' } } } } );
The user table should have some columns for holding the external credential
info, if you want to associate the two. We're using credential_identifier
to hold the Facebook uid. If you're using multiple external authentication
systems (like OpenID or OAuth) it would be a good idea to specify the source
for this particular credential in ( credential_source
).
package MyApp::Schema::Result::User; use strict; use warnings; use base 'DBIx::Class'; __PACKAGE__->table( 'users' ); __PACKAGE__->add_columns( user_id => { data_type => 'integer', is_auto_increment => 1, }, email => { data_type => 'varchar', is_nullable => 1 }, password => { data_type => 'varchar', is_nullable => 1 }, credential_identifier => { data_type => 'varchar', is_nullable => 1 }, credential_source => { data_type => 'varchar', is_nullable => 1 }, ); __PACKAGE__->set_primary_key( 'user_id' ); __PACKAGE__->add_unique_constraint( [ qw/ credential_identifier credential_source / ] ); 1;
The login action
The logic is simple: the first time you call $c->authenticate
for
the facebook
realm, the user will be redirected to the Facebook
login page. Once she manages to authenticate there, she will be sent
back by Facebook to our application (in the same action), but
accompanied by an auth_token
. When authenticate
is called this
time, the user is authenticated and $c->user
is created with the
session information. All this logic is abstracted away inside the
credential.
Once she's authenticated with FBConnect, she'll either register or
login (hence find_or_create) in our internal user database. After
that we'll just use the familiar API to reauthenticate the user in the
dbic
realm.
sub login : Path('/login/facebook') { my ($self, $c) = @_; if( $c->authenticate( {}, 'facebook' ) ) { my $user = $c->model('DB::User')->find_or_create( { credential_identifier => $c->user->session_uid, credential_source => 'facebook', } ); $c->authenticate( { credential_identifier => $user->credential_identifier, credential_source => 'facebook' }, 'dbic' ) or die "Login failed"; } }
Connect an existing user
You can also assign a Facebook account to an already existing account:
sub assign : Path('/assign/facebook') { my ($self, $c) = @_; my $user = $c->user if $c->user_in_realm('dbic'); if( $c->authenticate( {}, 'facebook' ) ) { $user->update( { credential_identifier => $c->user->session_uid, credential_source => 'facebook', } ); $c->authenticate( { credential_identifier => $user->credential_identifier, credential_source => 'facebook' }, 'dbic' ) or die "Login failed"; } }
Use the Facebook API
All the work above gets you the uid from Facebook. This can be used later on with WWW::Facebook::API.
Here's some example code to actually use WWW::Facebook::API:
my $client = WWW::Facebook::API->new( desktop => 0, api_key => 'my_api_key' secret => 'my_secret' ); #get user info my $response = $client->users->get_info( uids => $c->user->credential_identifier, fields => [ qw/about_me quotes/ ] ); # get user's friends my $friends = $client->friends->get( uid => $c->user->credential_identifier );
More about this can be found in the WWW::Facebook::API docs, enjoy :)
AUTHOR
Cosmin Budrica <cosmin@sinapticode.com>
COPYRIGHT
Copyright 2009 Sinapticode - http://www.sinapticode.com