SkuleUsers

Description

This is the user authentication system for Skule.ca.


Current Status

As it stands now, the sample code on GitHub does authentication by means of a cookie, which allows another application to do user-based authentication. Currently in progress is functionality to add users and perform more intricate queries.

XMLRPC Methods Currently Exposed

validateUser(username,hsh_pw) : Validate the given username/password
     Arguments:
     - username: Username to validate
     - hsh_pw: Hashed password for the user
     Returns:
     - (userid, password) if the password is correct, false otherwise
userExists(username) : Check if a user with the given name exists in the database.
     Arguments:
     - username: Name to check
     Returns:
     - User ID if the user exists, false otherwise
createUser(username, passwd, fname, lname) : Adds a user named `username` to the database, with the hashed password `passwd`,
                                             first name `fname`, last name `lname`
      Arguments:
      - `username`: username to add
      - `passwd`: Hash of password for new user
      - `fname`: First name
      - `lname`: Last name
      Returns:
      - True if the insert was successful, false otherwise
 checkUserSession(userid, sid) : Checks that the session id of the user with id `userid` is equivalent to the 
                                               supplied session id
      Arguments:
      - `userid`: User id to check 
      - `sid`: Session id to verify
      Returns: 
      - True if matches, false otherwise

Configuration

Recently added to the git repo is support for configuration of the authentication server via an external configuration file, located in the same directory as the server. The following options are currently available:

  • rpc_listen_port (default value 8082): The port the server will listen on for RPC
  • cookie_path (default value /~james): The path the cookies will be applicable to
  • cookie_timeout_minutes (default value 30): How long, in minutes, a user's session will last before timing out
  • ssl_key (default value testing/KeyGen/server.key): Location of the key file for SSL connections
  • ssl_cert (default value testing/KeyGen/server.crt): Location of the certificate file for SSL connections
  • db_name (default value test.db): Name of the database to connect to
  • db_driver (default value sqlite3): Type of database to connect to

At this point, it would be desirable for the other teams to try using this code in their systems and provide feedback as to what works, what doesn't, what they'd like added to the API, et cetera.


Overall Architure

Here is an intial concept for the architecure of the authentication system with regard to the rest of the site. Comments are very welcome. Initial Architecture Sketch

Some explanation may be in order: The “Barbarian Hordes” cloud is the wild, unbridled savagry of the Internet. Protecting our beloved Skule.ca (the from interlopers is our frontline server (which is here assumed to be Apache). For the server, the components falling within the blue box are those facing the WAN. This encompasses most of the site, such as SkuleMap,SkuleForum, SkuleCourses, etc. These systems will require the user to authenticate before allowing them to perform certain actions.

To authenticate users, control makes its way, either through a login page or via some sort of RPC mechanism, to the red part of the server, which is kept isolated from outside access. These two main uses of the authentication server are envisioned working in the following ways:

Login page

The login page, which can be reached from any other location on Skule.ca when authentication is required, simply reads in a username and password and sends both to the backend authenticating server (registering is conceptually the same). The authentication server compares a salted hash of the given password with the stored hash for the associated username in its database. If it does not match, the login page will indicate as such and (after a delay which increases after each failure) allows the user to try again. If successful, a session id is generated and returned to the original section of Skule.ca that requested authentication, at which point it will be the responsibilty of said page to use this information in a proper manner (i.e. giving the user a cookie, or merely confirming a valid user id and user its own database of users). Since the architecture of the site as a whole appears to be extremely heterogeneous, it behooves us to authenticate in a manner that is as platform- and language-agnostic as possible.

RPC to the Authentication Server

In order to facilitate other sections of the site to meaningfully use the authentication method presented here, they must have a way to acquire information about the user currently logged in, if any. In keeping with the wide variety of technologies used in the site, a generic method such as XML/RPC provides a generic way for other systems to get information from the authentication server. Some basic methods to be provided should include:

  • isLoggedIn( username, sessionID ) : ?
    • This method would, given a username and session identifier, respond with a boolean indicating if said user is authenticated into said session.
  • LogIn( username, password ) : sessionId/false
    • Allow logins without using the login page, to facilitate other methods of authentication. Its workings should be obvious given the above discussion of the login page.
  • GetUser() : UserInfoTuple/false
    • If the user is logged in, return a tuple/object representing the current user, else nil.

Implementation

The project is currently being implemented in Python, using the Twisted framework.

The project is currently hosted on github, here.

Usage

Following is a simple example for how one would use the authentication backend. The example is in PHP for simplicity (please note that I've never really used PHP before, so this is almost certainly written in very poor style).

Page 1:

<?php
session_start();
unset($_SESSION['rand_val']);
$_SESSION['rand_val'] = rand();
?>
<html>
<head><title>Test 1</title></head>
<body>
<p>Test One:
<?php if (isset($_COOKIE['username'])) { ?>
   <p>Hello, <? echo $_COOKIE['username'] ?>!</p>
   <p><form action="logout.php" method="POST"><input type="submit" value="Logout"/></form></p>
<?php    } else { ?>
   <p>Please <a href="./login.html">Login</a></p>
     <?php } ?>
 
<form action="test2.php" method="POST">
   <label title="A string" for="foo">String:
      <input type="text" name="foo" />
   </label>
   <input type="hidden" name="bar" value="<?php echo hash('sha256', $_COOKIE['sid'] . $_SESSION['rand_val'])?>"/>
   <input type="hidden" name="phpses" value="<?php echo session_id(); ?>">
   <input type="submit" value="done" />
</form>
</p>
</body>
</html>

Page 2:

<?php
require_once 'XML/RPC.php';
session_id($_POST['phpses']);
session_start();
?>
<html>
<head><title>Test 2</title></head>
<body>
<?php if ( isset($_COOKIE['sid']) && $_POST['bar'] == hash('sha256', $_COOKIE['sid'] . $_SESSION['rand_val'])) { 
    $client = new XML_RPC_Client('/auth', "https://localhost", 8082);
    $msg = new XML_RPC_Message('checkUserSession', array(new XML_RPC_Value($_COOKIE['uid'], "int"), 
                                                         new XML_RPC_Value($_COOKIE['sid'], "string")));
    $resp = $client->send($msg);
    if ( $resp->value()->scalarval() ) { ?>
<p>You entered <? echo $_POST['foo'] ?></p>
<?php } else { ?>
<p>Authentication error!</p>
<p><a href="./test1.php">Try again?</a></p>
<?php } } else { ?>
<p>Authentication error!</p>
<p><a href="./test1.php">Try again?</a></p>
<?php } ?> ?>
</body>
</html>
<?php
unset($_SESSION['rand_val']);
session_destroy();
?>

Ignoring issues of coding style, it is hopefully understandable how this is supposed to work: When the user logs in, they are given a cookie containing a sessionid value (which is simply a randomly-generated string long enough to avoid possible collisions (see the Birthday Paradox)). This value is then hashed (using a crytographically secure hash function) with a random number and set as the value of a hidden form parameter. The recieving page then verifies that the same value is arrived at by performing the same operation. This approach is in order to prevent CSRF attacks, by ensuring that there is also some hidden information that only the server and the authenticated client possess.

If you have any questions, please post them below.

Discussion

  • Great work James. Question: would it be possible for projects hosted elsewhere (outside the WAN) to access the user service? –Raf
    • Hm, I guess that would be desirable, especially if we want to start using things like OpenID or Facebook Connect…I suppose we could also have another RPC interface exposed to the public. –James
  • Other basic methods that may be useful:
    • get User (if someone is logged in, receive a user object with some public attributes, like User.ID, name, nickname, website, etc.) –Raf
  • We'll probably need to purchase an SSL cert, to facilitate logging in via HTTPS – James
    • Sure. I have no experience with this. Can you recommend a solution? –Raf
    • I've actually never done this either. GoDaddy looks cheap, VeriSign seems more legit, albiet 10x the price. I'll try to investigate further. We could also use self-signed certs, since we only want them for encryption, not authentication, but FF3 tends to scream to users about that… –James
 
projects/skuleusers.txt · Last modified: 2009/09/13 13:54 by jamesnvc
 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki