Step 1 is to create a new module.
I found this reference useful: http://yetanotherprogrammingblog.com/node/14
This solution is based on drupal-6. There are differences in 7.
In the module you need to override hook_login_validate. e.g.
function mymodule_login_validate($form, &$form_state) {
$username = $form_state['values']['name'];
// do if condition to see if your authentication is required.
// e.g. you might want to keep the drupal administration users.
if (_mymodule_use_webservice_login($username)) {
// We are logging in using service.
if (_mymodule_validate_password($username, $form_state['values']['pass']) {
user_external_login_register($username, 'mymodule');
user_authenticate_finalize($form_state['values'];
} // else drop through to the end and return nothing - Drupal will handle the rejection for us
}
} else {
// Username is not an member number, so use standard Drupal authentication function
user_login_authenticate_validate($form, &$form_state);
}
In the above note that
- You will have to create the function with _use_webservice_login in the title. Test whether to go to the REST service. I like to put these into php classes for namespace sanity.
- You need to create the function that does the credentials test. _mymodule_validate_password. This goes to your service.
- As per normal drupal - change 'mymodule' to the name of the module that you create so that there are no namespace clashes.
I was connecting to a grails (spring/java) application to call the service. This mainly determines the call to make and variables. I use the built-in grails authentication service to make the authentication and override that as well to make a custom authentication. The call just against a rest service should be similar. You need a http request and parse the response.
A snippet from my PHP class doing _mymodule_validate_password is:
$headers = array(
'Content-Type' => 'application/x-www-form-urlencoded',
'X-Requested-With' => 'XMLHttpRequest',
);
// _spring_security_remember_me
$data = array(
'j_username' => urlencode($username),
'j_password' => urlencode($password),
'_spring_security_remember_me' => 'on',
);
$query_string = http_build_query($data, '', '&');
// WARNING - API changes in drupal 7
// CAUTION - don't allow it to retry. Grails gives us a redirect which drupal_http_request follows, losing the cookie
$response = drupal_http_request($endpoint_url, $headers, 'POST', $query_string, 0);
// Did the initial login succeed.
if ($response->code != 200 && $response->code != 302) {
watchdog(self::module_name, self::module_name, 'Failed to log in %error.', array('%error' => $response->status_message));
// FAIL
return false;
}
// Initial login success. Follow the redirect.
// Redirected request. Look for 200 && {"success":true,"username":"888"}
$redirect_response = drupal_http_request($response->redirect_url, array(), 'GET', NULL);
// The true success criteria
if ($redirect_response->code == 200 && array_key_exists('Set-Cookie', $response->headers)) {
// Authentication worked.
$response_data = json_decode($redirect_response->data);
if (property_exists($response_data, 'success') && $response_data->success &&
property_exists($response_data, 'username')) {
$login_success = true;
} else {
return false;
}
At the end of the day my request gives me a JSON {"success": true, "username":"888"}. The username is superfluous.
Grails does a redirect if authenticated to the service providing data. You could just check that you get '200' back from your web service and be done, or alternatively just check that your JSON data has the equivalent of 'success' in it. In the case above, we check the authentication, record the redirect and then call the redirect to get authentication data.
You can return basic drupal profile information from the service and set the details. Override mymodule_user to get access to post update call-backs. I update the details on every login, as the back-end is the primary source of data. The reference at the top of this article uses 'update'.
function mymodule_user($type, &$edit, &$account, $category = null) {
switch ($type) {
case 'login' :
// Login should theoretically fail if the details don't get set.
... whatever you have to do to get the details. I call my class.
user_save($account, array('mail' => $fedAuth->email_address));
case 'logout' :
// remove the grails cookies.
}
}
Depending on what you are trying to do the fun may not end here. In my case I had to update custom profile information and save a client cookie to record login details for subsequent web service calls. Some argue that this is not 'restful' as there is a drupal session and a session in the web services provider.