31

I have an 'intranet' site that I have built, which has a login system of its own (users register as new users, and use the username/password thereon to login to the site). However, now I want to extend it, and have the intranet site use the existing ActiveDirectory for authentication. This is what I am looking for, going forward -

When a user access this intranet site (http://intranetsite/mySite), the user's domain credentials are validated against the active directory, and if the user's credentials match AD, the user is then presented the main page of the intranet site.

I am new to AD, and do not know how to go about this configuration. My intranet site is built around PHP and uses Apache on the application server; the AD is on a different IIS server.

What information do I need, and where do I put this information (into my site? htaccess? anywhere else?) so that I can use AD authentication? Is just 'configuration' enough, or do I need to write explicit PHP code for this authentication?

Any pointers are much appreciated.

kallakafar
  • 725
  • 3
  • 11
  • 27
  • [adLDAP](http://adldap.sourceforge.net) library might be very helpful for this. – dev-null-dweller Jul 21 '13 at 15:01
  • You're going to want to use the LDAP library for PHP, and request an account from your AD admin that has 'read only' access to the catalog. – DevlshOne Jul 21 '13 at 15:01
  • Let me know when you get further along and I'll happily show you what I've got setup. – DevlshOne Jul 21 '13 at 15:02
  • You'll get some good info from [this previous SO Question][1] [1]: http://stackoverflow.com/questions/3236007/how-to-retrieve-user-info-fra-a-active-directory-security-group-using-ldap-and-p?rq=1 – DevlshOne Jul 21 '13 at 15:04
  • @DevlshOne - thanks for the pointer to SO question. I am quite new to AD. May I know what all information I need to get from the AD admin? – kallakafar Jul 21 '13 at 15:11
  • @DevlshOne - my intranet site is hosted in a server that is within the domain. FYI :) Do I have to write custom PHP code still? – kallakafar Jul 21 '13 at 15:12
  • The Domain administrator is going to need to give you the username and password for an account that has the ability to view / read all accounts on the system. He'll also need to supply you with the name (or IP address) of the domain server and any backup domain servers. I'll go ahead and post my `custom` code below and you can use it to create your own. It's not nearly as much work as it sounds! – DevlshOne Jul 21 '13 at 16:19
  • @DevlshOne, there's no need to have dedicated account with read access to all user accounts in the domain in Active Directory - to verify any user's credentials, all the php code must do is to bind - if the binding is successful, the account is valid. If not, the account is not valid for whatever reason ( wrong pwd, expired account, locked account, disabled account etc. ). – Robert Rossmann Jul 21 '13 at 18:39
  • @ShadowWalker There my not NEED to be a dedicated account to bind to AD on a corporate network but I have found that most corporate networks have password change policies that could effective cause the coder a need to change the password on a regular basis. However, this password change policy would not be in effect for a dedicated account. – DevlshOne Jul 21 '13 at 21:57
  • My point was that to authenticate a user against Active Directory, the php script simply attempts a bind operation AS that user. The user currently using the mentioned Intranet site provides username and password. Any password policies, account status, logon hours, allowed workstations or any other account-related stuff is taken into consideration automatically during the bind operation, thus eliminating to have any dedicated service account. I would like to see an example situation where a service account is necessary to verify a user's credentials. – Robert Rossmann Jul 22 '13 at 00:13

2 Answers2

26

If you are looking only for authentication and nothing else, you may get away with only a few lines of code.

First, ensure you have ldap enabled in your php.

Here's pure php implementation:
(note that when doing it this way you should ensure that you DO HAVE a username and a password from a user - anonymous binding will almost always return true for AD)

$link = ldap_connect('domain.com'); // Your domain or domain server

if(! $link) {
    // Could not connect to server - handle error appropriately
}

ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, 3); // Recommended for AD

// Now try to authenticate with credentials provided by user
if (! ldap_bind($link, 'username@domain.com', 'SomeSecret')) {
    // Invalid credentials! Handle error appropriately
}
// Bind was successful - continue

If you expect to do more fun stuff with Active Directory like pulling some information about currently logged in user I strongly recommend using a framework to do the heavy lifting for you. As already mentioned, adLDAP is a good one and if you run PHP 5.4 I dare recommending the AD-X library which I actively develop (you can install it via Composer).

With the AD-X library, you can verify a user's credentials using this code:

try {
    $link = new ADX\Core\Link('domain.com'); // Establish connection to AD
    $link->bind('username@domain.com', 'SomeSecret'); // Authenticate user
}
catch (ADX\Core\ServerUnreachableException $e) {
    // Unable to connect to server, handle error
}
catch (ADX\Core\InvalidCredentialsException $e) {
    // Invalid credentials supplied
}
catch (Exception $e) {
    // Something else happened, check the exception and handle appropriately
}

// Successfully authenticated if no exception has been thrown

Feel free to choose which suits you best. However, if you expect to do more than authenticate I strongly suggest you use a library for the ldap work - it will save you a lot of time and possibly frustration when things do not work as you would expect them to.

Also, if in doubt what information you can/should use to connect and to authenticate feel free to check my previous answer on this topic.

Community
  • 1
  • 1
Robert Rossmann
  • 11,931
  • 4
  • 42
  • 73
  • While you certainly demonstrate how to connect and bind to AD with adLDAP this code includes so much advanced level code that it effectively conceals parts of code the asker is interested in most. – DevlshOne Jul 21 '13 at 21:53
  • 5
    My example does not demonstrate anything with adLDAP. Which part of it do you consider advanced and how would you recommend to update it? Thanks for explanation. – Robert Rossmann Jul 22 '13 at 00:17
  • 5
    How I can login user automatically? Your method requires username and password. How I can get those? – Somnium Mar 17 '17 at 09:21
8

Here is what I use:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');

define('DOMAIN_FQDN', 'mycompany.intra');
define('LDAP_SERVER', '192.168.0.1');

if (isset($_POST['submit']))
{
    $user = strip_tags($_POST['username']) .'@'. DOMAIN_FQDN;
    $pass = stripslashes($_POST['password']);

    $conn = ldap_connect("ldap://". LDAP_SERVER ."/");

    if (!$conn)
        $err = 'Could not connect to LDAP server';

    else
    {
        define('LDAP_OPT_DIAGNOSTIC_MESSAGE', 0x0032);

        ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3);
        ldap_set_option($conn, LDAP_OPT_REFERRALS, 0);

        $bind = @ldap_bind($conn, $user, $pass);

        ldap_get_option($conn, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error);

        if (!empty($extended_error))
        {
            $errno = explode(',', $extended_error);
            $errno = $errno[2];
            $errno = explode(' ', $errno);
            $errno = $errno[2];
            $errno = intval($errno);

            if ($errno == 532)
                $err = 'Unable to login: Password expired';
        }

        elseif ($bind)
        {
            $base_dn = array("CN=Users,DC=". join(',DC=', explode('.', DOMAIN_FQDN)), 
                "OU=Users,OU=People,DC=". join(',DC=', explode('.', DOMAIN_FQDN)));

            $result = ldap_search(array($conn,$conn), $base_dn, "(cn=*)");

            if (!count($result))
                $err = 'Unable to login: '. ldap_error($conn);

            else
            {
                foreach ($result as $res)
                {
                    $info = ldap_get_entries($conn, $res);

                    for ($i = 0; $i < $info['count']; $i++)
                    {
                        if (isset($info[$i]['userprincipalname']) AND strtolower($info[$i]['userprincipalname'][0]) == strtolower($user))
                        {
                            session_start();

                            $username = explode('@', $user);
                            $_SESSION['foo'] = 'bar';

                            // set session variables...

                            break;
                        }
                    }
                }
            }
        }
    }

    // session OK, redirect to home page
    if (isset($_SESSION['foo']))
    {
        header('Location: /');
        exit();
    }

    elseif (!isset($err)) $err = 'Unable to login: '. ldap_error($conn);

    ldap_close($conn);
}
?>
<!DOCTYPE html><head><title>Login</title></head>
<style>
* { font-family: Calibri, Tahoma, Arial, sans-serif; }
.errmsg { color: red; }
#loginbox { font-size: 12px; }
</style>
<body>
<div align="center"><img id="imghdr" src="/img/logo.png" height="100" /><br><br><h2>Login</h2><br><br>

<div style="margin:10px 0;"></div>
<div title="Login" style="width:400px" id="loginbox">
    <div style="padding:10px 0 10px 60px">
    <form action="/login.php" id="login" method="post">
        <table><?php if (isset($err)) echo '<tr><td colspan="2" class="errmsg">'. $err .'</td></tr>'; ?>
            <tr>
                <td>Login:</td>
                <td><input type="text" name="username" style="border: 1px solid #ccc;" autocomplete="off"/></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type="password" name="password" style="border: 1px solid #ccc;" autocomplete="off"/></td>
            </tr>
        </table>
        <input class="button" type="submit" name="submit" value="Login" />
    </form>
    </div>
</div>
</div>
</body></html>
jrm
  • 97
  • 1
  • 2