Database-Based Authentication for PHP Apps, Part 1


by Michael McLaughlin

Learn how to secure PHP-based Web applications via database-based authentication.

Published May 2007

Managing secure access to Web pages and applications is a common problem. You want to enable those you trust to access data while preventing unauthorized ones from gaining access to it. In most cases, database-based authentication is the solution.

Authentication systems contain an Access Control List (ACL) that lists your user credentials and matches them to your assigned system privileges. Credentials are typically a user name and password pair. Credentials link your users to system privileges. System privileges let your accounts access or modify data, and enable your accounts to execute subsystems or subroutines. Your accounts can be users, groups, or systems.

In this article, you will learn how to implement this concept in PHP-based Web applications. You will learn how to design and implement an authentication database model, and plan and manage all aspects of user interactions in a browser-hosted application.

In Part 2 of this article, you will explore different methods of database access and how authentication systems can leverage security policies in an Oracle Virtual Private Database (VPD), as well as with the built-in DBMS_APPLICATION_INFO package.

Architecture, Authentication, and Encryption

Architecture. When Web applications request information from the Apache HTTP server, they send information from the client to the server. Users view this process as submitting a URL and receiving a Web page in reply. A URL is only the exposed part of the URI; the URI contains HTTP headers, cookies, and a URL. The information is transmitted as an encoded name and value pair in the URI.

Cookies are small text files containing clear or encrypted text. Cookies contain the current transaction state of communication between the browser and server application. The content of cookies was once frequently attached to the URI to help maintain transaction state. A single session cookie now sends a numeric reference that enables the prior name and value cookie pairs to be stored securely on the server. The numeric reference is known as a Web session ID.

When using Web application sessions, the session ID and expiration information about the session are sent as a special session cookie. When client browsers disallow cookies, Web applications may default to URL rewriting, which redirects the session cookie from the undisclosed URI to the disclosed URL. You recognize two benefits by leaving the data on the server and storing it by a unique session ID — you minimize the size of the URL, and you secure the internal details of user interactions.

Sample Code Manifest

Program Name Language Description
AddDbUser.php PHP Program file that authenticates new users and returning active sessions; and renders a form to enter new users into an ACL.
create_identity_db1.sql SQL Script that builds the database and seeds necessary rows to initialize the Web application
Credentials1.inc PHP Include file that defines the three global constants you use in all oci_connect() function calls
SignOnDB.php PHP Program file that esets the PHP session ID before calling the AddDbUser.php program
SignOnRealm.php PHP Program file that authenticates credentials and then renders the authentication values to a static Web page

Although keeping session information on the server is more secure, you must guard against session hijacking. A malicious user can hijack a session by (a) stealing a session cookie, (b) copying a URL that includes the session ID or (c) snatching it through a man-in-the-middle attack. Modern browsers make this more difficult, and PHP does not write the physical session cookies to a file system.

A benefit of the cookie and session architecture is that you can mine historical session data, provided you store data in a normalized data model. The data let you discover how customers use your Web applications. The data also let you discover customer browsing patterns and purchasing trends. This type of information enables businesses to target marketing to individuals as opposed to groups.

Authentication. You will use one of two authentication models to verify users in your Web applications. Your choice is between the basic HTTP/HTTPS authentication, and the cookie and session authentication models. A third authentication model, a digest HTTP authentication, is under development but appears likely to remain an incomplete solution for a few more years.

Both of the first two authentication models let you take credentials from a Web page and validate them against an ACL. They also let you work successfully whether or not the browser accepts cookies. The basic HTTP/HTTPS and digest HTTP methods validate against a realm without using a cookie, while the session management writes at least a session ID cookie.

URL rewriting presents some security risks when you send the session ID as part of the URL because some users instant message the URL to someone else. That other person may hijack the authorization to access or compromise confidential data. An alternative to URL rewriting is placing the session ID into the rendered Web page as a hidden field. While this does create vulnerability, it is less risky than appending the session to the URL.

Your PHP authentication script takes a name and value pair for the user name and password. The script then compares the pair against data stored in your ACL. This is the same process in either the discussed authentication models. While it is customary to store server-side passwords in encrypted forms, you should recognize that any compromise of the clear text user and password values compromises your system security.

Therefore, you should not force users to create complex passwords because they aren't easily remembered. Users write hard-to-remember passwords on notepads or yellow sticky pads. Written passwords are security risks to your Web application because hackers might find and misuse them.

Encryption. Encryption is always a hot topic in Web application development, deployment, and management.

There are two encryption techniques in PHP Web applications. The first involves encrypting your protocol. Encrypted protocols, like HTTPS, protect you from man-in-the-middle attacks using network analyzers, commonly known as packet sniffers. The second involves encrypting your user password from exposure to prying eyes on the server.

Password encryption protects your system from attacks best when the encrypted passwords are stored separately from the source code. When the ACL is a file on the file system, both the method of encryption and encrypted values can be compromised by a single server hack. You reduce the risk of compromising the encrypted passwords by storing your ACL in a database.

Identity Management Data Modeling

Building an identity management data model can vary from the simple to the complex. The simplest model is a single table containing the ACL for your application. Unfortunately, this model only works with basic HTTP/HTTPS authentication.

The simplest way to implement cookie and session authentication requires at least two tables. One table contains the ACL, and the other contains the session data. The following depicts a basic model:



Figure 1 Basic authentication data model

A more effective solution for authentication would support capturing and mining user interactions. These vary from the list of Web pages called to individual events, like mouse clicks. Figure 2 shows a data model that can capture user navigation and both successful and unsuccessful logins. For our example, this data model is created by the create_identity_db.sql script (see sample code zip).



Figure 2 Detailed authentication data model

The larger model lets you track versions of defined modules against runtime calls to modules. Actual parameters map to catalog items, like books on Amazon.com. An access log is added to track multiple connections made during a single session. The full details of how you implement your model can vary based on your goals. Figure 2 also shows an example model that supports version control in a catalog ordering application. At present, the sample code only captures invalid log in attempts and the ACCESS_LOG table is renamed to reflect that difference as INVALID_SESSION table.

Authentication Process Models

Basic HTTP/HTTPS authentication operates by establishing a browser's credentials in a realm. Browsers can concurrently support many realms. Realms are set in the header of XHTML documents. They can be manually coded in the form, or set in the AuthName directive of the httpd.conf file. Realms act as guards over protected server areas, and they can apply to one or more protected server areas. One advantage of basic HTTP/HTTPS authentication is that you don't have to code a login or logout facility. Browsers provide the login forms and you logout by closing all open browser windows.

A major downfall of the basic HTTP/HTTPS authentication method is you must close all browser windows to end active realm authorizations. This means that a user can authenticate, walk away from the terminal while leaving an open browser, and enable someone to compromise their confidential data by hijacking their identity.

Cookie and session authentication operates by checking user credentials against your ACL. When the ACL is stored in a file, this means reading the file and comparing the name value pairs of a user name and password against a submitted clear text user name and encrypted password.

When the ACL is stored in a database, this means one thing on initial login and another on subsequent connections. On initial login, you connect to the database, read the user name and encrypted password values from a database table; and compare the results against the submitted clear text user name and encrypted password. On subsequent Web requests, you connect to the database, read the session ID, and compare results against the submitted session ID. Unlike basic HTTP/HTTPS authentication, you can successfully logout while leaving the Web browser open.

While the session ID becomes a potential security risk because of session hijacking, some reasonable precautions can minimize this risk. The biggest security gap can be closed by implementing your login page as a PHP script instead of a simple text XHTML form. The sample cookie and session code demonstrates this approach. It ensures that a second user on a shared machine doesn't gain entry from the prior credentials. The implementation details are found in the following Cookie and Session Authentication Model section.

Basic HTTP/HTTPS Authentication Model. The basic HTTP/HTTPS authentication model authorizes once for a realm and revokes the realm authentication only when all browser windows are closed. The activity diagram representing the model from within the browser context is shown in Figure 3. Microsoft Internet Explorer provides three attempts before failing authentication but you can simply refresh the page to get another three successive attempts. The Firefox browser differs by prompting until the user cancels the authentication process.



Figure 3 Basic HTTP/HTTPS authentication activity diagram

You start a Web application session by entering a URL for the login form, which then posts to the server and sends a message back to the browser requesting that it collect and send for validation the user's credentials. When you submit the credentials to the SignOnDB.php program, the code attempts to validate your log credentials. When it works you can then use any Web pages in the realm. Authentication failure re-prompts with the browser login form as noted previously.

When implementing basic HTTP/HTTPS authentication, PHP uses three predefined variable names in the $_SERVER array. Basic HTTP/HTTPS authentication puts the user ID and the password in the $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] respectively. When the third, HTTP_AUTHORIZATION, is not set, you submit these two $_SERVER values to your authentication function. After your server-side program authenticates the user, subsequent reads of the $_SERVER array HTTP_AUTHORIZATION name let the browser access to the realm until it is closed.

An easy way to protect your pages in the basic HTTP/HTTPS authentication model is to include a Boolean control variable that is false until authentication is verified. You display content only when the Boolean control variable becomes true. Your code should check for authentication, and prompt for credentials when you are not authenticated in the realm. This code should be found in all pages on your secure Web site.

You trigger HTTP header validation in your server-side script by checking the values of these predefined $_SERVER variables. The request to the SignOnRealm.php page places these values in the returned header, which instructs the browser to prompt with a credential entry dialog box. Clicking the credentials OK button sends a new request to the server-side script. The server-side script then attempts to validate the submitted credentials unless a user cancels the process. These realm sign in scripts typically return a failure message when the user cancels the process. Some sites also shut user accounts when there are three or four consecutive authentication failures because someone may be attempting to hack into the system through a known user name.

Setup Test Application. The demonstration application uses an Oracle schema named IDMGMT1, and the password is the same as the schema. You can create the user and environment by following these steps:

1. Log into the database as the privileged SYSTEM user and run the following commands:

SQL> CREATE USER IDMGMT1 IDENTIFIED BY IDMGMT1;
SQL> GRANT CONNECT, RESOURCE TO IDMGMT1;

2. Connect to the new user schema:

SQL> CONNECT IDMGMT1/IDMGMT1@XE

3. Run the create_identity_db1.sql script in the IDMGMT1 schema to create all necessary objects and seed the SYSTEM_USER table:

SQL> @create_identity_db1.sql

4. Place the following files in your htdocs directory or a subdirectory of the htdocs directory:

 • SignOnRealm.php
 • SignOnDB.php
 • AddDbUser.php

These steps complete the setup required for our purposes here. You can now experiment with either the realm or session identification management examples.

Experiment with Basic HTTP/HTTPS Authentication. You can experiment with the realm identity management by following these steps:

1. Open the realm identification management examples by using the following URL:

http://localhost/SignOnRealm.php 

2. You can use either of the following seeded accounts as valid credentials using Basic HTTP/HTTPS authentication. Make sure you don't have the cap lock enabled because they are case sensitive.

User Name Password
administrator welcome
guest guest



Figure 4 Basic HTTP/HTTPS authentication dialog box

After you successfully enter your credentials you will be presented with the following page that echoes back the plain text credentials and encrypted password:



Figure 5 Basic HTTP/HTTPS credential validation acknowledgment

Analyze the Basic HTTP/HTTPS Authentication Code. After you enter your user name and password, your script will call the verify_db_login() function. The following PHP code demonstrates how you begin the authentication of users in your realm:

// Declare control variable.
$valid_user = false;

// Authenticate user.
if ((isset($_SERVER['PHP_AUTH_USER'])) && (isset($_SERVER['PHP_AUTH_PW'])))
  if (verify_db_login($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']))
    $valid_user = true;

This function opens a connection to the Oracle database and checks whether the credentials are valid. The verify_db_login() function returns true when credentials are valid and false when not. When the SYSTEM_USER table is missing, the code tells you to check for the missing table. The main code for the Web page is shown in the function:

// Check for authorized account.
function verify_db_login($userid,$passwd)
{
  // Attempt connection.
  if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
  {
    // Return a row.
    $s = oci_parse($c,"SELECT   NULL
                       FROM     system_user
                       WHERE    system_user_name = :userid
                       AND      system_user_password = :passwd
                       AND      SYSDATE BETWEEN start_date
                                        AND NVL(end_date,SYSDATE)");

    // Encrypt password.
    $newpassword = sha1($passwd);
    // Bind variables as strings.
    oci_bind_by_name($s,":userid",$userid);
    oci_bind_by_name($s,":passwd", $newpassword));

      // Execute the query.
      if (@oci_execute($s,OCI_DEFAULT))
      {
        // Check for a validated user, also known as a fetched row.
        if (oci_fetch($s))
           return true;
        else
          return false;
      }
      else
      {
        // Print error when execution fails.
        $errorMessage = "Check for a missing SYSTEM_USER table.<br />";
        print $errorMessage;
      }

    // Close connection.
    oci_close($c);
  }
  else
  {
    $errorMessage = oci_error();
    print htmlentities($errorMessage['message'])."<br />";
  }
}

There are two things to note about the verify_db_function() function. First, the query returns a null value, which eliminates processing any unnecessary return values. Second, the clear text password is encrypted using the sha1() function and assigned to a local variable.

You now know how to build the required components for basic HTTP/HTTPS authentication. The two caveats associated with this authentication model are that you: (1) have complete access to the realm once you authenticate your credentials in a browser; and (2) can only log out by closing all browser windows.

Cookie and Session Model. The cookie and session model is popular because you can let users login and logout of Web applications without requiring them to shutdown all of their browser windows. This authentication model is also more complex than the basic HTTP/HTTPS model. There are different ways that you can implement cookie and session authentications.

The login and logout operations operate inside active browsers. This operating context adds more complexity to the cookie and session model. For example, the browser backward, forward, and refresh buttons require special handling in your code. A refresh button shouldn't attempt to send a new user authentication more than once. Likewise, a refresh button should not trigger new authentication requests after a user has logged out of an application. Unfortunately, they do unless the program prevents it.

Calling a login PHP script, not an XHTML Web page is the most direct way to guarantee a complete logout operation in your Web application. A PHP script can reset the session value whereas an XHTML Web page cannot because it passes the prior session value when submitting missing or new credentials to a target page.

There are several predefined variables in PHP that you can use to store globally scoped variables. One is the $_SESSION array. It lets you add any name-value pair, where the name becomes the index value to the actual data value. The advantages of using the $_SESSION array are simplicity and flexibility but the disadvantages can involve conflicts with other libraries.

The session_start() function returns the active PHP session ID value. You must call the session_start() function before the session_regenerate_id() function because it actually calls the session_destroy() function. The session_destroy() depends on the existence of a PHP session ID and will raise an error when one doesn't exist before you call it. Your call to the session_regenerate_id() function with an actual true parameter will reset the PHP session ID. You use runtime error suppression to guarantee that calling the session_regenerate_id() function doesn't raise an error when no prior session exists in the context of the browser.

<?php
  // Start and regenerate session.
  session_start();
  @session_regenerate_id(true);
  $_SESSION['sessionid'] = session_id();
?>

The preceding scriptlet alters the session value in the browser, and it will force new authentication with the next click of the Login button. It does this by replacing the previously authenticated PHP session ID with a new session ID. The new PHP session ID is transmitted as part of the URL to the next form.

There are two ways to manage database connections once you establish your PHP session: One uses a persistent connection between the PHP module and the database and the other uses a non-persistent connection between the two. Persistent connections let you start a transaction scope that can span several Web requests, and they end only when you issue a database commit command. During the scope of a persistent connection, you can run SQL and PL/SQL statements that lock rows pending change or after an uncommitted change. Non-persistent connections let you run SQL or PL/SQL statements to query or change data between opening and closing a connection, which occurs in a single Web request. Data transactions limited by a single connection are also known as autonomous transactions because their duration is limited to the duration of the connection.

These sample programs use non-persistent database connections to query and transact against the Oracle database. Consequently, all database transactions perform as autonomous transactions.

The SignOnDB.php Web page, shown in Figure 5, implements this type of PHP scriptlet before the XHTML page. The XHTML rendering found in the SignOnDB.php form is also found in the AddDbUser.php page as the signOnForm() function.

You will put the PHP authentication logic in all Web pages called after a user logs into the application. At present all programming logic is in the AddDbUser.php page, shown in Figure 6, but you can move the twelve functions into one authentication library.

Your browser must accept cookies for these programs to work. When cookies are disabled, all you can do is login, logout, and fail to enter a new user because the session values are lost between pages.

Experiment with Session Authentication. You can experiment with the session identity management by following these steps:

1. Open the session identification management examples by using the following URL, assuming that you placed the code in the htdocs directory:

http://localhost/SignOnDB.php 

2. You can use either of the seeded accounts presented in the Basic HTTP/HTTPS authentication:



Figure 6 Cookie and Session Login page

3. You can now enter a new user into your credential repository in the New User form, which requires user name to be a string that is 6 to 10 characters long and starts with a letter:



Figure 7 Cookie and session Add New User page

4. After you add the new user, the AddDBUser.php script will present you with a new copy of the New User form. It will acknowledge whether you successfully added a new user or explain what rule the entry violated. You can connect to the IDMGMT1 schema and run the following query to inspect a new user entry:

SELECT system_user_name, system_user_password FROM system_user; 

Analyze the Session Authentication Code

The PHP program's logic accounts for the typical nuances of the forward, backward and refresh browser buttons. Figure 7 displays the rendered Web page. When you call or refresh the form by using the browser navigation buttons, the program determines whether (a) the session is registered in the database model to the current user, (b) the remote address is the same client IP address from authentication, and (c) the session is current. A session is current when the last related database activity of the session occurred within the last five minutes.

Both the initial sign on form and add user forms check and verify a registered session before the program logic looks for new user credentials and attempts to authenticate the user. When the credentials are authenticated, the SignOnDB.php script registers the new session and calls the AddDbUser.php script. A refresh of the AddDbUser.php script calls the signOnForm() function that mimics the SignOn.php form's behavior. When the AddDbUser.php script authenticates credentials, it renders a page using the addUserForm() and when either script repudiates credentials, they log a failed login attempt and render a new sign on page.



Figure 8 Cookie and session authentication activity diagram

The Web page contains two XHTML forms. One form lets you enter a new user, validate that the credentials meet guidelines (e.g., 6 to 10 character strings starting with a letter), and inserts the credentials into the database. The other form lets you opt to log out of the application by calling the SignOnDB.php script, which resets the session to prevent it appearing authenticated in the database after a user logged out.

The Web page starts the session and assigns it to the $_SESSION array, assigns any submitted credentials to local variables, and then checks to see whether a current authenticated session is available. This is done by the following code:

  // Check for valid session and regenerate when session is invalid.
  if ((get_session($_SESSION['sessionid'],$userid,$passwd) == 0) ||
      (($_SESSION['userid'] != $userid) && ($userid)))
  {
    // Regenerate session ID.
    session_regenerate_id(true);
    $_SESSION['sessionid'] = session_id();
  }
  else
  {
    $authenticated = true;
  }

The get_session() function opens a connection to the database and queries a result set from the join of the SYSTEM_USER and SYSTEM_SESSION tables. The results from the query determine whether or not the session is authenticated. Part of this verification process checks to make sure that the request originates from the same IP address. When authenticated, Web page variables are assigned values from the query to process the page request. At the same time, the LAST_UPDATE_DATE column timestamp is updated by the update_session() function. The attempt is logged by the record_session() function to the OBSOLETE_SESSION table in the database when authentication fails.

// Get a valid session.
  function get_session($sessionid,$userid = null,$passwd = null)
  {
    // Attempt connection and evaluate password.
    if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
    {
      // Assign metadata to local variable.
      $remote_address = $_SERVER['REMOTE_ADDR'];

      // Return database UID within 5 minutes of session registration.
      // The Oracle DATE data type is a timestamp where .003472222 is
      // equal to 5 minutes.
      $s = oci_parse($c,"SELECT   su.system_user_name
                         ,        ss.system_remote_address
                         ,        ss.system_session_id
                         FROM     system_user su JOIN system_session ss
                         ON       su.system_user_id = ss.system_user_id
                         WHERE    ss.system_session_number = :sessionid
                         AND     (SYSDATE - ss.last_update_date) <=
                                    .003472222");

      // Bind the variables as strings.
      oci_bind_by_name($s,":sessionid",$sessionid);

      // Execute the query, error handling should be added.
      if (@oci_execute($s,OCI_DEFAULT))
      {
        // Check for a validated user, also known as a fetched row.
        if (oci_fetch($s))
        {
           // Assign unqualified values.
          $_SESSION['userid'] = oci_result($s,'SYSTEM_USER_NAME');

          // Check for same remote address.
          if ($remote_address == oci_result($s,'SYSTEM_REMOTE_ADDRESS'))
          {
            // Refresh last update timestamp of session.
            update_session($c,$sessionid,$remote_address);
            return (int) oci_result($s,'SYSTEM_SESSION_ID');           }
           else
           {
             // Log attempted entry.
             record_session($c,$sessionid);
             return 0;
           }
        }
        else
        {
          // Record when not first login.
          if (!isset($userid) && !isset($passwd))
            record_session($c,$sessionid);

          // Return a zero.
          return 0;
        }
      else
      {
        // Print error when oci_execute() fails.
        $errorMessage = "Check for a missing SYSTEM_USER or ";
        $errorMessage .= "SYSTEM_SESSION tables.<br />";
        print $errorMessage;
        return 0;
      }

      // Close the connection.
      oci_close($c);
    }
    else
    {
      $errorMessage = oci_error();
      print htmlentities($errorMessage['message'])."<br />";
      return 0;
    }
  }

You should note that the functions called from the get_session() function share the database connection opened to confirm an existing session. This goal is accomplished by passing the connection as an actual parameter to the other two functions. Managing the database transaction scope in a single database connection lets you manage multiple SQL and PL/SQL statements as components of a transaction. This enables functions to contain single subjects of your data abstraction layer and lets you treat nested function calls as subroutines.



Figure 9 Cookie and session Add New User Bad Credential page

The next section of primary code in the Web page renders different versions of the form because it serves multiple purposes. Figure 6 shows the form rendered on initial login to the application. Figure 9 demonstrates the form rendered after attempting to enter a null user name. The code calls the create_new_db_user() function when appropriate to add new users to the ACL in the SYSTEM_USER table, as shown:

  // Check whether the program should:
  // -----------------------------------------------------------------
  //  Action #1: Verify new credentials and start a database session.
  //  Action #2: Continue a session on refresh button.
  //  Action #3: Provide a new form after adding a user.
  //  Action #4: Provide a new form after failing to add a user.
  // -----------------------------------------------------------------
  if (($authenticated) || (authenticate($userid,$passwd)))
  {
    // Assign inputs to variables.
    $newuserid = @$_POST['newuserid'];
    $newpasswd = @$_POST['newpasswd'];

    // Set message and write new credentials.
    if ((isset($newuserid)) && (isset($newpasswd)) &&
        (($code = verify_credentials($newuserid,$newpasswd)) !== 0))
    {
      // Render empty form with error message from prior attempt.
      addUserForm(array("code"=>$code
                       ,"form"=>"AddDbUser.php"
                       ,"userid"=>$newuserid));
    }
    else
    {
      // Create new user only when authenticated.
      if (!(isset($userid)) && (isset($_SESSION['userid'])))
       create_new_db_user($_SESSION['db_userid'],$newuserid,$newpasswd);

      // Render fresh empty form.
      addUserForm(array("form"=>"AddDbUser.php"));
    }
  }
  else
  {
    // Destroy the session and force re-authentication.
    session_destroy();

    // Redirect to the login form.
    signOnForm();
  }

The nested if statement filters calls to the create_new_db_user() function by ensuring that $newuserid and $newpasswd variables are set. These two variables can only be set in the AddDbUser.php Web page, which means these cannot be called until after your user has authenticated. This works on a subsequent call to the AddDbUser.php Web page because clicking the Add User button recursively calls the same Web page.

  // Add a new user to the authorized control list.
  function create_new_db_user($userid,$newuserid,$newpasswd)
  {
    // Attempt connection and evaluate password.
    if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
    {
      // Check for prior insert, possible on Web page refresh.
      if (!is_inserted($c,$newuserid))
      {
        // Encrypt password.
        $newpassword = sha1($passwd);
        
        // Return database UID.
        $s = oci_parse($c,"INSERT INTO system_user
                           ( system_user_id
                           , system_user_name
                           , system_user_password
                           , system_user_group_id
                           , system_user_type
                           , created_by
                           , creation_date
                           , last_updated_by
                           , last_update_date )
                           VALUES
                           ( system_user_s1.nextval
                           , :newuserid
                           , :newpasswd
                           , 1
                           , 1
                           , :userid1
                           , SYSDATE
                           , :userid2
                           , SYSDATE)");

        // Bind the variables as strings.
        oci_bind_by_name($s,":newuserid",$newuserid);
        oci_bind_by_name($s,":newpasswd", $newpassword);
        oci_bind_by_name($s,":userid1",$userid);
        oci_bind_by_name($s,":userid2",$userid);

        // Execute the query, error handling should be added.
        if (!@oci_execute($s,OCI_COMMIT_ON_SUCCESS))
        {
          // Print error when oci_execute() fails.
          $errorMessage = "Check for a missing SYSTEM_USER table.<br />";
          print $errorMessage;
        }
      }

      // Close the connection.
      oci_close($c);

    }
    else
    {
      $errorMessage = oci_error();
      print htmlentities($errorMessage['message'])."<br />";
    }
  }

Inside of the create_new_db_user() function, the function makes a call to the is_inserted() function. This checks to make sure that the user does not already exist before attempting to insert one into the SYSTEM_USER table. Like the earlier nested function examples, the is_inserted() function shares the local connection for transaction control purposes. Also, the sha1() function converts the clear text password to an encrypted string before binding the user password to the data manipulation variable.

After successfully inserting the new user, the New User form renders itself again waiting for you to insert another user or to click the Log Out button. The Log Out button returns you to the login screen, which resets your session identifier.

Conclusion

Now that you have learned how identity management works and how you can implement a basic identity management solution, you should be comfortable with the terminology, architecture, and approach to authenticating your users.

You can now manage user authentication and access equally, but all users are not equal. Some may have unrestricted access, while most others have restricted access privileges. Part 2 of this article shows you how to link identity management with two technologies that enable fine-grained access control.

Although the VPD feature is the "state of the art," the older technology, DBMS_APPLICATION_INFO, also works in Oracle8i, Oracle9 i, and Oracle Database 10g Release 1. It is also the core utility supporting Oracle E-Business 11i Suite authentication. Both these technologies let you implement fine grained access privileges and roles.

Go on to Part 2


Michael McLaughlin is the author of Oracle Database 10g Express Edition PHP Web Programming, and co-author of Oracle Database 10g PL/SQL Programming and Expert Oracle PL/SQL by Oracle Press, and a professor of Computer Information Technology at Brigham Young University — Idaho.