Integrating FBOAuth with Services
Everyone loves the Services module. It allows you to easily create API endpoints for Drupal's core features, permitting you to interact with Drupal from external applications. It also very easily extended.
Among its many default resources, Services offers a user.login method that allows external applications to authenticate via Drupal. I'm going to share a quick snippet that will permit you to extend Services so that it allows your users to login via Facebook (leveraging the FBOauth module).
Start by downloading and enabling Services and Fboauth. Then, create a custom module to house your code. Your module's info file should specify services and fboauth as requirements.
name = Services FBOauth
description = Provides FBOAuth integration for the Services module.
package = services
core = 7.x
dependencies[] = services
dependencies[] = fboauth
Easy enough. Now let's use Service's hook_services_resources() function to define a new resource.
/**
* Implements hook_services_resources().
*/
function services_fboauth_services_resources() {
$definition['fboauth']['actions']['connect'] = array(
'help' => 'Login a user for a new session via FBOAuth',
'callback' => 'services_fboauth_connect',
'args' => array(
array(
'name' => 'access_token',
'type' => 'string',
'description' => 'A valid Facebook access token',
'source' => 'data',
'optional' => FALSE,
),
),
// The services module says this about services_access_menu:
// "If you think you need it you are almost certainly wrong."
// But I think that this is one of those rare exceptions.
'access callback' => 'services_access_menu',
);
return $definition;
}
Here, we've defined a new resource type "fboauth." We've also defined a new action for that resource, labeled 'connect'. This will be available at /your_endpoint_path/fboauth/connect.
Now let's define the services_fboauth_connect() callback that makes the magic happen.
/**
* Allow FBOAUTH login via services.
*
* @param $data,
* An associative array containing:
* - access_token: a valid Facebook access token (not access code).
* The requesting application must have already gone through the
* process of requesting permissions, getting access code, requesting
* access token, etc.
*
* @return
* A valid session object, just like _user_resource_login().
*/
function services_fboauth_connect($data) {
if ($user->uid) {
// user is already logged in
return services_error(t('Already logged in as @user.', array('@user' => $user->name)), 406);
}
// Include fboauth functions as required.
module_load_include('inc', 'fboauth', 'includes/fboauth.fboauth');
$access_token = $data['access_token'];
$app_id = variable_get('fboauth_id', '');
// Find Drupal user that corresponds with this Facebook user.
$fbuser = fboauth_graph_query('me', $access_token);
$uid = fboauth_uid_load($fbuser->id);
if ($user = user_load($uid)) {
if ($user->status) {
// Much of the login logic was taken from _user_resource_login().
user_login_finalize();
$return = new stdClass();
$return->sessid = session_id();
$return->session_name = session_name();
services_remove_user_data($user);
$return->user = $user;
return $return;
}
else {
$message = t('The username %name has not been activated or is blocked.', array('%name' => $account->name));
}
}
else {
$message = t('Error: no Drupal account was found for the specified Facebook user');
}
watchdog('services_fboauth', $message);
return services_error($message, 401);
}
To use this function, your external application must POST a valid 'access_token' to /your_endpoint_path/fboauth/connect. The endpoint will then return a valid session object, identical to the one posted by the default user.login method.
That's all there is to it!
7.x,
drupal, fboauth, services, snippet