Client Authentication

Quite often, a Web resource may require the HTTP client to authenticate itself before a service can be granted. The HTTP authentication scheme is based on a couple of message headers, namely WWW-Authenticate and Authorization.

Server-based HTTP authentication

Many Web servers, like Apache, provide built-in support for HTTP authentication, of either Basic- or Digest-type. When server-based HTTP authentication is in effect, the authorization phase is negotiated directly between the client and the server, with no involvement of CSA. Once the client has authenticated succesfully, the requested resource (CSA program) is called, and the authentication token is passed to it by the HTTP server in the environment variable $REMOTE_USER. If this variable is set and contain acceptable characters, CSA will do the following:

  1. Set variable CSA_AUTH_USER=$REMOTE_USER
  2. Set variable CSA_AUTH_OK=1

It will then be up to application-level routines to decide what to do if those variables are set. The normal behaviour will be that if CSA_AUTH_OK is set, access to the requested resource is granted with no further checks.

For $REMOTE_USER to be considered valid it MUST match the regular expression ^[-_a-zA-Z0-9@.]+$. So for instance, goofy, Minnie123, 12345, mikey@example.com and donald-123 are ok, while a/456 is not.

CSA-managed HTTP authentication

Beside relying directly on the Web server for the authentication phase, CSA is also able to perform basic HTTP authentication itself. Unlike what normally happens with server-based authentication, however, the authorization token can be sent by a client to the server not only in the relevant HTTP Authorization header, but also through an HTTP cookie, an ordinary GET/POST variable or a PATH_INFO element with the same name (case-insensitive).

This application-based authentication scheme cannot be applied to static objects, like images and HTML pages that are served directly by the HTTP server without the CSA intervention. Being CSA an application system, however, I assume that no private objects will be made accessible to the HTTP server directly, and that everything that needs to be protected will be accessible only through the application layer. That is, no protected "physical resources" will be exposed as such, but only their "logical representations", consistently with the REST architectural style. See also URL Rewriting for core REST features of CSA.

Note: in the CSA context, I consider the REST architectural style as a special case of the more generic term "Remote Procedure Call" (RPC).

Sample authentication code

A typical authenticated CSA program will begin as follows:

 # Verify client authentication. 
 authCheck 
 
 # ... other stuff follows. 
 
Providing the authcgi.rc function library and the relevant authCheck function is entirely up to the CSA application programmer. CSA considers authentication an application-level issue. Users can be authenticated against user/password pairs stored in flat-files, SQL databases, LDAP servers, and so on. This is why CSA itself does not provide built-in authentication facilities, but simply a way to set the authentication variables $CSA_AUTH_xxx, and in particular $CSA_AUTH_USER and $CSA_AUTH_PW, that will be the strings to look up in whatever authentication system is being used.

The automatic CSA function locader csaLoadLib looks for the application-specific library $CSA_ROOT/lib/authcgi.rc first, and then in the CSA installation directory. In this way the CSA programmer can not only provide her own function files, but also override any of the CSA-supplied ones, by placing the relevant custom versions in the application-level directory.

In fact, CSA does provide a few basic built-in facilities to authenticate Web users against a simple flat-file database, but you do not necessarily need/want to use them. I will however explain how they work, just in case you want to resort to a similar mechanism rather than providing a completely different one of your own. See also Sample Authenticated Session.

Here is an example of authcgi.rc, taken from the sample application provided with CSA. The example contains a number of comments, and it should be rather self-explanatory:

  # ===================================================================== 
  # authCheck [-u|--unlock] [-q|--quiet] [-r|--return] 
  # 
  # Check that the client provided the correct authorization credentials. 
  # If the authorization verifies correctly, the default is to retain the 
  # auth lock on return, unless option '-u' is specified. The default 
  # behaviour ensures that any actions performed by the user will not 
  # cause inconsistencies in the data, that may otherwise occur if the 
  # user table record is removed/modified while the calling program 
  # is still running. If '-q' is specified, the printing of message 
  # 0013 is suppressed. Sets CSA_AUTH_OK on return. It is up to the CSA 
  # AWK front-end to ensure that all authentication variables are set 
  # to proper values and contain only allowed characters. If '-r' is 
  # specified, the function returns to the caller in case of errors, 
  # while the normal behaviour would be to exit with an error message. 
  # ===================================================================== 
 
  fn authCheck { 
 
     ~ $CSA_DEBUG 0 || csaDebug $0 $* 
 
     # Handle server-managed auth. If server-based authentication is 
     # in effect, CSA_AUTH_OK will have already been set by either 
     # urlencoded.awk or multipart.awk, and access should be granted with 
     # no further checks. 
 
     ~ $CSA_AUTH_OK 1 && return 0 
 
     unlock=0 quiet=() record=() ret=() { 
 
        while (!~ $1 ()) { 
          switch ($1) { 
            case -u --unlock 
                 unlock = 1 
            case -q --quiet 
                 quiet = 1 
            case -r --return 
                 ret = 1 
          } 
          shift 
        } 
 
        # This variable may not be null, the password can. 
 
        if (~ $CSA_AUTH_USER ()) { 
           ~ $quiet () || csaPrintMsg 0013 
 
           ~ $ret () || return 1 
 
           # Authorization failed. We need to prompt the client 
           # for authentication credentials, using a method that 
           # supposedly suits the client. 
 
           ~ $CSA_AUTH_TYPE custom || csaExit.needauth 
           csaExit.location $CSA_RPC_URL2'?0='$CSA_ID'.showpage&page=ask-pass' 
        } 
 
        # Lock the user table. 
        csaLock $CSA_ROOT/var/user.d/allusers || csaExit.fault 
 
        # Lookup the target user record in the flat-file user table. 
        record = ``($tab$nl){ 
          grep -e '^'$CSA_AUTH_USER$tab $CSA_ROOT/var/user.d/allusers 
        } 
 
        # Handle invalid user. 
 
        ~ $bqstatus 0 || { 
          ~ $ret () || return 1 
          ~ $CSA_AUTH_TYPE custom || csaExit.needauth 
          csaExit.location $CSA_RPC_URL2'?0='$CSA_ID'.showpage&page=ask-pass' 
        } 
 
        # Handle invalid password. 
 
        if (!~ $record(2) $CSA_AUTH_PW) { 
           ~ $ret () || return 1 
           ~ $CSA_AUTH_TYPE custom || csaExit.needauth 
           csaExit.location $CSA_RPC_URL2'?0='$CSA_ID'.showpage&page=ask-pass' 
        } 
 
        # Everything ok, session granted. 
        CSA_AUTH_OK = 1 
 
        # Unlock current auth if requested. 
        if (~ $unlock 1) { 
           csaUnlock $CSA_ROOT/var/user.d/allusers || csaExit.fault 
        } 
     } 
 
     return 0 
  } 
 
  # ===================================================================== 
  # End of authcgi.rc 
  # ===================================================================== 
 

Trackbacks (1) | New trackback | Print