OO Auth lib - On the right track? class User {

    protected $DBH;
    protected $STH;

    public function __construct($DBH) {
        $this-&gt;DBH = $DBH;
    }

    public function logged_in() {
        if (isset($_SESSION['userid']) &amp;&amp; isset($_SESSION['hash'])) {
            $hash = sha1($_SESSION['userid'] . $_SERVER['HTTP_USER_AGENT']);

            if ($_SESSION['hash'] == $hash)
                return true;
        }
        return false;
    }

    public function login($username, $password) {
        $password = sha1($password);
        $this-&gt;STH = $this-&gt;DBH-&gt;prepare("SELECT id, banned, activated FROM users WHERE username = ? AND password = ?");
        $this-&gt;STH-&gt;setFetchMode(PDO::FETCH_OBJ); 
        $this-&gt;STH-&gt;execute(array($username, $password));

        if ($this-&gt;STH-&gt;rowCount() &gt; 0)
            return $this-&gt;STH-&gt;fetch();
    }

    public function create_account($username, $password, $email) {
        $password = sha1($password);
        $activation_key = md5($username . $email);

        $this-&gt;DBH-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        try {
            $this-&gt;STH = $this-&gt;DBH-&gt;prepare("INSERT INTO users(username, password, email, activation_key, created) VALUES(:username, :password, :email, :activation_key, :created)");
            $this-&gt;STH-&gt;bindParam(':username', $username);
            $this-&gt;STH-&gt;bindParam(':password', $password);
            $this-&gt;STH-&gt;bindParam(':email', $email);
            $this-&gt;STH-&gt;bindParam(':activation_key', $activation_key);
            $this-&gt;STH-&gt;bindParam(':created', time());
            $this-&gt;STH-&gt;execute();
        } catch (PDOException $e) {
            //$e-&gt;getMessage();
            return false;
        }

        return true;
    }

    public function check_username($username) {
        $this-&gt;STH = $this-&gt;DBH-&gt;prepare("SELECT username FROM users WHERE username = ?");
        $this-&gt;STH-&gt;execute(array($username));

        if ($this-&gt;STH-&gt;rowCount() &gt; 0)
            return false;

        return true;
    }

    public function check_email($email) {
        $this-&gt;STH = $this-&gt;DBH-&gt;prepare("SELECT email FROM users WHERE email = ?");
        $this-&gt;STH-&gt;execute(array($email));

        if ($this-&gt;STH-&gt;rowCount() &gt; 0)
            return false;

        return true;        
    }

    public function activate($key) {
        $this-&gt;STH = $this-&gt;DBH-&gt;prepare("SELECT activation_key FROM users WHERE activation_key = ?");
        $this-&gt;STH-&gt;execute(array($key));

        if ($this-&gt;STH-&gt;rowCount() &gt; 0) {
            $this-&gt;STH = $this-&gt;DBH-&gt;prepare("UPDATE users SET activated = 1, activation_key = null WHERE activation_key = ?"); 
            $this-&gt;STH-&gt;execute(array($key));

            return true;
        }

        return false;
    }  
}


Am I doing this right?
What should I change? Add, etc. Should I use more try { } catch blocks/etc. Give me some tips on how to improve the code. (Better performance, etc).
Regarding try/catch: this depends on whether your user class can handle errors or not.


An exception you can't handle is for example if the SQL-statement has a syntax error or the SQL-server being down. You can't do anything about it programatically during runtime except for displaying a nice error message to the user. In that case I wouldn't surround the DB-code with try/catch but implement a global try/catch which does just display a nice error message to the user and log the errors for you, the developer.
But if you have an exception because for example your username-field is UNIQUEand someone tries to insert an username which already exists you could resolve that problem automatically by just adding a number to the username and retry the insert (okay it's a dumb example, but I hope the point comes across). The pseudocode for this dumb example could be like:

$statement = $db-&gt;prepare("INSERT INTO user (username, ....) values (?, ...)");
$tryInsert = true;
$tries = 0;
while($tryInsert) {
    $insertName = $username;
    if($tries &gt; 0) {
        $insertName .= $tries;
    }
    try {
        $statement-&gt;execute(array($insertName, ...));
        $tryInsert = false; // it worked, stop trying
    } catch(PDOException $e) {
        if($e-&gt;code() == "23000") {
            // 23000 means unique constraint violation, probably 
            // duplicate username
            ++$tries;
        } else {
            // we can't handle the exception, therefore we just re-throw it
            throw $e;
        }
    }
}



I think in your code are no exceptions which you could handle. If an exception occurs it is most probably due to some issue with the SQL-server or the SQL-statement is wrong. In that case it doesn't make sense to wrap any of your code in try/catch. But you should add some kind of global exception handler which displays a nice error message (like: oops something went wrong, the developers have been informed).
In login change the last two lines to:

if (($row = $this-&gt;STH-&gt;fetch()) !== false)
    return $row;


The problem with PDOStatement::recordCount() is, that it's not portable for SELECT queries and the above code does the same thing as your code.
Then in create_account, you use PDO::setAttribute to change the PDO error mode of the supplied PDO-instance. Change that so that this is called when the PDO is initialized and not deep down in some mapper-class.
Your activate method could be rewritten to just execute one query:

public function activate($key) {
    $this-&gt;STH = $this-&gt;DBH-&gt;prepare("UPDATE users SET activated = 1, activation_key = null WHERE activation_key = ?"); 
    $this-&gt;STH-&gt;execute(array($key));

    // rowCount is set to the number of rows affected by UPDATE
    return $this-&gt;STH-&gt;rowCount() &gt; 0;
}

Optional: This could be simplified:

if (isset($_SESSION['userid']) &amp;&amp; isset($_SESSION['hash'])) {


To that:

if (isset($_SESSION['userid'], $_SESSION['hash'])) {

Optional: Consider using phpass for hashing your passwords. It's definitely more secure that just sha1 with some salt thrown in and the class is simple to use. If you're doing this you would have to rework your login method a bit  (meaning the password verification must be done in PHP when using phpass).
If you like to change your check_*-methods you could do something like that:

protected function check_record($field, $value) {
    $this-&gt;STH = $this-&gt;DBH-&gt;prepare("SELECT COUNT(*) FROM users WHERE {$field} = ?");
    $this-&gt;STH-&gt;execute(array($value));

    $count = $this-&gt;STH-&gt;fetchColumn();

    if($count !== false) {
        return (int)$count &gt; 0;
    }

    return null;
}

public function check_username($username) {
    return $this-&gt;check_record("username", $username);
}

public function check_email($username) {
    return $this-&gt;check_record("email", $username);
}