Darren Mothersele

London based Drupal Web Developer
Drupal.org Google+ Facebook Twitter GitHub LinkedIn RSS Feed

Using an external data source for Drupal user authentication and login

You can use any database table to validate users for your Drupal site. In this case I'm validating users against a database of clients. This client database is maintained outside of Drupal. It's a simple two step process, and just needs a couple of PHP functions in a custom module:

Step One: hook_auth() puts our own authentication code into the Drupal authentication process, we return true to validate a user, or false if the credentials don't match. If we have successfully authenticated someone then we also set a global variable that we can check in the next step.

Step Two: hook_user() allows us to put our own code into the user creation process. When a new user is authenticated Drupal will create a local user object for them. We hook into this process to fill in their details.

<?php
/**
* Implementation of hook_auth()
*/
function modulename_auth($username, $pass, $server) {
 
// Does username exist in our clients database?
 
$results = db_query("SELECT login, password FROM clients WHERE login = '%s' AND password = '%s'",$username,$pass);
 
$row1 = db_fetch_array($results);
  if (
$row1) {
     global
$modulename_authenticated;
    
$modulename_authenticated = TRUE;
     return
TRUE;
  }
  else {
     return
FALSE;
  }
}

/**
* Implementation of hook_user()
*/
function modulename_user($op, &$edit, &$account, $category = NULL) {
  switch(
$op) {
     case
'insert':
       
// a new user is being added; check if we did authentication,
       
global $modulename_authenticated;
        if (
$modulename_authenticated) {
         
$result = db_result(db_query("SELECT email FROM clients WHERE login = '%s'",$account->name));
         
// Set email address in the user table for this user
          // You might want to set up some other parts of the user record here too
         
db_query("UPDATE {users} SET mail = '%s', name = '%s' WHERE uid = %d", $result, $account->name, $account->uid);
        }
  }
}
?>

I'm looking to do this on one of my drupal sites. I've been using drupal for years now, but am only now getting into module development. Could you please further detail how to make a custom module of this to implement?

Thanks =)

Ryan

To create this as a module, you need two files:

modulename.info
modulename.module

You save these files in a folder called modulename somewhere within the Drupal module include path. For example in /sites/all/modules/modulename

The first of these files contains the basic module information. This can be as basic as this...

name = modulename
description = what your module does
package = Other

You then put the PHP code for the hooks you wish to implement into the *.module file. Hooks are documented on api.drupal.org. All your hooks start with the module name, as per the config file, and your filenames. For this example the content of the modulename.module file would be as follows:

<?php
/**
* Implementation of hook_auth()
*/
function modulename_auth($username, $pass, $server) {
 
// Does username exist in our clients database?
 
$results = db_query("SELECT login, password FROM clients WHERE login = '%s' AND password = '%s'",$username,$pass);
 
$row1 = db_fetch_array($results);
  if (
$row1) {
     global
$modulename_authenticated;
    
$modulename_authenticated = TRUE;
     return
TRUE;
  }
  else {
     return
FALSE;
  }
}

/**
* Implementation of hook_user()
*/
function modulename_user($op, &$edit, &$account, $category = NULL) {
  switch(
$op) {
     case
'insert':
       
// a new user is being added; check if we did authentication,
       
global $modulename_authenticated;
        if (
$modulename_authenticated) {
         
$result = db_result(db_query("SELECT email FROM clients WHERE login = '%s'",$account->name));
         
// Set email address in the user table for this user
          // You might want to set up some other parts of the user record here too
         
db_query("UPDATE {users} SET mail = '%s', name = '%s' WHERE uid = %d", $result, $account->name, $account->uid);
        }
  }
}
?>

You will need to adapt the actual authentication code to match your requirements.

Does that make sense?

Hmm

I tried this, but had no luck.

Does this force the normal login form to use your custom authentication? Or do you need to make your own form?

The normal login process will call your custom authentication when the built in login process fails.

The process is as follows:

  1. user submits login information
  2. drupal checks if user is blocked, if so message is displayed and login fails
  3. drupal then attempts to load the user locally
    • if this is successful then the user object is loaded and drupal fires the load and login hooks. the user is then directed towards the 'user' page.
    • if user is not found locally then drupal will call external authentication

External authentication involves firing the auth hook, and on success either validating an existing user or creating a new user object. When a user is created this way a record is stored in the authmap table that confirms which module validates this user login.

just wondering, since the $user object doesnt have alot of things for this users session when signing in for the first time, like user's email which also might be in the old database, how/where do you prefill it? maybe an array to global in auth() or in insert $op in user(). but like what should be put into the $user array if so

also are there any cercumstances where pretend auth() might just authenticate user based on something other than user/pass. like IP address, then does anything get inserted into the user database automatically? cause it seems like it does, and then insert $op you can add onto it.

just curious for wierd authentication schemes like by ip address.

I have made this (Drupal 5.7) but wanted to point out that in my case Drupal didn't add the username in the users table as it should after I return true in the hook_auth. Instead it get an error in core user-module user_access() where it tries to implode an empty array of roles. As far as I can see it might be a Drupal-bug as there are no roles because the user is not loaded, and it is not loaded because thats why it is being added in the first place...
Let me know if someelse have run in to the same issue.

I'm looking to do something very similar to what you're explaining here, thank you very much! I have a few questions though. I don't see how you pick a server and a database in the code. I see that the input for the function modulename_auth contains $server, but I don't see where I pick the database I want to look into. I don't understand how it works with choosing the server either...
I installed and enabled the module you're describing, changing the SQL query but the users are still picked from the old location. Do I change the whole 'user' module too?
Thank you for your time and response.

Vitek

I did a external authentication against our MS SQL user table with something like this and it actually worked. But after login there showed several warning messages, as discussed in drupal.org/node/208908.

Any suggestions? Thanks a lot.

The warning messages were gone once Drupal was upgraded to the 5.8 release.

Again, thanks for the posting.

Dan

I am a total Drupal noob. Where can I add this code without going the module path?

So it just overwrites those two methods.

For additional help for Drupal 6, check out Pro Drupal Development. Google has some of the book available to read online.

Hi There,

I have to create my own login form for Drupal new user registration. When the new user enters Drupal my login page should be displayed and when he enter User name and Password , he should be registered by Drupal and when he enters the same in Drupal's orginal User name and password he should be sucessfully able to login into drupal

You probably don't need to create your own login form, you can render then Drupal provided login form by using drupal_get_form('user_login')

You do realize this page is almost 100% useless? Can ANYONE please provide how to do this? This author says "oh, make these two functions and you are done"... NO WAY! There is a lot more involved. I'm not saying you need to teach newbies how to authenticate against their MySQL table but please... "make these two functions and save them" is about 1/10th of the story as far as I can tell. I'm about to just give up and call it impossible... Drupal documentation pretty much sucks...

This information is not applicable to Drupal 6.

To use an external data source for authentication in Drupal 6 you need to use hook_form_alter to add your own validation function to the login form.

The two functions above are intended as an outline of how it was done in Drupal 5. You only need these two hooks to extend the authentication - anything further than this will be specific to your requirements.

Could you provide an example for Drupal 6? Otherwise, it might be helpful if you make explicit the version in your article's title. :)

Pro Drupal Development chapter 6 page 130 explains all

Thanks for pointing me here... Found what I needed, now it's time to get to work!

Hey Darren,

Thanks for the post. Just wanted to let you know that your functions worked great for me turning what I figured would be a bit of digging around and work into a real quick module.

Thanks again,
--
Dan

Hey

Hey Darren,

http://drupal.org/node/330091 isn't by chance familiar to you is it?

Thanks,
--
Dan

Plesae any one tell me how I do that authentication drupal 6

Because I have two site Site A and Site B on same server with different database how I login on site A and that is authenticate Site B

please tell me if any one know

It is worth pointing out that this no longer works on Drupal 6- the API has been changed.

An update of this post would be really useful though!

As mentioned above this is for Drupal 5. For Drupal 6 you probably need to look at using hook_form_alter.

I am trying to get this to work in drupal 6. My users need to login with their mobile number and password and I cannot seem to get the login process to hook this function.

I echo out "test" and it shows nothing so I know its not even being called. All the other hooks in my module work fine. Any suggestions?

D5

As stated above this old article relates to Drupal 5.

>> I have my site built in D-6.15
>> I have another external database.

>> There is not a single Unique Field Between Both Drupal Database and External Database.

>> When User try to login to the site he/she should be validated from the external Database. In External Database There is a field - status

If this status is TRUE than and only than User can access the DRUPAL SITE.

Any Suggestion on this?

Well, you will need to add a unique field to Drupal user objects that matches something in the external database, otherwise you have no way of knowing which is the right user object to load once you have authenticated a user.

You can then deal with the authentication by querying the external database in a custom submit handler added to the user_login form using hook_form_alter. You may also need to do some work to manage the relationship between drupal users accounts and external user records, possibly creating users if they don't exist locally.

Good luck, and remember to contact me if you require further assistance.

I've got the Simple External Authentication example from the Pro Drupal Development book (Chapter 6 p.130) working somewhat successfully, but I am a noob and was hoping for just a little more guidance.

With this example code I can successfully login with any username that begins with "dave", but I cannot log in with my admin account.

I am guessing that is because this code example fails to call

"user_login_authenticate_validate"

... in the "not a Dave" section of the code (i.e., if the username does not begin with "dave" the example code just assumes it is an error. I have tried to add a call to "user_login_authenticate_validate" in this section of the code but I haven't gotten that to work yet and I am really unsure of what to do next.

Is there anyone who has filled out that part of the code so that "non-dave" users that already exist in the User table (such as my admin account) will also work?

90% of code already exists

I have successfully implemented the "Simple External Authentication" example in Chapter 6 of Pro Drupal Development (second edition), but there are a few gaps in the logic that are needed and for which as a noob I need help.

My Drupal site will have several user accounts:

  • one admin account (which happens to be username "admin")
  • several supervisor accounts (for simplifying this example let's assume they all also begin with "adm", such as "admsvc01", "admsvc25", "admsvc88", etc.)

In addition to these admin and supervisor usernames, I would like to maintain a separate database (external to Drupal because it may reside on a remote MySQL server) containing all of the regular members' username/passwords. Thus, when you login to Drupal you would be entering a username/password that is either in the standard User table entry -OR- in this external database.

Here is the 10% that I need

The example from Chapter 6 does about 90% of what I need, but the following logic is still needed:

  • the example assumes that ALL usernames/passwords are in an external source (i.e., it does not let you sign in as "admin" if "admin" is not in the external source)
    • I would like to be able to login with usernames/passwords that can be either in the standard User table (which in my case would be those that begin with "adm") -OR- that exist in the external table (which would be those that do NOT begin with "adm")
  • in the Chapter 6 example, the first time that a particular username is validated against the external source the Chapter 6 example's logic will dynamically creates a permanent User table entry for that username
    • if I log in today, a User table entry is dynamically created today for the externally-authenticated accounts
    • if tonite that username is removed from the external table (or its password is changed in the external table) then unfortunately tomorrow I can still sign on because the dynamically-created User table entry still exists)

Does anyone have any suggestions how to augment this example from Chapter 6 to...

  1. allow both standard User table usernames/passwords as well as external source usernames/passwords to be used
  2. for external source usernames/passwords: if a username in the external source table gets removed, or if the password in the external source table gets changed, then the dynamically-created User table entry that was created for it yesterday when you logged in is no longer valid today
    Note that I don't want to just delete the dynamically-created User table entries because there may have been nodes of content created on behalf of those users that would be lost if the username was deleted and re-created each time the user logged in

NOTE: I've already figured out how to add an external database - I just need help in using it to validate these non-native usernames/passwords

I'm dealing with the same issue right now. If you find any approach to handle it please let me know.

Can you help on how to do this on Drupal 7?

Have a look at this post on d.o that looks at a sample workflow for an external authentication module in Drupal 7. Essentially you need to use hook_form_alter() to add your validation function into the login block. I'm actually working on something like this at the moment, so I will post up my progress when I've worked it out.

I'm a noob in the module development game. I've gone through every drupal.org posting and external websites i could find on the topic. My main sources of help were:

https://www.touchnoc.com/node/83
http://omegadelta.net/2011/03/23/custom-authentication-with-drupal-7/
http://drupal.stackexchange.com/questions/4346/logging-into-drupal-from-...

Here's is what I'm trying to accomplish and what I've done so far.

I've added an additional database definition to the array in settings.php:

$databases = array (
  'default' =>
  array (
    'default' => // default database definition
    array (
      'database' => 'default_db',
      'username' => 'defusername',
      'password' => 'passone',
      'host' => 'defaulthost',
      'port' => '5432',
      'driver' => 'pgsql',
      'prefix' => '',
    ),
  'external' => // external authenticate database definition
  array (
      'database' => 'external_db',
      'username' => 'extusername',
      'password' => 'passtwo',
      'host' => 'externalhost',
      'port' => '5432',
      'driver' => 'pgsql',
  ),
),
);

I've created a module called mycustomextauth and properly have the .info and .module files created. Here's what I have in the .module file:

<?php

/**
* Implements hook_form_FORM_ID_alter().
*/

/**
* Alter the user login block form
*/

       
function mycustomextauth_form_user_login_block_alter(&$form, &$form_state) {
           
_mycustomextauth_user_login_form_alter($form, $form_state);
        }

/**
* Alter the user login page
*/

       
function mycustomextauth_form_user_login_alter(&$form, &$form_state) {
           
_mycustomextauth_user_login_form_alter($form, $form_state);
        }



        function
_mycustomextauth_user_login_form_alter(&$form, &$form_state) {
           
$saveForm = $form;

           
$form = array();

           
// overrides Drupals default validator
           
foreach( $saveForm as $key => $value ) {
                if(
$key == '#validate' ) {

                   
$form[ $key ] = array();
                    foreach(
$value as $validator ) {
                        if(
$validator == 'user_login_authenticate_validate' ) {
                           
$validator = 'mycustomextauth_authenticate_validate';
                        }
                       
$form[ $key ][] = $validator;
                    }
                } else {
                   
$form[ $key ] = $value;
                }
            }
        }

/**
* Custom Validation
*/


       
function mycustomextauth_authenticate_validate( $form, &$form_state ) {

           
$name = $form_state[ 'values' ][ 'name' ];
           
$pass = $form_state[ 'values' ][ 'pass' ];

            if(
$name == 'root' )
            {
                return
user_login_authenticate_validate( $form, $form_state );
            }

           
$authenticated = your_awesome_authentication_method($name, $pass)

            if (
$authenticated)
            {
               
// this sets up the external user with Drupal by creating a local entry. If they don't exist they are added
               
user_external_login_register($name, "mycustomextauth");

               
// we must set the 'uid' to pass back.  this looks up the logged in user and sets the Drupal UID
               
$account = user_external_load($name);
               
$form_state['uid'] = $account->uid;
            }
            else
            {
               
// do nothing, Drupal will handle the errors

           
}
        }


?>

My question is what do I do with this line?

$authenticated = your_awesome_authentication_method($name, $pass)

I know I need to call the external DB with the following (In the DB table, the usr_email field is the username field):

$records = db_select( 'users', 'u', array( 'target' => 'other' ))
->fields( 'u', array( 'usr_email', 'usr_password' ))
->condition( 'u.usr_email', $usr_email, '=' )
->execute();

But exactly where and how? Any help you can provide is deeply appreciated.

You need to switch to the external DB, then use the database API to talk to that database. Something like this:

<?php
  $other_database
= array(
     
'database' => 'databasename',
     
'username' => 'username',
     
'password' => 'password',
     
'host' => 'localhost',
     
'driver' => 'mysql',
  );
 
// replace 'YourDatabaseKey' with something that's unique to your module
 
Database::addConnectionInfo('YourDatabaseKey', 'default', $other_database);
 
db_set_active('YourDatabaseKey');

 
// execute queries here

 
db_set_active();
 
// without the paramater means set back to the default for the site
?>

Example code from http://drupal.org/node/18429

Is there any Module for drupal so we can add to the checkout page which works alongside the customer information pane so that when a user has entered an email address an ajax lookup is performed. If the email is found in our user database the user will have to login before continuing. We are currently using logintoboggan to allow customers to login with email also.

Post new comment
The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

Recent comments
Latest Posts