This is the user authentication system for Skule.ca.
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.
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
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 RPCcookie_path (default value /~james): The path the cookies will be applicable tocookie_timeout_minutes (default value 30): How long, in minutes, a user's session will last before timing outssl_key (default value testing/KeyGen/server.key): Location of the key file for SSL connectionsssl_cert (default value testing/KeyGen/server.crt): Location of the certificate file for SSL connectionsdb_name (default value test.db): Name of the database to connect todb_driver (default value sqlite3): Type of database to connect toAt 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.
Here is an intial concept for the architecure of the authentication system with regard to the rest of the site. Comments are very welcome.
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:
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.
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 ) : ?LogIn( username, password ) : sessionId/falseGetUser() : UserInfoTuple/falseThe project is currently being implemented in Python, using the Twisted framework.
The project is currently hosted on github, here.
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.