UserLogin

Filepath: phork/php/utilities/UserLogin.class.php

The UserLogin class works with a User model and a UserLogin model to log a user in and out via cookie or form submission. Phork/it comes with pre-built models (UserModel and UserLoginModel) but the basic Phork package leaves them up to you to implement. Phork/it also comes with access hooks which automatically integrate the UserLogin utility into the site.

The idea behind the UserLogin model is that each record contains a user ID and a public and private key. The public key can be generated randomly and is stored in a cookie along with the user ID. The private key is built from a string unique to the user but that doens't change (eg. their browser information or user agent). A user can have several user login records (eg. one per browser if the browser data was used to generated the private key) but it's recommended that you set a maximum threshold and flush any older records.

When a user is logged in via cookie a call is made to load a user login record from the database by the user ID and public key from the cookie. The private key in the database is then compared to a newly generated private key and if they match then the cookie is considered valid and the user is logged in.


Example Private Key

//fall back to the user agent when no browscap available; if the user updates their browser they may be logged out
public function getPrivateKey($intUserId) {
    if (@ini_get('browscap')) {
        $objBrowser = get_browser();
        $strIdentity = $objBrowser->platform . $objBrowser->parent;
    } else {
        $strIdentity = $_SERVER['HTTP_USER_AGENT'];
    }
    return md5($intUserId . $strIdentity . AppConfig::get('HashKey'));
}

Example Public Key

$strPublicKey = md5(rand() . microtime());

Example Flush (from UserLoginModel)

public function flushExpired() {
    if ($intUserId = $this->current()->get('userid')) {
        if ($intMaxConcurrentLogins = AppConfig::get('MaxConcurrentLogins', false)) {
            $objUserLogin = clone $this;
            
            //load in order of last accessed date with the more recently used records first
            if ($objUserLogin->loadByUserId($intUserId) && $objUserLogin->count() > $intMaxConcurrentLogins) {
                $objUserLogin->seek($intMaxConcurrentLogins);
                while (list(, $objRecord) = $objUserLogin->each()) {
                    $arrDeleteIds[] = $objRecord->get(self::ID_PROPERTY);
                }
                
                if (!empty($arrDeleteIds)) {
                    $objUserLogin->deleteById($arrDeleteIds);
                }
            }
            unset($objUserLogin);
        }
    }
}