<NME> simplesamlphp-customauth.txt
<BEF> ADDFILE
<MSG> Add document describing how to implement username/password authentication.

git-svn-id: a243b28a2a52d65555a829a95c8c333076d39ae1@2872 44740490-163a-0410-bde0-09ae8108e29a

<DFF> @@ -0,0 +1,350 @@
+Implementing custom username/password authentication
+====================================================
+
+This is a step-by-step guide for creating a custom username/password [authentication source](./simplesamlphp-authsource) for simpleSAMLphp.
+An authentication source is responsible for authenticating the user, typically by getting a username and password, and looking it up in some sort of database.
+
+<!-- {{TOC}} -->
+
+Create a custom module
+----------------------
+
+All custom code for simpleSAMLphp should be contained in a [module](./simplesamlphp-modules).
+This ensures that you can upgrade your simpleSAMLphp installation without overwriting your own code.
+In this example, we will call the module `mymodule`.
+It will be located under `modules/mymodule`.
+
+First we need to create the module directory:
+
+    cd modules
+    mkdir mymodule
+
+Since this is a custom module, it should always be enabled.
+Therefore we create a `default-enable` file in the module.
+We do that by copying the `default-enable` file from the `core` module.
+
+    cd mymodule
+    cp ../core/default-enable .
+
+Now that we have our own module, we can move on to creating an authentication source.
+
+
+Creating a basic authentication source
+--------------------------------------
+
+Authentication sources are implemented using PHP classes.
+We are going to create an authentication source named `mymodule:MyAuth`.
+It will be implemented in the file `modules/mymodule/lib/Auth/Source/MyAuth.php`.
+
+To begin with, we will create a very simple authentication source, where the username and password is hardcoded into the source code.
+Create the file `modules/mymodule/lib/Auth/Source/MyAuth.php` with the following contents:
+
+    <?php
+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {
+        protected function login($username, $password) {
+            if ($username !== 'theusername' || $password !== 'thepassword') {
+                throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+            }
+            return array(
+                'uid' => array('theusername'),
+                'displayName' => array('Some Random User'),
+                'eduPersonAffiliation' => array('member', 'employee'),
+            );
+        }
+    }
+
+Some things to note:
+
+  - The classname is `sspmod_mymodule_Auth_Source_MyAuth`.
+    This tells simpleSAMLphp to look for the class in `modules/mymodule/lib/Auth/Source/MyAuth.php`.
+
+  - Our authentication source subclassese `sspmod_core_Auth_UserPassBase`.
+    This is a helper-class that implements much of the common code needed for username/password authentication.
+
+  - The `login` function receives the username and password the user enters.
+    It is expected to authenticate the user.
+    If the username or password is correct, it must return a set of attributes for the user.
+    Otherwise, it must throw the `SimpleSAML_Error_Error('WRONGUSERPASS');` exception.
+
+  - Attributes are returned as an associative array of `name => values` pairs.
+    All attributes can have multiple values, so the values are always stored in an array.
+
+
+Configuring our authentication source
+-------------------------------------
+
+Before we can test our authentication source, we must add an entry for it in `config/authsources.php`.
+`config/authsources.php` contains an list of enabled authentication sources.
+
+The entry looks like this:
+
+    'myauthinstance' => array(
+        'mymodule:MyAuth',
+    ),
+
+You can add it to the beginning of the list, so that the file looks something like this:
+
+    <?php
+    $config = array(
+        'myauthinstance' => array(
+            'mymodule:MyAuth',
+        ),
+        /* Other authentication sources follow. */
+    );
+
+`myauthinstance` is the name of this instance of the authentication source.
+(You are allowed to have multiple instances of an authentication source with different configuration.)
+The instance name is used to refer to this authentication source in other configuration files.
+
+The first element of the configuration of the authentication source must be `'mymodule:MyAuth'`.
+This tells simpleSAMLphp to look for the `sspmod_mymodule_Auth_Source_MyAuth` class.
+
+
+Testing our authentication source
+---------------------------------
+
+Now that we have configured the authentication source, we can test it by accessing "authentication"-page of the simpleSAMLphp web interface.
+By default, the web interface can be found on `http://yourhostname.com/simplesaml/`.
+(Obviously, "yourhostname.com" should be replaced with your real hostname.)
+
+Then select the "Authentication"-tab, and choose "Test configured authentication sources".
+You should then receive a list of authentication sources from `config/authsources.php`.
+Select `myauthinstance`, and log in using "theusername" as the username, and "thepassword" as the password.
+You should then arrive on a page listing the attributes we return from the `login` function.
+
+Next, you should log out by following the log out link.
+
+
+Using our authentication source in an IdP
+-----------------------------------------
+
+To use our new authentication source in an IdP we just need to update the IdP configuration to use it.
+Open `metadata/saml20-idp-hosted.php`.
+In that file you should locate the `auth`-option for your IdP, and change it to `myauthinstance`:
+
+    <?php
+    /* ... */
+    $metadata['__DYNAMIC:1__'] = array(
+        /* ... */
+        /*
+         * Authentication source to use. Must be one that is configured in
+         * 'config/authsources.php'.
+         */
+        'auth' => 'myauthinstance',
+        /* ... */
+    );
+
+You can then test logging in to the IdP.
+If you have logged in previously, you may need to log out first.
+
+
+Adding configuration to our authentication source
+-------------------------------------------------
+
+Instead of hardcoding options in our authentication source, they should be configurable.
+We are now going to extend our authentication source to allow us to configure the username and password in `config/authsources.php`.
+
+First, we need to define the properties in the class that should hold our configuration:
+
+    private $username;
+    private $password;
+
+Next, we create a constructor for the class.
+The constructor is responsible for parsing the configuration and storing it in the properties.
+
+    public function __construct($info, $config) {
+        parent::__construct($info, $config);
+        if (!is_string($config['username'])) {
+            throw new Exception('Missing or invalid username option in config.');
+        }
+        $this->username = $config['username'];
+        if (!is_string($config['password'])) {
+            throw new Exception('Missing or invalid password option in config.');
+        }
+        $this->password = $config['password'];
+    }
+
+We can then use the properties in the `login` function.
+The complete class file should look like this:
+
+    <?php
+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {
+
+        private $username;
+        private $password;
+
+        public function __construct($info, $config) {
+            parent::__construct($info, $config);
+            if (!is_string($config['username'])) {
+                throw new Exception('Missing or invalid username option in config.');
+            }
+            $this->username = $config['username'];
+            if (!is_string($config['password'])) {
+                throw new Exception('Missing or invalid password option in config.');
+            }
+            $this->password = $config['password'];
+        }
+
+        protected function login($username, $password) {
+            if ($username !== $this->username || $password !== $this->password) {
+                throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+            }
+            return array(
+                'uid' => array($this->username),
+                'displayName' => array('Some Random User'),
+                'eduPersonAffiliation' => array('member', 'employee'),
+            );
+        }
+
+    }
+
+We can then update our entry in `config/authsources.php` with the configuration options:
+
+    'myauthinstance' => array(
+        'mymodule:MyAuth',
+        'username' => 'theconfigusername',
+        'password' => 'theconfigpassword',
+    ),
+
+Next, you should go to the "Test configured authentication sources" page again, and test logging in.
+Note that we have updated the username & password to "theconfigusername" and "theconfigpassword".
+(You may need to log out first before you can log in again.)
+
+
+A more complete example - custom database authentication
+--------------------------------------------------------
+
+The [sqlauth:SQL](./sqlauth:sql) authentication source can do simple authentication against SQL databases.
+However, in some cases it cannot be used, for example because the database layout is too complex, or because the password validation routines cannot be implemented in SQL.
+What follows is an example of an authentication source that fetches an user from a database, and validates the password using a custom function.
+
+This code assumes that the database contains a table that looks like this:
+
+    CREATE TABLE userdb (
+        username VARCHAR(32) PRIMARY KEY NOT NULL,
+        password_hash VARCHAR(64) NOT NULL,
+        full_name TEXT NOT NULL);
+
+An example user (with password "secret"):
+
+    INSERT INTO userdb (username, password_hash, full_name)
+        VALUES('exampleuser', 'QwVYkvlrAMsXIgULyQ/pDDwDI3dF2aJD4XeVxg==', 'Example User');
+
+In this example, the `password_hash` contains a base64 encoded SSHA password.
+A SSHA password is created like this:
+
+    $password = 'secret';
+    $numSalt = 8; /* Number of bytes with salt. */
+    $salt = '';
+    for ($i = 0; $i < $numSalt; $i++) {
+        $salt .= chr(mt_rand(0, 255));
+    }
+    $digest = sha1($password . $salt, TRUE);
+    $password_hash = base64_encode($digest . $salt);
+
+The class follows:
+
+    <?php
+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {
+
+        /* The database DSN.
+         * See the documentation for the various database drivers for information about the syntax:
+         *     http://www.php.net/manual/en/pdo.drivers.php
+         */
+        private $dsn;
+
+        /* The database username & password. */
+        private $username;
+        private $password;
+
+        public function __construct($info, $config) {
+            parent::__construct($info, $config);
+
+            if (!is_string($config['dsn'])) {
+                throw new Exception('Missing or invalid dsn option in config.');
+            }
+            $this->dsn = $config['dsn'];
+            if (!is_string($config['username'])) {
+                throw new Exception('Missing or invalid username option in config.');
+            }
+            $this->username = $config['username'];
+            if (!is_string($config['password'])) {
+                throw new Exception('Missing or invalid password option in config.');
+            }
+            $this->password = $config['password'];
+        }
+
+        /**
+         * A helper function for validating a password hash.
+         *
+         * In this example we check a SSHA-password, where the database
+         * contains a base64 encoded byte string, where the first 20 bytes
+         * from the byte string is the SHA1 sum, and the remaining bytes is
+         * the salt.
+         */
+        private function checkPassword($passwordHash, $password) {
+            $passwordHash = base64_decode($passwordHash);
+            $digest = substr($passwordHash, 0, 20);
+            $salt = substr($passwordHash, 20);
+
+            $checkDigest = sha1($password . $salt, TRUE);
+            return $digest === $checkDigest;
+        }
+
+        protected function login($username, $password) {
+
+            /* Connect to the database. */
+            $db = new PDO($this->dsn, $this->username, $this->password);
+            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+            /* Ensure that we are operating with UTF-8 encoding.
+             * This command is for MySQL. Other databases may need different commands.
+             */
+            $db->exec("SET NAMES 'utf8'");
+
+            /* With PDO we use prepared statements. This saves us from having to escape
+             * the username in the database query.
+             */
+            $st = $db->prepare('SELECT username, password_hash, full_name FROM userdb WHERE username=:username');
+
+            if (!$st->execute(array('username' => $username))) {
+                throw new Exception('Failed to query database for user.');
+            }
+
+            /* Retrieve the row from the database. */
+            $row = $st->fetch(PDO::FETCH_ASSOC);
+            if (!$row) {
+                /* User not found. */
+                SimpleSAML_Logger::warning('MyAuth: Could not find user ' . var_export($username, TRUE) . '.');
+                throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+            }
+
+            /* Check the password. */
+            if (!$this->checkPassword($row['password_hash'], $password)) {
+                /* Invalid password. */
+                SimpleSAML_Logger::warning('MyAuth: Wrong password for user ' . var_export($username, TRUE) . '.');
+                throw new SimpleSAML_Error_Error('WRONGUSERPASS');
+            }
+
+            /* Create the attribute array of the user. */
+            $attributes = array(
+                'uid' => array($username),
+                'displayName' => array($row['full_name']),
+                'eduPersonAffiliation' => array('member', 'employee'),
+            );
+
+            /* Return the attributes. */
+            return $attributes;
+        }
+
+    }
+
+And configured in `config/authsources.php`:
+
+    'myauthinstance' => array(
+        'mymodule:MyAuth',
+        'dsn' => 'mysql:host=sql.example.org;dbname=userdatabase',
+        'username' => 'db_username',
+        'password' => 'secret_db_password',
+    ),
+

{'commit': 'diff --git a/docs/index.txt b/docs/index.txt\nindex b2eba5e99..8ecea739c 100644\n--- a/docs/index.txt\n+++ b/docs/index.txt\n@@ -30,6 +30,8 @@ SimpleSAMLphp Documentation\n  * [simpleSAMLphp Modules](simplesamlphp-modules) - how to create own customized modules\n  * [Installing third party modules with the pack.php tool](pack)\n  * [Key rollover](./saml:keyrollover)\n+ * [Creating authentication sources](./simplesamlphp-authsource)\n+  * [Implementing custom username/password authentication](./simplesamlphp-customauth)\n \n Documentation on specific simpleSAMLphp modules:\n  \ndiff --git a/docs/simplesamlphp-authsource.txt b/docs/simplesamlphp-authsource.txt\nindex 311099a24..ad63b4967 100644\n--- a/docs/simplesamlphp-authsource.txt\n+++ b/docs/simplesamlphp-authsource.txt\n@@ -44,6 +44,7 @@ If the username or password is incorrect, it should throw an error saying so:\n \n     throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n \n+"[Implementing custom username/password authentication](./simplesamlphp-customauth)" describes how to implement username/password authentication using that base class.\n \n \n Generic rules & requirements\ndiff --git a/docs/simplesamlphp-customauth.txt b/docs/simplesamlphp-customauth.txt\nnew file mode 100644\nindex 000000000..d32e8a4e0\n--- /dev/null\n+++ b/docs/simplesamlphp-customauth.txt\n@@ -0,0 +1,350 @@\n+Implementing custom username/password authentication\n+====================================================\n+\n+This is a step-by-step guide for creating a custom username/password [authentication source](./simplesamlphp-authsource) for simpleSAMLphp.\n+An authentication source is responsible for authenticating the user, typically by getting a username and password, and looking it up in some sort of database.\n+\n+<!-- {{TOC}} -->\n+\n+Create a custom module\n+----------------------\n+\n+All custom code for simpleSAMLphp should be contained in a [module](./simplesamlphp-modules).\n+This ensures that you can upgrade your simpleSAMLphp installation without overwriting your own code.\n+In this example, we will call the module `mymodule`.\n+It will be located under `modules/mymodule`.\n+\n+First we need to create the module directory:\n+\n+    cd modules\n+    mkdir mymodule\n+\n+Since this is a custom module, it should always be enabled.\n+Therefore we create a `default-enable` file in the module.\n+We do that by copying the `default-enable` file from the `core` module.\n+\n+    cd mymodule\n+    cp ../core/default-enable .\n+\n+Now that we have our own module, we can move on to creating an authentication source.\n+\n+\n+Creating a basic authentication source\n+--------------------------------------\n+\n+Authentication sources are implemented using PHP classes.\n+We are going to create an authentication source named `mymodule:MyAuth`.\n+It will be implemented in the file `modules/mymodule/lib/Auth/Source/MyAuth.php`.\n+\n+To begin with, we will create a very simple authentication source, where the username and password is hardcoded into the source code.\n+Create the file `modules/mymodule/lib/Auth/Source/MyAuth.php` with the following contents:\n+\n+    <?php\n+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {\n+        protected function login($username, $password) {\n+            if ($username !== \'theusername\' || $password !== \'thepassword\') {\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+            return array(\n+                \'uid\' => array(\'theusername\'),\n+                \'displayName\' => array(\'Some Random User\'),\n+                \'eduPersonAffiliation\' => array(\'member\', \'employee\'),\n+            );\n+        }\n+    }\n+\n+Some things to note:\n+\n+  - The classname is `sspmod_mymodule_Auth_Source_MyAuth`.\n+    This tells simpleSAMLphp to look for the class in `modules/mymodule/lib/Auth/Source/MyAuth.php`.\n+\n+  - Our authentication source subclassese `sspmod_core_Auth_UserPassBase`.\n+    This is a helper-class that implements much of the common code needed for username/password authentication.\n+\n+  - The `login` function receives the username and password the user enters.\n+    It is expected to authenticate the user.\n+    If the username or password is correct, it must return a set of attributes for the user.\n+    Otherwise, it must throw the `SimpleSAML_Error_Error(\'WRONGUSERPASS\');` exception.\n+\n+  - Attributes are returned as an associative array of `name => values` pairs.\n+    All attributes can have multiple values, so the values are always stored in an array.\n+\n+\n+Configuring our authentication source\n+-------------------------------------\n+\n+Before we can test our authentication source, we must add an entry for it in `config/authsources.php`.\n+`config/authsources.php` contains an list of enabled authentication sources.\n+\n+The entry looks like this:\n+\n+    \'myauthinstance\' => array(\n+        \'mymodule:MyAuth\',\n+    ),\n+\n+You can add it to the beginning of the list, so that the file looks something like this:\n+\n+    <?php\n+    $config = array(\n+        \'myauthinstance\' => array(\n+            \'mymodule:MyAuth\',\n+        ),\n+        /* Other authentication sources follow. */\n+    );\n+\n+`myauthinstance` is the name of this instance of the authentication source.\n+(You are allowed to have multiple instances of an authentication source with different configuration.)\n+The instance name is used to refer to this authentication source in other configuration files.\n+\n+The first element of the configuration of the authentication source must be `\'mymodule:MyAuth\'`.\n+This tells simpleSAMLphp to look for the `sspmod_mymodule_Auth_Source_MyAuth` class.\n+\n+\n+Testing our authentication source\n+---------------------------------\n+\n+Now that we have configured the authentication source, we can test it by accessing "authentication"-page of the simpleSAMLphp web interface.\n+By default, the web interface can be found on `http://yourhostname.com/simplesaml/`.\n+(Obviously, "yourhostname.com" should be replaced with your real hostname.)\n+\n+Then select the "Authentication"-tab, and choose "Test configured authentication sources".\n+You should then receive a list of authentication sources from `config/authsources.php`.\n+Select `myauthinstance`, and log in using "theusername" as the username, and "thepassword" as the password.\n+You should then arrive on a page listing the attributes we return from the `login` function.\n+\n+Next, you should log out by following the log out link.\n+\n+\n+Using our authentication source in an IdP\n+-----------------------------------------\n+\n+To use our new authentication source in an IdP we just need to update the IdP configuration to use it.\n+Open `metadata/saml20-idp-hosted.php`.\n+In that file you should locate the `auth`-option for your IdP, and change it to `myauthinstance`:\n+\n+    <?php\n+    /* ... */\n+    $metadata[\'__DYNAMIC:1__\'] = array(\n+        /* ... */\n+        /*\n+         * Authentication source to use. Must be one that is configured in\n+         * \'config/authsources.php\'.\n+         */\n+        \'auth\' => \'myauthinstance\',\n+        /* ... */\n+    );\n+\n+You can then test logging in to the IdP.\n+If you have logged in previously, you may need to log out first.\n+\n+\n+Adding configuration to our authentication source\n+-------------------------------------------------\n+\n+Instead of hardcoding options in our authentication source, they should be configurable.\n+We are now going to extend our authentication source to allow us to configure the username and password in `config/authsources.php`.\n+\n+First, we need to define the properties in the class that should hold our configuration:\n+\n+    private $username;\n+    private $password;\n+\n+Next, we create a constructor for the class.\n+The constructor is responsible for parsing the configuration and storing it in the properties.\n+\n+    public function __construct($info, $config) {\n+        parent::__construct($info, $config);\n+        if (!is_string($config[\'username\'])) {\n+            throw new Exception(\'Missing or invalid username option in config.\');\n+        }\n+        $this->username = $config[\'username\'];\n+        if (!is_string($config[\'password\'])) {\n+            throw new Exception(\'Missing or invalid password option in config.\');\n+        }\n+        $this->password = $config[\'password\'];\n+    }\n+\n+We can then use the properties in the `login` function.\n+The complete class file should look like this:\n+\n+    <?php\n+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {\n+\n+        private $username;\n+        private $password;\n+\n+        public function __construct($info, $config) {\n+            parent::__construct($info, $config);\n+            if (!is_string($config[\'username\'])) {\n+                throw new Exception(\'Missing or invalid username option in config.\');\n+            }\n+            $this->username = $config[\'username\'];\n+            if (!is_string($config[\'password\'])) {\n+                throw new Exception(\'Missing or invalid password option in config.\');\n+            }\n+            $this->password = $config[\'password\'];\n+        }\n+\n+        protected function login($username, $password) {\n+            if ($username !== $this->username || $password !== $this->password) {\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+            return array(\n+                \'uid\' => array($this->username),\n+                \'displayName\' => array(\'Some Random User\'),\n+                \'eduPersonAffiliation\' => array(\'member\', \'employee\'),\n+            );\n+        }\n+\n+    }\n+\n+We can then update our entry in `config/authsources.php` with the configuration options:\n+\n+    \'myauthinstance\' => array(\n+        \'mymodule:MyAuth\',\n+        \'username\' => \'theconfigusername\',\n+        \'password\' => \'theconfigpassword\',\n+    ),\n+\n+Next, you should go to the "Test configured authentication sources" page again, and test logging in.\n+Note that we have updated the username & password to "theconfigusername" and "theconfigpassword".\n+(You may need to log out first before you can log in again.)\n+\n+\n+A more complete example - custom database authentication\n+--------------------------------------------------------\n+\n+The [sqlauth:SQL](./sqlauth:sql) authentication source can do simple authentication against SQL databases.\n+However, in some cases it cannot be used, for example because the database layout is too complex, or because the password validation routines cannot be implemented in SQL.\n+What follows is an example of an authentication source that fetches an user from a database, and validates the password using a custom function.\n+\n+This code assumes that the database contains a table that looks like this:\n+\n+    CREATE TABLE userdb (\n+        username VARCHAR(32) PRIMARY KEY NOT NULL,\n+        password_hash VARCHAR(64) NOT NULL,\n+        full_name TEXT NOT NULL);\n+\n+An example user (with password "secret"):\n+\n+    INSERT INTO userdb (username, password_hash, full_name)\n+        VALUES(\'exampleuser\', \'QwVYkvlrAMsXIgULyQ/pDDwDI3dF2aJD4XeVxg==\', \'Example User\');\n+\n+In this example, the `password_hash` contains a base64 encoded SSHA password.\n+A SSHA password is created like this:\n+\n+    $password = \'secret\';\n+    $numSalt = 8; /* Number of bytes with salt. */\n+    $salt = \'\';\n+    for ($i = 0; $i < $numSalt; $i++) {\n+        $salt .= chr(mt_rand(0, 255));\n+    }\n+    $digest = sha1($password . $salt, TRUE);\n+    $password_hash = base64_encode($digest . $salt);\n+\n+The class follows:\n+\n+    <?php\n+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {\n+\n+        /* The database DSN.\n+         * See the documentation for the various database drivers for information about the syntax:\n+         *     http://www.php.net/manual/en/pdo.drivers.php\n+         */\n+        private $dsn;\n+\n+        /* The database username & password. */\n+        private $username;\n+        private $password;\n+\n+        public function __construct($info, $config) {\n+            parent::__construct($info, $config);\n+\n+            if (!is_string($config[\'dsn\'])) {\n+                throw new Exception(\'Missing or invalid dsn option in config.\');\n+            }\n+            $this->dsn = $config[\'dsn\'];\n+            if (!is_string($config[\'username\'])) {\n+                throw new Exception(\'Missing or invalid username option in config.\');\n+            }\n+            $this->username = $config[\'username\'];\n+            if (!is_string($config[\'password\'])) {\n+                throw new Exception(\'Missing or invalid password option in config.\');\n+            }\n+            $this->password = $config[\'password\'];\n+        }\n+\n+        /**\n+         * A helper function for validating a password hash.\n+         *\n+         * In this example we check a SSHA-password, where the database\n+         * contains a base64 encoded byte string, where the first 20 bytes\n+         * from the byte string is the SHA1 sum, and the remaining bytes is\n+         * the salt.\n+         */\n+        private function checkPassword($passwordHash, $password) {\n+            $passwordHash = base64_decode($passwordHash);\n+            $digest = substr($passwordHash, 0, 20);\n+            $salt = substr($passwordHash, 20);\n+\n+            $checkDigest = sha1($password . $salt, TRUE);\n+            return $digest === $checkDigest;\n+        }\n+\n+        protected function login($username, $password) {\n+\n+            /* Connect to the database. */\n+            $db = new PDO($this->dsn, $this->username, $this->password);\n+            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n+\n+            /* Ensure that we are operating with UTF-8 encoding.\n+             * This command is for MySQL. Other databases may need different commands.\n+             */\n+            $db->exec("SET NAMES \'utf8\'");\n+\n+            /* With PDO we use prepared statements. This saves us from having to escape\n+             * the username in the database query.\n+             */\n+            $st = $db->prepare(\'SELECT username, password_hash, full_name FROM userdb WHERE username=:username\');\n+\n+            if (!$st->execute(array(\'username\' => $username))) {\n+                throw new Exception(\'Failed to query database for user.\');\n+            }\n+\n+            /* Retrieve the row from the database. */\n+            $row = $st->fetch(PDO::FETCH_ASSOC);\n+            if (!$row) {\n+                /* User not found. */\n+                SimpleSAML_Logger::warning(\'MyAuth: Could not find user \' . var_export($username, TRUE) . \'.\');\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+\n+            /* Check the password. */\n+            if (!$this->checkPassword($row[\'password_hash\'], $password)) {\n+                /* Invalid password. */\n+                SimpleSAML_Logger::warning(\'MyAuth: Wrong password for user \' . var_export($username, TRUE) . \'.\');\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+\n+            /* Create the attribute array of the user. */\n+            $attributes = array(\n+                \'uid\' => array($username),\n+                \'displayName\' => array($row[\'full_name\']),\n+                \'eduPersonAffiliation\' => array(\'member\', \'employee\'),\n+            );\n+\n+            /* Return the attributes. */\n+            return $attributes;\n+        }\n+\n+    }\n+\n+And configured in `config/authsources.php`:\n+\n+    \'myauthinstance\' => array(\n+        \'mymodule:MyAuth\',\n+        \'dsn\' => \'mysql:host=sql.example.org;dbname=userdatabase\',\n+        \'username\' => \'db_username\',\n+        \'password\' => \'secret_db_password\',\n+    ),\n+\n', 'commit_subject': 'Add document describing how to implement username/password authentication.', 'repo_name': 'simplesamlphp/simplesamlphp', 'committer': {'name': 'Olav Morken', 'email': '4725dc1a004aae106a46e49b3a1f11079a2cb0b6@uninett.no', 'time_sec': '1310635981', 'tz_offset': '0', 'date': {'seconds': '1310635981'}}, 'commit_tree': '186af5e3510807c28216cc183d27522b84796a61', 'commit_message': 'Add document describing how to implement username/password authentication.\n\ngit-svn-id: a243b28a2a52d65555a829a95c8c333076d39ae1@2872 44740490-163a-0410-bde0-09ae8108e29a\n', 'language_name': 'Shell', 'license': 'lgpl-2.1', 'hunks': '@@ -0,0 +1,350 @@\n+Implementing custom username/password authentication\n+====================================================\n+\n+This is a step-by-step guide for creating a custom username/password [authentication source](./simplesamlphp-authsource) for simpleSAMLphp.\n+An authentication source is responsible for authenticating the user, typically by getting a username and password, and looking it up in some sort of database.\n+\n+<!-- {{TOC}} -->\n+\n+Create a custom module\n+----------------------\n+\n+All custom code for simpleSAMLphp should be contained in a [module](./simplesamlphp-modules).\n+This ensures that you can upgrade your simpleSAMLphp installation without overwriting your own code.\n+In this example, we will call the module `mymodule`.\n+It will be located under `modules/mymodule`.\n+\n+First we need to create the module directory:\n+\n+    cd modules\n+    mkdir mymodule\n+\n+Since this is a custom module, it should always be enabled.\n+Therefore we create a `default-enable` file in the module.\n+We do that by copying the `default-enable` file from the `core` module.\n+\n+    cd mymodule\n+    cp ../core/default-enable .\n+\n+Now that we have our own module, we can move on to creating an authentication source.\n+\n+\n+Creating a basic authentication source\n+--------------------------------------\n+\n+Authentication sources are implemented using PHP classes.\n+We are going to create an authentication source named `mymodule:MyAuth`.\n+It will be implemented in the file `modules/mymodule/lib/Auth/Source/MyAuth.php`.\n+\n+To begin with, we will create a very simple authentication source, where the username and password is hardcoded into the source code.\n+Create the file `modules/mymodule/lib/Auth/Source/MyAuth.php` with the following contents:\n+\n+    <?php\n+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {\n+        protected function login($username, $password) {\n+            if ($username !== \'theusername\' || $password !== \'thepassword\') {\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+            return array(\n+                \'uid\' => array(\'theusername\'),\n+                \'displayName\' => array(\'Some Random User\'),\n+                \'eduPersonAffiliation\' => array(\'member\', \'employee\'),\n+            );\n+        }\n+    }\n+\n+Some things to note:\n+\n+  - The classname is `sspmod_mymodule_Auth_Source_MyAuth`.\n+    This tells simpleSAMLphp to look for the class in `modules/mymodule/lib/Auth/Source/MyAuth.php`.\n+\n+  - Our authentication source subclassese `sspmod_core_Auth_UserPassBase`.\n+    This is a helper-class that implements much of the common code needed for username/password authentication.\n+\n+  - The `login` function receives the username and password the user enters.\n+    It is expected to authenticate the user.\n+    If the username or password is correct, it must return a set of attributes for the user.\n+    Otherwise, it must throw the `SimpleSAML_Error_Error(\'WRONGUSERPASS\');` exception.\n+\n+  - Attributes are returned as an associative array of `name => values` pairs.\n+    All attributes can have multiple values, so the values are always stored in an array.\n+\n+\n+Configuring our authentication source\n+-------------------------------------\n+\n+Before we can test our authentication source, we must add an entry for it in `config/authsources.php`.\n+`config/authsources.php` contains an list of enabled authentication sources.\n+\n+The entry looks like this:\n+\n+    \'myauthinstance\' => array(\n+        \'mymodule:MyAuth\',\n+    ),\n+\n+You can add it to the beginning of the list, so that the file looks something like this:\n+\n+    <?php\n+    $config = array(\n+        \'myauthinstance\' => array(\n+            \'mymodule:MyAuth\',\n+        ),\n+        /* Other authentication sources follow. */\n+    );\n+\n+`myauthinstance` is the name of this instance of the authentication source.\n+(You are allowed to have multiple instances of an authentication source with different configuration.)\n+The instance name is used to refer to this authentication source in other configuration files.\n+\n+The first element of the configuration of the authentication source must be `\'mymodule:MyAuth\'`.\n+This tells simpleSAMLphp to look for the `sspmod_mymodule_Auth_Source_MyAuth` class.\n+\n+\n+Testing our authentication source\n+---------------------------------\n+\n+Now that we have configured the authentication source, we can test it by accessing "authentication"-page of the simpleSAMLphp web interface.\n+By default, the web interface can be found on `http://yourhostname.com/simplesaml/`.\n+(Obviously, "yourhostname.com" should be replaced with your real hostname.)\n+\n+Then select the "Authentication"-tab, and choose "Test configured authentication sources".\n+You should then receive a list of authentication sources from `config/authsources.php`.\n+Select `myauthinstance`, and log in using "theusername" as the username, and "thepassword" as the password.\n+You should then arrive on a page listing the attributes we return from the `login` function.\n+\n+Next, you should log out by following the log out link.\n+\n+\n+Using our authentication source in an IdP\n+-----------------------------------------\n+\n+To use our new authentication source in an IdP we just need to update the IdP configuration to use it.\n+Open `metadata/saml20-idp-hosted.php`.\n+In that file you should locate the `auth`-option for your IdP, and change it to `myauthinstance`:\n+\n+    <?php\n+    /* ... */\n+    $metadata[\'__DYNAMIC:1__\'] = array(\n+        /* ... */\n+        /*\n+         * Authentication source to use. Must be one that is configured in\n+         * \'config/authsources.php\'.\n+         */\n+        \'auth\' => \'myauthinstance\',\n+        /* ... */\n+    );\n+\n+You can then test logging in to the IdP.\n+If you have logged in previously, you may need to log out first.\n+\n+\n+Adding configuration to our authentication source\n+-------------------------------------------------\n+\n+Instead of hardcoding options in our authentication source, they should be configurable.\n+We are now going to extend our authentication source to allow us to configure the username and password in `config/authsources.php`.\n+\n+First, we need to define the properties in the class that should hold our configuration:\n+\n+    private $username;\n+    private $password;\n+\n+Next, we create a constructor for the class.\n+The constructor is responsible for parsing the configuration and storing it in the properties.\n+\n+    public function __construct($info, $config) {\n+        parent::__construct($info, $config);\n+        if (!is_string($config[\'username\'])) {\n+            throw new Exception(\'Missing or invalid username option in config.\');\n+        }\n+        $this->username = $config[\'username\'];\n+        if (!is_string($config[\'password\'])) {\n+            throw new Exception(\'Missing or invalid password option in config.\');\n+        }\n+        $this->password = $config[\'password\'];\n+    }\n+\n+We can then use the properties in the `login` function.\n+The complete class file should look like this:\n+\n+    <?php\n+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {\n+\n+        private $username;\n+        private $password;\n+\n+        public function __construct($info, $config) {\n+            parent::__construct($info, $config);\n+            if (!is_string($config[\'username\'])) {\n+                throw new Exception(\'Missing or invalid username option in config.\');\n+            }\n+            $this->username = $config[\'username\'];\n+            if (!is_string($config[\'password\'])) {\n+                throw new Exception(\'Missing or invalid password option in config.\');\n+            }\n+            $this->password = $config[\'password\'];\n+        }\n+\n+        protected function login($username, $password) {\n+            if ($username !== $this->username || $password !== $this->password) {\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+            return array(\n+                \'uid\' => array($this->username),\n+                \'displayName\' => array(\'Some Random User\'),\n+                \'eduPersonAffiliation\' => array(\'member\', \'employee\'),\n+            );\n+        }\n+\n+    }\n+\n+We can then update our entry in `config/authsources.php` with the configuration options:\n+\n+    \'myauthinstance\' => array(\n+        \'mymodule:MyAuth\',\n+        \'username\' => \'theconfigusername\',\n+        \'password\' => \'theconfigpassword\',\n+    ),\n+\n+Next, you should go to the "Test configured authentication sources" page again, and test logging in.\n+Note that we have updated the username & password to "theconfigusername" and "theconfigpassword".\n+(You may need to log out first before you can log in again.)\n+\n+\n+A more complete example - custom database authentication\n+--------------------------------------------------------\n+\n+The [sqlauth:SQL](./sqlauth:sql) authentication source can do simple authentication against SQL databases.\n+However, in some cases it cannot be used, for example because the database layout is too complex, or because the password validation routines cannot be implemented in SQL.\n+What follows is an example of an authentication source that fetches an user from a database, and validates the password using a custom function.\n+\n+This code assumes that the database contains a table that looks like this:\n+\n+    CREATE TABLE userdb (\n+        username VARCHAR(32) PRIMARY KEY NOT NULL,\n+        password_hash VARCHAR(64) NOT NULL,\n+        full_name TEXT NOT NULL);\n+\n+An example user (with password "secret"):\n+\n+    INSERT INTO userdb (username, password_hash, full_name)\n+        VALUES(\'exampleuser\', \'QwVYkvlrAMsXIgULyQ/pDDwDI3dF2aJD4XeVxg==\', \'Example User\');\n+\n+In this example, the `password_hash` contains a base64 encoded SSHA password.\n+A SSHA password is created like this:\n+\n+    $password = \'secret\';\n+    $numSalt = 8; /* Number of bytes with salt. */\n+    $salt = \'\';\n+    for ($i = 0; $i < $numSalt; $i++) {\n+        $salt .= chr(mt_rand(0, 255));\n+    }\n+    $digest = sha1($password . $salt, TRUE);\n+    $password_hash = base64_encode($digest . $salt);\n+\n+The class follows:\n+\n+    <?php\n+    class sspmod_mymodule_Auth_Source_MyAuth extends sspmod_core_Auth_UserPassBase {\n+\n+        /* The database DSN.\n+         * See the documentation for the various database drivers for information about the syntax:\n+         *     http://www.php.net/manual/en/pdo.drivers.php\n+         */\n+        private $dsn;\n+\n+        /* The database username & password. */\n+        private $username;\n+        private $password;\n+\n+        public function __construct($info, $config) {\n+            parent::__construct($info, $config);\n+\n+            if (!is_string($config[\'dsn\'])) {\n+                throw new Exception(\'Missing or invalid dsn option in config.\');\n+            }\n+            $this->dsn = $config[\'dsn\'];\n+            if (!is_string($config[\'username\'])) {\n+                throw new Exception(\'Missing or invalid username option in config.\');\n+            }\n+            $this->username = $config[\'username\'];\n+            if (!is_string($config[\'password\'])) {\n+                throw new Exception(\'Missing or invalid password option in config.\');\n+            }\n+            $this->password = $config[\'password\'];\n+        }\n+\n+        /**\n+         * A helper function for validating a password hash.\n+         *\n+         * In this example we check a SSHA-password, where the database\n+         * contains a base64 encoded byte string, where the first 20 bytes\n+         * from the byte string is the SHA1 sum, and the remaining bytes is\n+         * the salt.\n+         */\n+        private function checkPassword($passwordHash, $password) {\n+            $passwordHash = base64_decode($passwordHash);\n+            $digest = substr($passwordHash, 0, 20);\n+            $salt = substr($passwordHash, 20);\n+\n+            $checkDigest = sha1($password . $salt, TRUE);\n+            return $digest === $checkDigest;\n+        }\n+\n+        protected function login($username, $password) {\n+\n+            /* Connect to the database. */\n+            $db = new PDO($this->dsn, $this->username, $this->password);\n+            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n+\n+            /* Ensure that we are operating with UTF-8 encoding.\n+             * This command is for MySQL. Other databases may need different commands.\n+             */\n+            $db->exec("SET NAMES \'utf8\'");\n+\n+            /* With PDO we use prepared statements. This saves us from having to escape\n+             * the username in the database query.\n+             */\n+            $st = $db->prepare(\'SELECT username, password_hash, full_name FROM userdb WHERE username=:username\');\n+\n+            if (!$st->execute(array(\'username\' => $username))) {\n+                throw new Exception(\'Failed to query database for user.\');\n+            }\n+\n+            /* Retrieve the row from the database. */\n+            $row = $st->fetch(PDO::FETCH_ASSOC);\n+            if (!$row) {\n+                /* User not found. */\n+                SimpleSAML_Logger::warning(\'MyAuth: Could not find user \' . var_export($username, TRUE) . \'.\');\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+\n+            /* Check the password. */\n+            if (!$this->checkPassword($row[\'password_hash\'], $password)) {\n+                /* Invalid password. */\n+                SimpleSAML_Logger::warning(\'MyAuth: Wrong password for user \' . var_export($username, TRUE) . \'.\');\n+                throw new SimpleSAML_Error_Error(\'WRONGUSERPASS\');\n+            }\n+\n+            /* Create the attribute array of the user. */\n+            $attributes = array(\n+                \'uid\' => array($username),\n+                \'displayName\' => array($row[\'full_name\']),\n+                \'eduPersonAffiliation\' => array(\'member\', \'employee\'),\n+            );\n+\n+            /* Return the attributes. */\n+            return $attributes;\n+        }\n+\n+    }\n+\n+And configured in `config/authsources.php`:\n+\n+    \'myauthinstance\' => array(\n+        \'mymodule:MyAuth\',\n+        \'dsn\' => \'mysql:host=sql.example.org;dbname=userdatabase\',\n+        \'username\' => \'db_username\',\n+        \'password\' => \'secret_db_password\',\n+    ),\n+\n', 'addition_count': 350, 'deletion_count': 0, 'src_file': '/dev/null', 'tgt_file': 'b/docs/simplesamlphp-customauth.txt', 'file_extension': '.txt', 'before_file': 'ADDFILE', 'message': 'Add document describing how to implement username/password authentication.\n\ngit-svn-id: a243b28a2a52d65555a829a95c8c333076d39ae1@2872 44740490-163a-0410-bde0-09ae8108e29a\n', 'author_name': 'Olav Morken', 'lang': 'txt'}