Single Sign-On Server/Client Documentation

Do you need a PHP login system that rocks? Well, you found it.

The Single Sign-On (SSO) server and client is a complete SSO system in a single, self-contained package. This system is designed to be secure, massively scalable, and capable of handling outages. The SSO server has an easy to use point-and-click administrative interface for quick configuration and access to user account information. Versioned accounts make it possible to fill in additional information and display custom messages to users before allowing access to the account (e.g. tell users about a new product). The SSO client (for PHP and ASP.NET) integrates with numerous existing software products with minimal effort. The system also comes with an example API and a framework for native apps for Android, iOS, Windows Phone 8, and wxWidgets (Windows, Mac, and Linux desktop). This SSO system will quite literally handle all of your current and future needs regarding user account management.

Also, the SSO server/client comes with clear, easy to follow documentation on setting it up. Instead of inundating you with weird terms like Federation, iDP, SAML, etc. and expectations on knowing what those rather dense terms mean, this product takes a different approach where all the behind-the-scenes stuff stays behind the scenes. As long as you can follow step-by-step directions, you'll be up and running in just a few hours with a very slick sign in system that is point-and-click setup with a little programming. Don't worry about the programming part because there is plenty of example code and forums with friendly folks if you get stuck.

Features

This tool is split into three components: The SSO server, SSO client, and native app frameworks/examples. The following is a list of several features of the SSO server:

Now for a list of features of the SSO client:

And much more. If your jaw is currently on the floor, please pick it up.

Live Demos

Nothing beats a live demonstration of a working Single Sign-On (SSO) system. The following demos use the SSO system running on this website:

Using a Single Sign-On system allows for one sign in to be used to access multiple, disparate resources optionally across multiple domains.

License

Like Barebones CMS, both the SSO server and client are dual-licensed under a MIT or LGPL license - your choice. The license and restrictions are identical to the Barebones CMS License.

Donations

If you use this product, donate financially or with some of your time, to support future development efforts.

Downloads

Single Sign-On Server/Client 3.5 is the latest stable release of Single Sign-On Server/Client.

Download sso-3.5.zip

Download barebones_sso_plugin-2.0.zip (Barebones CMS plugin and login hook)

Download sso_client_asp_net.zip (ASP.NET port of the SSO client)

Other plugins: MyBB

If you find the Single Sign-On Server/Client useful, please donate toward future development efforts.

Terminology and Concepts

Unless you have used a Single Sign-On (SSO) system before, there is some terminology that you will need to understand to be able to work with this software product.

The SSO server separates login information from account information. The SSO server only knows what a Provider and a user account are, but not a login. Today, a Provider will implement a standard login system. However, if software developers figure out a way to eliminate usernames and passwords in the future, this SSO server can easily adapt to the new approach via a new Provider.

The SSO client communicates with the SSO server using an API key and encrypts the transmission using the associated API secret.

Architecture Overview

If you are the type of person who likes to know how things work at a high-level before delving into implementation details, this section is for you. A simplified breakdown of how things work is:

As far as most developers are concerned, the following set of steps will construct the first working application that uses the SSO server and client:

The SSO server and client are generally quite flexible, which is why so many steps are necessary to get a working and secure setup. Once the first application is up and running, it becomes much easier to start deploying more.

Upgrading the SSO Server/Client

These upgrade instructions only apply to those who have previously installed an older version of the SSO Server/Client. The SSO Server and SSO Client communicate with a tightly bound protocol. Major version number jumps in the software package (e.g. 1.0 to 2.0) indicate that the underlying protocol and/or database schema has changed significantly and that all server and client components have to be upgraded. Smaller version jumps (e.g. 2.0 to 2.1) indicate that there have been no protocol/database schema changes and upgrading just the server and whatever clients need to use a new feature will be necessary.

If you use the Security Through Obscurity (STO) feature of the SSO server, the 'admin.php' and 'endpoint.php' files will need to be uploaded separately to their respective directories on the web server when uploading files (i.e. 'admin_...' and 'endpoint_...'). If the files are uploaded by accident to the root SSO server directory with STO enabled, they won't function at the default location and the SSO client may not work properly because they will still be using the previous version of the endpoint. STO makes it harder for a hacker to find the location of the admin and endpoint but also adds a little extra time to the upgrade process.

Since these instructions are fairly short, they come before the extensive installation documentation. If you are installing the server and client for the first time, just skip ahead.

Major Version Upgrades

Major version upgrades take time to complete and can be a little nerve-wracking. You should schedule time for the SSO server and client to be offline/unavailable in which to work. Total time for a major version upgrade depends on a lot of factors, including how large the database is if there are schema changes, how many clients need to be upgraded, etc.

The upgrade procedure is as follows:

And that's it. If you encounter any problems upgrading, visit the forums on this site.

Minor Version Upgrades

Minor version upgrades take less time and planning to execute than major version upgrades. The upgrade procedure is similar to major version upgrades:

And that's it. If you encounter any problems upgrading, visit the forums on this site.

SSO Server Installation

Installing the SSO server is similar to installing Barebones CMS. But, due to its flexibility, it does require writing some extra code. However, don't worry - there are lots of examples to work from. The SSO server can reside on any host but must be accessible to a SSO client, the end-user, and any servers a provider might need to talk to. The SSO Server is written in PHP and requires a minimum of PHP 5.3 but preferably the latest version of PHP should be used. A CSDB-compliant database host is required to install the SSO server. The SSO server is also master/slave replication-aware if you need to scale out the database or already use a replication cluster.

At any rate, the installation procedure is as follows:

Take your time installing and setting up the SSO server since it will likely be around for a really long time. During the installation, a compatibility checklist is shown. It is highly recommended that all tests on the compatibility checklist pass before installing the SSO server. Some of the items will significantly impact the security and performance of the system if they don't pass.

If you opt to "Use CRON For Cleanup" during the installation, be sure to set up 'cron.php' to run on a schedule. It may be necessary to change permissions and modify the file a little to get it working. If the file is modified, rename 'cron.php' to something else so that upgrading the SSO server will be easier to do in the future.

After installation, the server needs to be set up for its first SSO client. The administrative interface requires a file called 'admin_hook.php' to exist that sets up '$bb_usertoken' before it will function. Create a file called 'admin_hook.php' and copy and paste the following code into the newly created file (may need to be modified if you are behind a firewall):

The above code is a simple way to initially access the administration interface. This code isn't secure but it will be secured properly in a bit, so keep following along.

Now upload the 'admin_hook.php' file to the same directory as 'admin.php'. If you opted to move 'admin.php' to a randomly named directory during the installation, you will have to figure out what the name of the directory is. Now click the link at the end of the installation to enter the administration interface.

The next few sections provide general guidance on how to set up the initial SSO server environment.

SSO Server Global Configuration

You can configure the server however you want and the configuration can be changed at any time. You will likely bounce around between various areas of the administration interface as you set things up.

Start with the "Configure" option under "SSO Server Options" and set the preferred timezone for the administration interface as well as set up any desired global whitelist and blacklist options - the settings here affect all Providers.

There are two semi-advanced options in the global configuration. There is a way to reset the namespace encryption key and IV that is used to encrypt the namespace cookie and the other option deals with clock drift. Understanding clock drift will help with deploying a sensible SSO server and client environment for your needs. The default clock drift is set to five minutes (300 seconds). This translates to a minimum of a 10 minute session regardless of what the SSO client declares as the session length because the SSO server automatically adjusts the minimum based on clock drift settings. Clock drift happens because computers can't keep perfect time. This causes problems that usually aren't noticeable on a single computer but across a network of machines it becomes apparent. Each SSO client packet is timestamped with the current date and time before encrypting the packet and sending it to the SSO server endpoint. The SSO server endpoint verifies that the packet timestamp is within acceptable limits to reduce the probability of a replay attack. This is where clock drift comes into play - the SSO client and server can be on completely different computers whose clocks likely aren't in perfect synchronization. In addition, the SSO client will attempt to send the same packet up to three times because networks are notoriously unreliable. The endpoint also applies a random delay to any request. Networks also have delays in delivery. A five minute window in either direction is decent, but it does force a minimum session length of ten minutes. Clock drift can be configured globally or on a per API key basis. Start with the default and adjust on an as-needed basis keeping in mind that minimum session length is clock drift * 2.

If a user is declared to be a spammer, the system default is to not provide any details about the situation and only outputs the generic message, "This system does not have any active providers. Either no providers have been configured or your current location has been blocked." That message, while accurate, is entirely unhelpful to the user. The server configuration option "No Providers Message" allows for additional details to be provided. This site uses the following:

<div class="sso_main_wrap">
<div class="sso_main_wrap_inner">
@BLOCKDETAILS@

<p>
If there are additional details above, please correct the problem(s) from your end.
Note that even if an IP block is cleared, it will take two weeks for the local cache to clear.
If there are no additional details, please contact the website administrator at:  webmaster @ cubiclesoft . com
</p>
</div>
</div>

The @BLOCKDETAILS@ token is replaced with information on why the user is declared to be a spammer. From there the user can take appropriate action to remove the block. Note that information about an IP address is cached for 14 days (by default). Even if a block is immediately removed, it will take additional time for the cached IP information to leave the SSO server.

The SSO server whitelist is a common field found in both the global server configuration and each provider configuration. It consists of one or more IP address patterns that are allowed to access or use the server or provider. Also, patterns should generally be IPv6 rather than IPv4.

Blacklists come in two flavors: DNSRBL and GeoIP blacklists. If an IP address is on a blacklist, it will be unable to access the server or the specific provider. Generally, an IP address should only be added to a blacklist if it is a spammer IP. For the global DNSRBL configuration, it is highly recommended to use the following setup:

# dnsbl.tornevall.org offers HTTP blacklisting via StopForumSpam.com's database.
# To add a spammer to this list, visit:  http://www.stopforumspam.com/add.php
# For more information, visit:  http://www.stopforumspam.com/usage
# Be sure to donate:  http://www.stopforumspam.com/donate
https://dnsbl.tornevall.org/|dnsbl.tornevall.org|127.0.0.&64

# HTTP:bl is a Honeypot Project DNSRBL.  Registration is required to use the service.
# For more information, visit:  http://www.projecthoneypot.org/httpbl.php
# Be sure to donate:  http://www.projecthoneypot.org/donate.php
http://www.projecthoneypot.org/list_of_ips.php|[YourPassword].@IP@.dnsbl.httpbl.org|127.<60.>9.>0

# dnsbl.sorbs.net offers a limited HTTP DNSRBL that doesn't catch many spammers.
# For more information, visit:  http://dnsbl.sorbs.net/
http://dnsbl.sorbs.net/|dnsbl.sorbs.net|127.0.0.2|127.0.0.7

GeoIP blacklisting takes an IP address and maps it to a physical location in the world. It isn't accurate or always right but it is usually good enough to identify someone's physical location to a city level. In order to use GeoIP blacklists, an IPv6 compatible database will have to be obtained and uploaded into the 'support' directory (e.g. 'GeoLiteCityv6.dat').

The global configuration also has GeoIP mapping options to take a GeoIP location field and map it to a SSO server field. Again, this requires a valid IPv6 compatible database to function.

Whitelist and blacklist options are found in both global and provider configurations. The global options are processed first followed by the specific provider options.

SSO Server Field Setup

A field stores a piece of information about a user. It can contain anything such as first name, last name, e-mail address, etc. Field data can be stored encrypted or unencrypted in the database. Ideally, all fields are encrypted, however only unencrypted fields are able to be searched when using the "Find User" option. So, encrypt as many fields as make sense to encrypt. You will still be able to edit encrypted field data.

The SSO server field manager.
The SSO server field manager.

To add a field, select the "Manage Fields" option under the "SSO Server Options" menu and then click "Add Field". Fill in the form and click "Create". The field will be added to the list in the field manager. Fields in the field manager can easily be added, removed, enabled, disabled, sliced, diced, and toasted. Changes to a field can be made at any time.

Of course, you may be wondering what fields to set up. Have no fear! Here is a list of common field names:

It is a good idea to separate pieces of user information wherever possible. Take 'first_name' and 'last_name' for instance. While 'full_name' could be a field that contains the user's full name, very few providers have support for a full name mapping and most SSO clients won't either.

Keep the number of fields to the minimum required for your needs. This helps keep RAM and storage requirements to a minimum and the server will run faster with fewer enabled fields. Also, asking users for more information will tend to keep them away.

Encrypted fields can't be searched on. The data goes into the database in a serialized and encrypted packet format. There is no way to search such data. If the field later is set to unencrypted, the user has to log in before the data becomes unencrypted and will show up in search results. The "Reset All Sessions" option can force everyone to login again if they are already logged in.

When a field is mapped in a provider, the provider protects the field from being edited in the administrative interface and across the API endpoint. This happens because the information will be overwritten the next time the user logs in using that provider. It is up to each provider to provide editing capabilities for protected fields (if possible).

SSO Server Tag Setup

The SSO server tag manager.
The SSO server tag manager.

A tag is generally an administrative permission but a tag can also change the behavior of an account in some specific way. Tags are usually added manually to user accounts but they can also be added via automation.

There are three default tags that are set up during installation that can't be deleted or easily changed: 'sso_site_admin', 'sso_admin', and 'sso_locked'.

The 'sso_site_admin' tag is intended to be used for an all-powerful admin account. A user with this tag can grant and revoke privileges for any user in the system, including themselves via the SSO server administration interface. As such, this tag is passed along automatically to SSO clients if the user has this tag associated with their account.

The 'sso_admin' tag is intended to be used to give certain users partial access to the SSO server administration interface. These users have the ability to find users in the system and edit fields that aren't protected by a Provider.

The 'sso_locked' tag is used to lock an account. Locked accounts stop a user from logging in while retaining their information. This tag can be used to temporarily disable a user account for a misbehaving user or as a pre-deletion step.

Before a client is set up, at least one provider needs to be set up.

SSO Server Provider: Generic Login

The SSO server comes with a generic login provider with a creative name of Generic Login. Behind the scenes, this is known as 'sso_login' and is the largest, most flexible, and the most complex provider of all the included SSO server providers.

Installing this provider is easy but there is one critical choice to make: Do you want users to sign up with a username, e-mail, or both? It depends heavily on your needs as to what gets selected during installation. However, changing this later is basically impossible and the default setting is recommended.

After installing, configure the Generic Login provider. Note that the default settings are set to be fairly liberal in terms of website security. If e-mail address verification is desired, use a subject line like "[websitename] Verify your e-mail address" and a message along the lines of:

<html>
<body>
@USERNAME@,<br />
<br />
In order to verify your new WebsiteName account, please use the link below:<br />
<br />
@VERIFY@<br />
<br />
If clicking the link doesn't work, try copying it and then pasting it into your web browser's address bar.<br />
<br />
If you did not sign up for a WebsiteName account, please ignore this e-mail.<br />
<br />
Your anti-phishing phrase is:<br />
<br />
@ANTIPHISH@
</body>
</html>

Replace "WebsiteName" with your website's name. Also, the above example depends on the anti-phishing module being enabled.

If account recovery via e-mail is desired, use either the same subject line as the e-mail address verification and a message along the lines of:

<html>
<body>
@USERNAME@,<br />
<br />
In order to recover your WebsiteName account, use the link below:<br />
<br />
@VERIFY@<br />
<br />
If clicking the link doesn't work, try copying it and then pasting it into your web browser's address bar.<br />
<br />
If you did not request recovery for a WebsiteName account, please ignore this e-mail.<br />
<br />
Your anti-phishing phrase is:<br />
<br />
@ANTIPHISH@
</body>
</html>

The two e-mails are very similar-looking. These are just simple examples of HTML that might be used. Obviously, it can get more complicated. The HTML is automatically transformed into text upon saving and both the HTML and text versions are sent in a multipart e-mail.

There are several modules included with the Generic Login provider:

Enabling a module adds more options to the configuration and immediately activates it, if possible. All of the modules are highly recommended.

The Password Requirements module calculates password strength and then requires users to meet a minimum strength for each password. It can optionally display a set of randomly selected words to the user that they can use to more easily construct a password that meets the minimum strength threshold. The following thresholds are recommended:

At 18 bits of entropy, approximately 99% of all poorly selected passwords are rejected outright and the brute force password cracking tools out there become useless hunks of junk.

Passwords in the Generic Login provider are hashed using a bcrypt-like hashing mechanism. Passwords are first salted and then as many rounds as possible are executed according to the minimum rules of the Generic Login configuration. If more rounds can be executed in the specified time frame, then the password will be that much more secure. As computer hardware gets faster, the number of rounds gets higher, thus taking the same amount of time to create the hash for new passwords.

Before moving on, most SSO server users won't want the words "Generic Login" to appear but something more familiar such as the website name. The easiest way to change this while making upgrading easy to do is to create a language pack that maps the string "Generic Login" to whatever is desired.

SSO Server Providers: Third-Party/Social Media

A third-party provider relies on a completely remote system to perform account authentication and authorization by redirecting to it. The SSO server comes with the following third-party providers:

A third-party provider generally requires access keys, tokens, etc. before it will start functioning properly. For example, the Facebook provider requires setting up a Facebook application at which point Facebook will generate an application ID and application secret that gets put into the provider. A well-written provider of this nature will include instructions on how to successfully set it up.

SSO Server Providers: Enterprise

An enterprise provider relies on a remote system to perform account authentication and authorization by directly connecting to it. The SSO server comes with the following enterprise providers:

An enterprise provider generally requires firewall settings to be configured to allow the SSO server to have access to the target host and the provider has to be configured correctly before it will connect to the target host. Also, setting up such providers can require a level of technical expertise that may result in hair-pulling. For example, the LDAP provider requires setting up a rather finicky server URL and Distinguished Name. If the setup isn't perfect, cryptic error messages will show up when someone attempts to sign in.

The Remote Login provider is documented in depth later on. It is an incredibly useful tool for signing into an application that relies on a public SSO server instance outside a firewall using a private server behind a firewall (e.g. via VPN) without opening any ports on the corporate firewall. The private server is responsible for authenticating the user (e.g. LDAP or SSPI), pushing the user's information to the public SSO server, and then redirecting the browser back to the public SSO server, which completes the sign in process. In addition, multiple "remotes" can be set up with the Remote Login provider.

SSO Server API Key Setup

The SSO server API key manager.
The SSO server API key manager.

After setting up the server configuration and providers as desired, it is time to prepare for the first SSO client installation. An API key and secret are required for proper operation. Every SSO client should have its own API key.

To create a new API key, select "Manage API Keys" under the "SSO Server Options" menu and click "Add API Key". Fill in the namespace, the reason for this key and a URL where an end-user will access the SSO system - not where the SSO client will reside but an obvious location that uses the client. Click "Create" and then set up an IP address whitelist, field mappings, and tag mappings. Setting up an IP address whitelist of what web server IP addresses can use the API key is highly recommended, but the system is generally secure even with the default settings.

Now for a brief word on namespaces. Namespaces allow for seamless sign in sharing between API keys. If a user signs into one application (e.g. the SSO client uses API key #1) and then makes their way over to another application (e.g. the SSO client for the second application uses API key #2), the user would normally have to sign in again. With namespaces, if two API keys share the same namespace, the SSO server will see that there is an active session in the namespace already, automatically activate the same account, and attempt to automate the validation phase of the sign in process. In theory, the sign in for the second application will be completely transparent, behind-the-scenes browser redirects. If the session has expired, the SSO client sent the special 'invalid_permissions' message, or the user's IP address has changed, the user will have to sign in again.

To avoid confusing users, all SSO client installations using API keys in the same namespace should have similar cookie timeout settings. If the cookie timeout settings for a client are not the same, users may have to sign in again at weird times and may perceive the system as flaky.

A good strategy is to use the following namespaces:

If you wish to isolate each API key into its own namespace, you can use the API key's numerical ID. The API key ID is guaranteed to be unique and generated upon creation of the API key.

SSO Client Installation

Installing the SSO client is similar to installing Barebones CMS. The installation procedure is as follows:

SSO server API key information.
SSO server API key information.

During installation, the installer will ask for an endpoint URL, an API key, and an API secret.

All three required bits of information can be obtained by editing an API key in the SSO server. Copy and paste the information from the SSO server into the installer. Be sure to test the settings before installing to make sure that everything works properly.

Ideally, there should be one SSO client installation for every single application so that only absolutely required information is passed to each client. However, each time a new client is encountered, the user will have to log in again, which could get to be rather annoying when using several different systems. Convenience will unfortunately win out over security. A good, balanced approach is to install two clients: One for regular end-user activities and one for access to administrative interfaces.

A fair bit of warning: The SSO client has a lot of options and some of them are not obvious as to how to set them. It really depends heavily on what you want to do with the software that will utilize the client. Of particular note is the SSO client cookie path, which is almost guaranteed to be wrong for your needs - it should point at the root of your application, not the root of the SSO client. But since software isn't magical, it has to be pointed at the right location manually. But I didn't want the field to be empty either so that I didn't have to write a paragraph of instructions in the installer.

Testing the SSO Client

Once the SSO client has been successfully installed, it is time to try it out and make sure everything is in working order. Create a file called 'test_oo.php' wherever the application will reside and copy and paste the following code:

<?php
	// These two lines should be executed as soon as possible.
	require_once "client/config.php";
	require_once SSO_CLIENT_ROOT_PATH . "/index.php";

	$sso_client = new SSO_Client;
	$sso_client->Init(array("sso_impersonate", "sso_remote_id"));

	// The rest of this code can be executed whenever.
	$extra = array();
	if (isset($_REQUEST["sso_impersonate"]) && is_string($_REQUEST["sso_impersonate"]))  $extra["sso_impersonate"] = $_REQUEST["sso_impersonate"];
	else if (isset($_REQUEST["sso_remote_id"]) && is_string($_REQUEST["sso_remote_id"]))
	{
		$extra["sso_provider"] = "sso_remote";
		$extra["sso_remote_id"] = $_REQUEST["sso_remote_id"];
	}
	if (!$sso_client->LoggedIn())  $sso_client->Login("", "You must login to use this system.", $extra);

	if (!$sso_client->UserLoaded())
	{
		// Load local information from the encrypted cookie.
		$username = $sso_client->GetData("u");
		$firstname = $sso_client->GetData("fn");

		// If the cookie data is too long, false will be returned,
		// so load the official data.
		if ($username === false || $firstname === false)
		{
			if (!$sso_client->LoadUserInfo())
			{
				echo "Unable to load user information.";
				exit();
			}
		}
	}

	if ($sso_client->UserLoaded())
	{
		// Refresh local information from the SSO server data.
		$username = $sso_client->GetField("username");
		$firstname = $sso_client->GetField("first_name");

		// Save the data for later.
		$sso_client->SetData("u", $username);
		$sso_client->SetData("fn", $firstname);
	}

	// Send the browser cookies.
	$sso_client->SaveUserInfo();

	// Test permissions for the user.
	if (!$sso_client->IsSiteAdmin())  $sso_client->Login("", "insufficient_permissions");

	// Get the internal token for use with XSRF defenses.
	// Not used in this example.
	$bb_usertoken = $sso_client->GetSecretToken();

	// A simple example.
	if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "logout")
	{
		$sso_client->Logout();

		$url = $sso_client->GetFullRequestURLBase();

		header("Location: " . $url);
		exit();
	}
	else
	{
		echo "User ID:  " . $sso_client->GetUserID() . "<br />";
		echo "Username:  " . htmlspecialchars($username) . "<br />";
		echo "First Name:  " . htmlspecialchars($firstname) . "<br />";
		echo "<br />";
		echo "<a href=\"test_oo.php\">Test local access</a><br />";
		echo "<a href=\"test_oo.php?action=logout\">Logout</a>";
	}
?>

If you would rather use plain functions instead of the class, name the file 'test_flat.php' and copy and paste the following code:

<?php
	// These two lines should be executed as soon as possible.
	$sso_removekeys = array("sso_impersonate", "sso_remote_id");
	require_once "client/config.php";
	require_once SSO_CLIENT_ROOT_PATH . "/index.php";
	require_once SSO_CLIENT_ROOT_PATH . "/functions.php";

	// The rest of this code can be executed whenever.
	$extra = array();
	if (isset($_REQUEST["sso_impersonate"]) && is_string($_REQUEST["sso_impersonate"]))  $extra["sso_impersonate"] = $_REQUEST["sso_impersonate"];
	else if (isset($_REQUEST["sso_remote_id"]) && is_string($_REQUEST["sso_remote_id"]))
	{
		$extra["sso_provider"] = "sso_remote";
		$extra["sso_remote_id"] = $_REQUEST["sso_remote_id"];
	}
	if (!SSO_LoggedIn())  SSO_Login("", "You must login to use this system.", $extra);

	if (!SSO_UserLoaded())
	{
		// Load local information from the encrypted cookie.
		$username = SSO_GetData("u");
		$firstname = SSO_GetData("fn");

		// If the cookie data is too long, false will be returned,
		// so load the official data.
		if ($username === false || $firstname === false)
		{
			if (!SSO_LoadUserInfo())
			{
				echo "Unable to load user information.";
				exit();
			}
		}
	}

	if (SSO_UserLoaded())
	{
		// Refresh local information from the SSO server data.
		$username = SSO_GetField("username");
		$firstname = SSO_GetField("first_name");

		// Save the data for later.
		SSO_SetData("u", $username);
		SSO_SetData("fn", $firstname);
	}

	// Send the browser cookies.
	SSO_SaveUserInfo();

	// Test permissions for the user.
	if (!SSO_IsSiteAdmin())  SSO_Login("", "insufficient_permissions");

	// Get the internal token for use with XSRF defenses.
	// Not used in this example.
	$bb_usertoken = SSO_GetSecretToken();

	// A simple example.
	if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "logout")
	{
		SSO_Logout();

		$url = SSO_GetFullRequestURLBase();

		header("Location: " . $url);
		exit();
	}
	else
	{
		echo "User ID:  " . SSO_GetUserID() . "<br />";
		echo "Username:  " . htmlspecialchars($username) . "<br />";
		echo "First Name:  " . htmlspecialchars($firstname) . "<br />";
		echo "<br />";
		echo "<a href=\"test_flat.php\">Test local access</a><br />";
		echo "<a href=\"test_flat.php?action=logout\">Logout</a>";
	}
?>

Change the highlighted line with 'config.php' to point at the 'config.php' in the SSO client directory. The first few lines should be executed as soon as is possible in an application so the SSO client has the best chance of operating transparently to the application (especially true when implementing this as a third-party software plugin).

This example uses all sorts of features of the SSO client. Specifically, extensive use of cookie caching to minimize the number of requests to the SSO server for user information. At any rate, go ahead and visit the 'test_oo.php' or 'test_flat.php' page.

Initial sign in is ugly.
Initial sign in is ugly.

If everything is successful and at least one provider is configured correctly, you will be rewarded with an ugly-looking sign in screen.

Build a Custom Header and Footer

But don't fear! For the ugliness can be remedied with a little HTML and CSS. Fortunately, in the 'examples' directory included with the SSO server is a very nice-looking, modern layout. You don't have to use it, but it will save some time. The only part you actually have to build is a header and footer.

The new hotness.
The new hotness.

Simply create 'header.php' and 'footer.php' in the SSO server's main directory and write a standard header and footer. Then link to the 'examples/main.css' file in the header as one does with a CSS file and...voilĂ ! The result is a really, really ridiculously good-looking interface.

The header and footer files are loaded very early on and stored into $sso_header and $sso_footer variables in the SSO server frontend to accommodate early error messages that might arise (e.g. database connection failures). This makes major customizations a bit difficult. There are two functions that can be created which can be placed either in 'header.php' or 'footer.php' that allow $sso_header and $sso_footer to be completely or partially replaced with something else after the user's session information is loaded up but before any actions are processed:

The recommended approach is to use a single header and footer so users aren't confused as to which sign in they need to use and which login information to provide.

Create a Site Admin Account

The next step is to create an account that will have access to the SSO server admin. Right now, the admin is only accessible because 'admin_hook.php' is granting access but it still needs to be secured. This is the next step of that whole process.

Creating a valid account is easy: Just go through the motions of successfully signing in and returning to the test page. Be sure to use a secure password. If you are using the example test page, you will be kicked back to the login screen with the message, "Your account has insufficient permissions to access that resource."

Switch over to the SSO server admin and use "Find User" to locate the account. Add the 'sso_site_admin' tag to the account. Switch back to the login screen and sign in again. Logging in should now succeed.

Understanding Field Flow

Understanding how user account field information flows from and through the SSO server to the SSO client is critical to making sure each application gets the information it needs to operate correctly.

Flowchart of how field data flows through.
Flowchart of how field data flows through.

Providers supply some field information. When a SSO server provider supplies a field, it also protects that information from being modified. For instance, the Generic Login provider passes an e-mail address and username onto the SSO server and also protects those fields from modification at the user account level. This is because the next time the user would log in, the provider will overwrite the value for each field in the user account. It is up to the provider to make it possible to change the associated fields that it protects if it is possible to do so.

The other source of field information is user-supplied. There are two ways to set user-supplied information:

These two sets of information form the entire user account. This information is transformed by the SSO server endpoint when a client connects with a specific API key. The API key determines what fields and tags flow through from the server to the client and what the target field names will be. Each API key restricts what fields and tags flow through by the mapping for each field and tag. If there is no mapping, the field/tag will not be sent. If there is a mapping, the field/tag will be sent.

The application can retrieve a field's value with the SSO_GetField() function and if the user has a specific tag with the SSO_HasTag() function.

If you logged into the system with the test page using the Generic Login provider, there will likely be no information for the Username and First Name fields. This happens because there is either no information or the mapping for the API key is incorrect. Feel free to experiment a bit.

Using Versioned Accounts

One of the nice features of the SSO server is that each user account has a version number associated with it. Every account starts at version zero (0) and it is entirely up to you to decide how you want to deal with version numbers. Checking the version of an account and doing something based on the current value is done by creating an 'index_hook.php' file. Here is a non-working example of a possible 'index_hook.php' implementation:

<?php
	// An example index hook for the SSO server.
	// (C) 2012 CubicleSoft

	if (!defined("SSO_FILE"))  exit();

	$versions = array(
		"legal" => 4,
		"marketing_ads" => 6
	);
	$latestversion = max($versions);
	if ($sso_userrow->version == 0)
	{
		// Awesome.
		if (SSO_FrontendFieldValue("submit") !== false)
		{
			// Process form submission.
//			$sso_user_info["first_name"] = "Colonel";
//			$sso_user_info["last_name"] = "Sanders";

			// Save changes.
			SSO_SetUserVersion($latestversion);

			header("Location: " . $sso_target_url);
			exit();
		}

		echo "New account!  You rock!";
	}
	else if ($sso_userrow->version < $versions["legal"])
	{
		// Legal sent this down the other day.
		echo "New Terms of Service and Privacy Policy - BORING!";
	}
	else if ($sso_userrow->version < $versions["marketing_ads"])
	{
		// Because we want our users to give us their money.
		echo "Latest promotion/advertisement/feature!  Slobbery hugs and kisses!";
	}
	else
	{
		// Automate some fields here.
		$changed = false;

		// ...

		// Save changes.
		if ($changed)
		{
			SSO_SetUserVersion($latestversion);

			header("Location: " . $sso_target_url);
			exit();
		}

		if (count($sso_missingfields))
		{
			// Have the user fill in the remaining missing fields.
			if (SSO_FrontendFieldValue("submit") !== false)
			{
				// Process form submission.

				// Save changes.
				SSO_SetUserVersion($latestversion);

				header("Location: " . $sso_target_url);
				exit();
			}

			// Display form here.
			echo "Need some additional information to continue.  Sell your soul (or privacy) here.";
		}
		else
		{
			SSO_ValidateUser();

			SSO_DisplayError("Error:  Unable to validate the new session.  Most likely cause:  Internal error.");
		}
	}
?>

The code starts off with checking to see if this is a brand new account. This is where you might offer the user the option to sign up for a newsletter or agree to your terms of service or whatever else you might dream of. When they submit the form (not shown), it sets the user version to the latest version, redirects back to itself, and then moves along to the latest version tasks.

If the user account is not on the latest version, the code checks to see what the user needs to see or do first. In this case, 'legal' issues come first, then 'marketing and advertising' initiatives. The user only sees one or the other but not both during the same login attempt.

Finally, the code checks to see which fields are being sent to the client that are empty and aren't protected by the associated provider. First, it tries to automatically fill in the missing fields. If it fails to do that, the user gets to fill the fields in. Once everything looks okay, SSO_ValidateUser() is called, which finalizes the session and returns to the SSO client.

And now for a working implementation from this website:

<?php
	// Barebones CMS website index hook.
	// (C) 2012 CubicleSoft

	if (!defined("SSO_FILE"))  exit();

	foreach ($sso_missingfields as $field)  $sso_user_info[$field] = "";

	$knownfields = array(
		"email" => "E-mail Address",
		"username" => "Username",
		"first_name" => "First Name",
		"last_name" => "Last Name",
		"mybb_usertitle" => "",
		"mybb_gid" => ""
	);

	foreach ($sso_apikey_info["field_map"] as $key => $info)
	{
		if (!isset($knownfields[$key]))
		{
			echo htmlspecialchars(BB_Translate("Unknown required user field '%s'.  Someone broke this system.  Oops!", $key));
			exit();
		}
	}

	// Add miscellaneous protected fields.
	$protectedfields = array(
		"mybb_usertitle", "mybb_gid"
	);
	foreach ($protectedfields as $key)  $sso_protectedfields[$key] = true;

	// Check for editable fields.
	$found = false;
	foreach ($knownfields as $key => $disp)
	{
		if ((!isset($sso_protectedfields[$key]) || !$sso_protectedfields[$key]) && ((isset($sso_user_info[$key]) && $sso_user_info[$key] != "") || isset($sso_apikey_info["field_map"][$key])))
		{
			if (!$sso_automate || !isset($sso_user_info[$key]) || $sso_user_info[$key] == "")  $found = true;

			break;
		}
	}

	// Skip the verification if there are no editable fields.
	if (!$found)
	{
		SSO_ValidateUser();

		SSO_DisplayError("Error:  Unable to validate the new session.  Most likely cause:  Internal error.");
	}

	$messages = array();

	// Process form submission.
	if (SSO_FrontendFieldValue("submit") !== false)
	{
		foreach ($knownfields as $key => $disp)
		{
			if ((!isset($sso_protectedfields[$key]) || !$sso_protectedfields[$key]) && ((isset($sso_user_info[$key]) && $sso_user_info[$key] != "") || isset($sso_apikey_info["field_map"][$key])))
			{
				$sso_user_info[$key] = SSO_FrontendFieldValue($key, "");
				if ($sso_user_info[$key] == "")  $messages[] = BB_Translate("Fill in '%s'.", BB_Translate($disp));
			}
		}

		if (!count($messages))
		{
			// Save changes.
			SSO_SetUserVersion(0);

			// Proceed.
			SSO_ValidateUser();

			SSO_DisplayError("Error:  Unable to validate the new session.  Most likely cause:  Internal error.");
		}
	}

	echo $sso_header;

	SSO_OutputHeartbeat();

?>
<div class="sso_main_wrap">
<div class="sso_main_wrap_inner">
<?php
	if (count($messages))
	{
?>
	<div class="sso_main_messages_wrap">
		<div class="sso_main_messages_header"><?php echo htmlspecialchars(BB_Translate(count($messages) == 1 ? "Please correct the following problem:" : "Please correct the following problems:")); ?></div>
		<div class="sso_main_messages">
<?php
		foreach ($messages as $message)
		{
?>
			<div class="sso_main_messageerror"><?php echo htmlspecialchars($message); ?></div>
<?php
		}
?>
		</div>
	</div>
<?php
	}
?>
	<div class="sso_main_form_wrap sso_login_signup_form">
		<div class="sso_main_form_header"><?php echo htmlspecialchars(BB_Translate("Verify Information")); ?></div>
		<form class="sso_main_form" name="sso_main_form" method="post" accept-charset="UTF-8" enctype="multipart/form-data" action="<?php echo htmlspecialchars($sso_target_url); ?>">

<?php
	foreach ($knownfields as $key => $disp)
	{
		if ($disp == "")  continue;

		if (isset($sso_protectedfields[$key]) && $sso_protectedfields[$key])
		{
?>
			<div class="sso_main_formitem">
				<div class="sso_main_formtitle"><?php echo htmlspecialchars(BB_Translate($disp)); ?></div>
				<div class="sso_main_formdata"><div class="sso_main_static"><?php echo htmlspecialchars($sso_user_info[$key]); ?></div></div>
			</div>
<?php
		}
		else if ((isset($sso_user_info[$key]) && $sso_user_info[$key] != "") || isset($sso_apikey_info["field_map"][$key]))
		{
?>
			<div class="sso_main_formitem">
				<div class="sso_main_formtitle"><?php echo htmlspecialchars(BB_Translate($disp)); ?></div>
				<div class="sso_main_formdata"><input class="sso_main_text" type="text" name="<?php echo SSO_FrontendField($key); ?>" value="<?php echo htmlspecialchars(SSO_FrontendFieldValue($key, $sso_user_info[$key])); ?>" /></div>
			</div>
<?php
		}
	}
?>
			<script type="text/javascript">
			jQuery('input.sso_main_text:first').focus();
			</script>
			<div class="sso_main_formsubmit">
				<input type="submit" name="<?php echo SSO_FrontendField("submit"); ?>" value="<?php echo htmlspecialchars(BB_Translate("Continue")); ?>" />
			</div>
		</form>
	</div>
</div>
</div>
<?php

	echo $sso_footer;
?>

This code looks very different because it shows the actual form construction and the approach is entirely different from the previous approach. Here versioned accounts are not being used but the interception of the login allows me to not have to create a separate user profile page. This system allows the user to evaluate the stored information they are allowed to modify and make changes to it before continuing.

One significant benefit of implementing 'index_hook.php' is that bots will have to get through it before continuing. Custom-built code breaks most bots. Therefore, doing virtually anything here will throw a monkey wrench into some bot operator's life.

Securing the Admin Interface

The last key step to most installations is to secure the SSO server administration interface. The original access script just allowed a specific IP address. If this is the desired behavior, there is some authentication risk but that can be mitigated by deleting 'admin_hook.php' and then no one can access the admin interface until 'admin_hook.php' is re-uploaded to the server.

The simpler method is to install a second SSO client into the SSO server admin directory. When setting up the API key configuration, map the 'sso_admin' tag to itself. Then use the following for the 'admin_hook.php' script:

<?php
	if (!defined("SSO_FILE"))  exit();

	require_once "client/config.php";
	require_once SSO_CLIENT_ROOT_PATH . "/index.php";

	$sso_client = new SSO_Client;
	$sso_client->Init(array("sso_impersonate", "sso_remote_id"));

	if (!$sso_client->LoggedIn())  $sso_client->Login("", "You must login to use this system.");

	// Send the browser cookies.
	$sso_client->SaveUserInfo();

	// Test permissions for the user.
	if (!$sso_client->IsSiteAdmin() && !$sso_client->HasTag("sso_admin"))  $sso_client->Login("", "insufficient_permissions");

	// Get the internal token for use with XSRF defenses.
	$bb_usertoken = $sso_client->GetSecretToken();

	$sso_site_admin = $sso_client->IsSiteAdmin();
	$sso_user_id = $sso_client->GetUserID();

	// Add a menu option to logout.
	function AdminHook_MenuOpts()
	{
		global $sso_menuopts, $sso_client;

		$sso_menuopts["SSO Server Options"]["Logout"] = BB_GetRequestURLBase() . "?action=logout&sec_t=" . BB_CreateSecurityToken("logout");

		if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "logout")
		{
			$sso_client->Logout();

			header("Location: " . BB_GetFullRequestURLBase());
			exit();
		}
	}
?>

This code is fairly simple and only allows users with 'sso_site_admin' and 'sso_admin' tags to access the system. Similar sort of code can be used for Admin Pack and a Barebones CMS login hook but with different tags as necessary.

Miscellaneous Customization

When more than one provider is available, the user gets a selection screen. At this point, the user starts seeing some of the internal names such as "Generic Login" show up but this is not necessarily desired behavior. Customization of display strings is accomplished through the multilingual support options. Using IANA language codes (e.g. 'en-us'), a language pack can be built to transform nearly all display strings in order to display whatever output you want users to see. Using the language pack approach allows for easier future upgrades of the SSO server and client.

The multilingual support in the SSO server is more robust than displaying alternate strings. The SSO client also supports passing an IANA language code along to the SSO server for a seamless transition across hosts and complete translation support of almost every string (including those in a header and footer). The configuration files and installers can also select default languages for various scenarios.

When multiple providers are active and the CSS file is used from the 'examples' directory, the default icons may not be what you want to use. There is a sample Photoshop PSD file included with the Generic Login icon pattern. That should make it easier to construct alternate or additional icons to use with the various SSO server providers or your own provider.

There are several callbacks in the SSO server that hook scripts can utilize. An example of this is found in the "Securing the Admin Interface" section above.

The SSO server endpoint supports Custom API keys. These are similar to Remote API keys but won't do anything until you write code to support this key type but they allow for complete customization of the server via the same secure communication mechanisms of other API keys. For example, they could be used to set and retrieve fields and/or tags that are only available to a private API for server-to-server communications.

User Impersonation

Sometimes it becomes necessary to impersonate users. There are two common scenarios where user impersonation becomes useful: Seeing an issue that a specific user is seeing with an application and users who find new ways to forget their sign in password. Both scenarios are equally frustrating for the user and system administrators. System administrators will generally have site admin privileges and not be able to see what ordinary user accounts see unless they have a separate account for that purpose. User impersonation becomes a tool for making life simpler. However, user impersonation support is a security risk.

The SSO server/client, by default, disables user impersonation for the aforementioned security reasons. Enabling user impersonation requires enabling it for each user account that needs it, the application's API key, and the application itself. The first step is to enable the API key and the application for passing 'sso_impersonate' with an account impersonation token. Once that is done, the API key will accept impersonation tokens from that point on (unless it is disabled again later). The next step is to locate a user account and enable impersonation by selecting 'Yes' from the appropriate drop down and clicking 'Save'. The server will generate a token and a new drop down for automation appears that is optional. Take the impersonation token and construct a URL by using the token with 'sso_impersonate'. If you are signed in, you'll likely need to sign out first. An impersonation URL will look like:

http://yourdomain.com/?sso_impersonate=[impersonation_token]

User impersonation URLs can be bookmarked in a browser for a one-click sign in. This can be useful for users who frequently forget their password. Such users are likely a security risk anyway by being part of the group of people who select weak passwords, so the one-click sign in token (64 completely random letters and numbers) is theoretically more secure as long as it is only transmitted over HTTPS.

SSO Server Provider: Remote Login

The Remote Login provider gets its own section of documentation because, behind the scenes, it is a strange provider with special capabilities that both the SSO server endpoint and the SSO client directly support. It is the only provider like this for very specific reasons that will become apparent shortly. There are three different scenarios where the Remote Login provider comes in real handy:

The Remote Login provider solves all of these problems and takes roughly 30 minutes to an hour to set up. The first scenario is common and sign ins can actually be fully automated in most corporate environments (Apache + mod_auth_sspi + Active Directory + SSO Client + a little software glue). The second scenario is the one scenario most Software as a Service (SaaS) developers have nightmares over having to deal with but is actually quite similar to the first scenario - the only difference is that you have to work with someone else usually via e-mail. The third is basically custom work and the Remote Provider is merely there to make the process less painful.

The first step to using the Remote Login provider is to install it. Click "Install" under the "Remote Login" option in the SSO server admin. Click the "Install" button. This creates the necessary database table that will contain the remotes.

Next, set up a "Remote" API key. This procedure is identical to setting up a "Normal" API key. After creating the API key as usual (leave the URL field empty for now), change its "Type" to "Remote" and click "Save" and the API key will be a Remote API key. Also, be sure to map fields you want to allow the remote to be able to write to and set the "Permissions" to "Read/Write" on each mapped field. Remote API keys can only be used with the SSO_RemoteLogin() call in the SSO client. That is, the usual SSO client functions (e.g. SSO_Login()) are disabled at the SSO server level. Remote API keys can only write field mapped data, which is subsequently considered protected by the Remote Login provider.

Copy the API key numerical ID to the clipboard (or just memorize it) and select "Manage Remotes" under "Remote Login". Click "Add Remote." Enter in the business name or business unit that will sign in with this remote and paste or type in the API key numerical ID. Click "Create." If all goes well, the editing screen will appear.

On the editing screen, fill in any extra details. The icon URL is highly recommended. If the user gets signed out of the application for any reason, the Remote Login provider will show the icon and business name as an option for signing into the system in addition to any other providers that might be enabled. The "Automate Validation Phase" option allows for setting up a remote to bypass the validation phase of signing in, which can be useful to enable for creating a seamless experience for a large company. This results in similar behavior to how namespace sign ins work.

At this point the remote is about halfway set up. The other half has to be set up on the remote end of things. See:

Setting Up a Remote Sign In

I separated that documentation out from this documentation so as to not overwhelm any developer you might need to send it to. This documentation page is extensive but they don't necessarily need to understand how this system works to integrate with it.

Using Custom API Keys

If you have entered the area of custom API keys, then you have reached a limitation of the core SSO server. Instead of working with custom API keys and endpoint hooks, it is highly recommended to find another approach to accomplishing the task. There are very few reasons to ever use a custom API key when other options almost always exist that will work better. Custom API keys should only be used as a last resort.

That said, creating a custom API key is doable but is a process that involves multiple steps:

Those are the basic guidelines for working with custom API keys. The best way to work with them is to crack open the relevant source code of the SSO server and client to understand what is going on. Help with working with custom API keys is limited and falls outside the general scope of support.

Creating a SSO Server Provider

Suppose a popular method of logging in is not already a part of the SSO server. This is where creating a new SSO server provider comes into play. This can be quite the undertaking and the simplest solution is probably to request it in the forums. That said, it is best to look at the source code to LDAP provider. That is about as simple as the average provider gets and it took me less than a day to create and test the LDAP provider. Whatever you do, don't look at the Generic Login provider source code or you'll have a heart attack and die. Generic Login is a very complex provider due to its flexibility.

There are three aspects to every SSO server provider: The configuration, the admin interface functions, and the user interface functions. The SSO server manages all aspects of loading and calling the correct functions at the appropriate times.

All SSO server providers are classes that derive from the base class 'SSO_ProviderBase'. The default functions in the base class don't do much of anything. Since there is a base class, not every function must be defined in the derived class.

To create a new provider, create a directory in the 'providers' directory with the name of the provider. Limit the characters of the name to lowercase letters and underscores. The 'sso_' prefix is reserved for official providers. Inside the new directory, create an 'index.php' file. The class name must be the same name as the directory (hence the restrictions on the directory name).

The rest of this section is a breakdown of each member function and what it is expected to do.

SSO_ProviderBase::Init()

Parameters: None.

Returns: Nothing.

This member function is expected to initialize the class settings in preparation for other calls. $sso_settings is guaranteed to at least contain a key-value pair of class name and an empty array. Most providers use this function as an opportunity to initialize an 'iprestrict' option with the results of a SSO_InitIPFields() call.

SSO_ProviderBase::DisplayName()

Parameters: None.

Returns: A translated string containing the name of the provider to display to a user.

This member function is expected to return a translated string that will be displayed to the user. This function is called both within the admin interface and the frontend - primarily for a selector when more than one provider is enabled.

SSO_ProviderBase::DefaultOrder()

Parameters: None.

Returns: An integer containing the default display order of the provider.

This member function is expected to return the default display order for the provider in relation to other providers when more than one is available/enabled. The default order can be overridden by changing the global configuration in the admin interface.

SSO_ProviderBase::MenuOpts()

Parameters: None.

Returns: An array containing 'name' and 'items' keys that map to a string and array of links to be displayed respectively.

This member function is expected to generate a set of items to display in the admin interface and a section name for the items. Most providers differentiate between users with 'sso_site_admin' and 'sso_admin' privileges here and show only relevant options to the user. The array returned is ordered by the display order before being included into the global $sso_menuopts array. URLs are generally generated with the SSO_CreateConfigURL() function.

SSO_ProviderBase::Config()

Parameters: None.

Returns: Nothing.

This member function is expected to take request inputs and generate a standard Admin Pack compliant interface. Be sure to check for permissions and errors before executing any command.

SSO_ProviderBase::IsEnabled()

Parameters: None.

Returns: A boolean of true if the user should be able to see the provider, false otherwise.

This member function is expected to run a series of tests to make sure that the provider is enabled for a specific user. Tests can range from checking for specific PHP functions and configuration settings to verifying that the user isn't coming from a spammer IP address.

SSO_ProviderBase::GetProtectedFields()

Parameters: None.

Returns: An array containing key-value pairs.

This member function is expected to return a mapping of SSO field names to a boolean value of whether the field is protected or not. Protected fields are not able to be modified by the user except possibly in the provider itself. The only provider that currently offers direct editing of protected fields is the Generic Login provider.

SSO_ProviderBase::GetEditUserLinks($id)

Parameters:

Returns: An array of links.

This member function is expected to return an array of links if it supports editing of protected fields. The $this->Config() function is expected to be able to handle the actual editing.

SSO_ProviderBase::AddIPCacheInfo()

Parameters: None.

Returns: Nothing.

This member function is expected to modify the global $contentopts to introduce additional IP address cache information when displaying information about an IP address. Primarily used by various Generic Login rate limiting modules.

SSO_ProviderBase::GenerateSelector()

Parameters: None.

Returns: Nothing.

This member function is expected to output HTML for a selector for the frontend. Only called when there is more than one enabled provider that the user can choose from.

SSO_ProviderBase::ProcessFrontend()

Parameters: None.

Returns: Nothing.

This member function is expected to perform all frontend tasks required before the user can proceed to the next step. A login system of some sort is generally implemented by this function. SSO_ActivateUser() moves the user to the next step.

Creating a Generic Login Module

The Generic Login provider supports modules. A module is a bit of logic that extends the Generic Login provider's default e-mail address, username, and password options. As bot authors adapt to changing circumstances and new ways are devised to keep them out of systems where they don't belong, new modules will need to be developed. A custom-built module has the added advantage of doing something no one else is doing, which generally results in fewer bots getting through.

Developing a Generic Login module is not a simple task. It is best to find existing modules that have aspects to them that are similar to what is desired and use them as a template for the new module.

Generic Login modules are not meant to provide additional SSO field mapping information. Adding first name, last name, etc. fields is not the purpose of the module architecture but rather to add new system defenses - including legal, which is why there is a Terms of Service module.

Anyway, all Generic Login modules are classes that derive from the base class 'sso_login_ModuleBase'. The official modules implement a private function called GetInfo() to retrieve the settings for the module, which is a good practice to follow. The default functions in the base class don't do much of anything. Since there is a base class, not every function must be defined in the derived class.

To create a new Generic Login module, create a PHP file inside the 'providers/sso_login/modules' directory. The name of the file before '.php' should consist of lowercase letters and underscores and will be used as the internal module name. The 'sso_' prefix is reserved for official modules. The class name must be 'sso_login_module_[modulename]'.

The rest of this section is a breakdown of each member function and what it is expected to do.

sso_login_ModuleBase::DefaultOrder()

Parameters: None.

Returns: An integer containing the field display order for the module or a boolean of false if the module has no publicly displayed fields.

This member function is expected to return the order of the module when displaying fields to the user. The Generic Login provider manages this setting and won't show an ordering option when false is returned.

sso_login_ModuleBase::ConfigSave()

Parameters: None.

Returns: Nothing.

This member function is expected to save the configuration changes for the module. BB_SetPageMessage() can set an information or error message for the user to see.

sso_login_ModuleBase::Config(&$contentopts)

Parameters:

Returns: Nothing.

This member function is expected to append fields to $contentopts["fields"] containing configuration options for the module. This function is called only if the module is enabled. This may also modify other parts of the array (e.g. 'htmldesc') to add links to a custom page such that $this->CustomConfig() is called.

sso_login_ModuleBase::CustomConfig()

Parameters: None.

Returns: Nothing.

This member function is expected to construct and display an entire page itself using BB_GeneratePage(). Not really necessary but available just in case. I originally was doing something with this and then ended up not needing it but left the logic in anyway.

sso_login_ModuleBase::CheckEditUserFields(&$userinfo)

Parameters:

Returns: Nothing.

This member function is expected to validate user input and save the user's information into the array if it validates. This function is called when submitting changes while editing a user in the Generic Login admin interface.

sso_login_ModuleBase::AddEditUserFields(&$contentopts, &$userinfo)

Parameters:

Returns: Nothing.

This member function is expected to append fields to $contentopts["fields"] when the user is being edited in the Generic Login admin interface.

sso_login_ModuleBase::AddFindUserOptions(&$contentopts, &$options, &$num)

Parameters:

Returns: Nothing.

This member function is expected to append search fields to $contentopts["fields"] and any extra output options to the $options array. This function lets a module add extra search fields and output options to the Generic Login search page to find users. The sequence of steps for each new field must be to check if $num is the third entry in the current row and to append "startrow" if it is, then append the field, and finally increment $num.

sso_login_ModuleBase::AddFindUserCols(&$cols)

Parameters:

Returns: A boolean of true if the function added columns, otherwise false.

This member function is expected to append a column name to the input array if the user requested that column to be displayed. If false is returned, the module will be excluded from handling row information later.

sso_login_ModuleBase::AddFindUserRowInfo(&$result, &$userinfo)

Parameters:

Returns: Nothing.

This member function is expected to append the columns the user requested to the row using the current user information. This function is only called by the Generic Login search enging if AddFindUserCols() returned true. This function should be prepared to handle mangled data due to changes in the settings over time.

sso_login_ModuleBase::IsAllowed()

Parameters: None.

Returns: A boolean of true if the user is allowed to see the Generic Login provider, false otherwise.

This member function is not used by the official modules but can be used to hide the Generic Login provider. The default return value is true. This was originally going to be used in the rate limiting module, but that changed during development to be more user-friendly.

sso_login_ModuleBase::AddProtectedFields(&$result)

Parameters:

Returns: Nothing.

This member function is expected to set a mapping of SSO field names to a boolean value of whether a field is protected or not. This function exists for future development purposes and should not be used for current modules.

sso_login_ModuleBase::AddIPCacheInfo($displayname)

Parameters:

Returns: Nothing.

This member function is expected to modify the global $contentopts to introduce additional IP address cache information when displaying information about an IP address. Primarily used by various rate limiting modules to show a user's current rate limiting status.

sso_login_ModuleBase::ModifyEmail($userinfo, &$htmlmsg, &$textmsg)

Parameters:

Returns: Nothing.

This member function is expected to modify an e-mail. This allows a module to add extra string replacement tokens to e-mail messages sent to the user. An example of this is the anti-phishing module with the @ANTIPHISH@ string replacement token.

sso_login_ModuleBase::TwoFactorCheck(&$result, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to check a two-factor authentication code and add an error message if the check fails. The member function may check the value of $userinfo["two_factor_method"] to see if the module is supposed to handle it or not.

sso_login_ModuleBase::TwoFactorFailed(&$result, $userinfo)

Parameters:

Returns: Nothing.

This member function is lets a module act on failures of two-factor authentications. Primarily used by the Rate Limiting module to only allow a certain number of failures before requiring the user to sign in again.

sso_login_ModuleBase::SignupCheck(&$result, $ajax, $admin)

Parameters:

Returns: Nothing.

This member function is expected to check the user's input for validity and append any messages to be displayed to the $result array. This function is called during the Generic Login signup process. The $ajax parameter can be used to take a slightly different path through the code because, generally, only one field is being checked when AJAX is used on a signup form. Modules that support both SignupCheck() and UpdateInfoCheck() generally call an internal function that handles both calls.

sso_login_ModuleBase::SignupAddInfo(&$userinfo, $admin)

Parameters:

Returns: Nothing.

This member function is expected to add information to the user information array based on user input. This function is called during the Generic Login signup process.

sso_login_ModuleBase::SignupDone($userid, $admin)

Parameters:

Returns: Nothing.

This member function lets a module act on a newly created user in the Generic Login provider database.

sso_login_ModuleBase::GetTwoFactorName()

Parameters: None.

Returns: A boolean of false or a string to display to the user representing the two-factor authentication mechanism that the module supports.

This member function lets the Generic Login provider know which modules offer two-factor authentication capabilities and presents the options to the user in various dropdowns.

sso_login_ModuleBase::GenerateSignup($admin)

Parameters:

Returns: Nothing.

This member function is expected to generate fields for the user to view or fill in. Can also output Javascript. Fields should be wrapped using standard 'div' wrapping. See existing modules on how to do this correctly. Output is cached and reordered according to the module ordering specified in the admin interface.

sso_login_ModuleBase::VerifyCheck(&$result)

Parameters:

Returns: Nothing.

This member function is expected to check settings and append any errors or warnings. Called by the Generic Login provider when verifying an account. Primarily used by the rate limiting module.

sso_login_ModuleBase::InitMessages(&$result)

Parameters:

Returns: Nothing.

This member function is expected to examine $_REQUEST["sso_msg"] and append any messages to display to the user. Primarily used for redirection message management.

sso_login_ModuleBase::LoginCheck(&$result, $userinfo, $recoveryallowed)

Parameters:

Returns: Nothing.

This member function is expected to check the login information and act accordingly. Error and warnings to display to the user are appended to the relevant section of the $result array. LoginCheck() is called twice - once with $userinfo set to false and then again with an array of user information if the first check didn't encounter any problems.

sso_login_ModuleBase::SendTwoFactorCode(&$result, $userrow, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to send the two-factor authentication code to the user. sso_login::GetTimeBasedOTP() is typically used to generate the time-based one-time password that is sent.

sso_login_ModuleBase::LoginAddMap(&$mapinfo, $userrow, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to append SSO field mapping data based on user information and configuration. It is also a good place to do last minute activities such as set browser cookies. The SSO field options exist for future development purposes and should not be used for current modules.

sso_login_ModuleBase::GenerateLogin($messages)

Parameters:

Returns: Nothing.

This member function is expected to generate fields for the user to view or fill in. Can also output Javascript. Fields should be wrapped using standard 'div' wrapping. See existing modules on how to do this correctly. Output is cached and reordered according to the module ordering specified in the admin interface.

sso_login_ModuleBase::IsRecoveryAllowed($allowoptional)

Parameters:

Returns: A boolean of true if the module offers a recovery option, false otherwise.

This member function is expected to return whether or not it supports account recovery under the specified conditions. For example, the SMS recovery module is an optional convenience for users so, under the username only installation method, changing some information on an account would be impossible if the user didn't specify an SMS recovery phone number during account creation.

sso_login_ModuleBase::AddRecoveryMethod($method)

Parameters:

Returns: Nothing.

This member function is expected to output a valid select option for the module's account recovery support.

sso_login_ModuleBase::RecoveryCheck(&$result, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to check the recovery information and act accordingly. Error and warnings to display to the user are appended to the relevant section of the $result array. RecoveryCheck() is called twice - once with $userinfo set to false and then again with an array of user information if the first check didn't encounter any problems.

sso_login_ModuleBase::RecoveryDone(&$result, $method, $userrow, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to handle setting up for and going to the second account recovery step. Also called for the 'email' recovery method. This is the last chance to bail with an error message and let the user select a different account recovery method.

sso_login_ModuleBase::GenerateRecovery($messages)

Parameters:

Returns: Nothing.

This member function is expected to generate fields for the user to view or fill in. Can also output Javascript. Fields should be wrapped using standard 'div' wrapping. See existing modules on how to do this correctly. Output is cached and reordered according to the module ordering specified in the admin interface.

sso_login_ModuleBase::RecoveryCheck2(&$result, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to check the second step recovery information and act accordingly. Error and warnings to display to the user are appended to the relevant section of the $result array. RecoveryCheck2() is called twice - once with $userinfo set to false and then again with an array of user information if the first check didn't encounter any problems.

sso_login_ModuleBase::GenerateRecovery2($messages)

Parameters:

Returns: Nothing.

This member function is expected to generate fields for the user to view or fill in. Can also output Javascript. Fields should be wrapped using standard 'div' wrapping. See existing modules on how to do this correctly. Output is cached and reordered according to the module ordering specified in the admin interface.

sso_login_ModuleBase::UpdateInfoCheck(&$result, $userinfo, $ajax)

Parameters:

Returns: Nothing.

This member function is expected to check the user's input for validity and append any messages to be displayed to the $result array. This function is called during the Generic Login update information process after going through the account recovery process. The $ajax parameter can be used to take a slightly different path through the code because, generally, only one field is being checked when AJAX is used on a signup form. Modules that support both SignupCheck() and UpdateInfoCheck() generally call an internal function that handles both calls.

sso_login_ModuleBase::UpdateAddInfo(&$userinfo)

Parameters:

Returns: Nothing.

This member function is expected to add information to the user information array based on user input. This function is called during the Generic Login update information process after going through the account recovery process.

sso_login_ModuleBase::UpdateInfoDone($userid)

Parameters:

Returns: Nothing.

This member function lets a module act on a user account that has been changed in the Generic Login provider database.

sso_login_ModuleBase::GenerateUpdateInfo($userrow, $userinfo)

Parameters:

Returns: Nothing.

This member function is expected to generate fields for the user to view or fill in so they can update their information. Can also output Javascript. Fields should be wrapped using standard 'div' wrapping. See existing modules on how to do this correctly. Output is cached and reordered according to the module ordering specified in the admin interface.

Import Existing User Accounts

Let's suppose you already have a large database of users and want to import them into the SSO server. While this is possible, this is a fairly advanced task and you are somewhat on your own as far as programming goes. There is some code later on in this section to give you an idea of how to proceed and the forums can be helpful for getting on the right track, but you'll still ultimately have to do your own thing.

There are several approaches you can take when dealing with the issue of importing users. You'll have to first decide which one is right for your situation.

The rest of this section is dedicated to importing user accounts into the Generic Login provider.

The Generic Login provider is quite versatile but it is also hard to integrate with because of both its flexibility and the security measures taken to prevent a data breach. This is intentional but it does make it difficult to import accounts from other systems into this provider. The recommended approach for importing large numbers of accounts in one go is to write a command-line script. The following is an example to get you started. The code is borrowed from both 'cron.php' and the Generic Login provider:

<?php
	define("SSO_FILE", 1);

	// Temporary root.
	$rootpath = str_replace("\\", "/", dirname(__FILE__));

	require_once $rootpath . "/config.php";
	require_once SSO_ROOT_PATH . "/" . SSO_SUPPORT_PATH . "/debug.php";
	require_once SSO_ROOT_PATH . "/" . SSO_SUPPORT_PATH . "/str_basics.php";
	require_once SSO_ROOT_PATH . "/" . SSO_SUPPORT_PATH . "/page_basics.php";
	require_once SSO_ROOT_PATH . "/" . SSO_SUPPORT_PATH . "/sso_functions.php";
	require_once SSO_ROOT_PATH . "/" . SSO_SUPPORT_PATH . "/random.php";

	// Initialize language settings.
	BB_InitLangmap(SSO_ROOT_PATH . "/" . SSO_LANG_PATH . "/", SSO_DEFAULT_LANG);
	BB_SetLanguage(SSO_ROOT_PATH . "/" . SSO_LANG_PATH . "/", SSO_ADMIN_LANG);

	// Initialize the global CSPRNG instance.
	$sso_rng = new CSPRNG();

	// Connect to the database and generate database globals.
	SSO_DBConnect(true);

	// Load in fields without admin select.
	SSO_LoadFields(false);

	// Load in $sso_settings and initialize it.
	SSO_LoadSettings();

	// Load the SMTP functions so e-mail addresses can be verified.
	define("CS_TRANSLATE_FUNC", "BB_Translate");
	require_once SSO_ROOT_PATH . "/" . SSO_SUPPORT_PATH . "/smtp.php";

	// Define SET_PASSWORD_MODE to one of the following:
	//   0 = Specify the user's password via $password.
	//   1 = Generate a password for the user.
	//   2 = Force the user to use account recovery options to set a password.
	define("SET_PASSWORD_MODE", 2);

	// Connect to your database here and run the query to extract user accounts.
	$numrows = 0;
	$result = $yourdb->query("SELECT * FROM yourusers");
	while ($row = $result->getrow())
	{
		// Put your code here to get the username, e-mail, and (optional) password out of your database row.
		$username = $row->username;
		$email = $row->email;
		$password = "";

		// Load up $mapinfo with field data.  Keys must match field names in the server.
		// Don't worry about e-mail address and username.  Those are dealt with later.
		$mapinfo = array();

		// Do not modify anything below this line unless you really know what you are doing.
		if ($sso_settings["sso_login"]["install_type"] == "email_username" || $sso_settings["sso_login"]["install_type"] == "email")
		{
			$result2 = SMTP::MakeValidEmailAddress($email);
			if (!$result2["success"])
			{
				echo BB_Translate("Invalid e-mail address.  %s\n", $email["error"]);
				continue;
			}

			$email = $result2["email"];
		}

		// Create the new user in the Generic Login database.
		$userinfo = array();
		$phrase = "";
		for ($x = 0; $x < 4; $x++)  $phrase .= " " . SSO_GetRandomWord();
		$phrase = preg_replace('/\s+/', " ", trim($phrase));
		if (SET_PASSWORD_MODE == 0)  $phrase = $password;

		$salt = $sso_rng->GenerateString();
		$data = $username . ":" . $email . ":" . $salt . ":" . $phrase;
		$userinfo["extra"] = $sso_rng->GenerateString();
		if (SET_PASSWORD_MODE == 0 || SET_PASSWORD_MODE == 1)
		{
			$passwordinfo = Blowfish::Hash($data, $sso_settings["sso_login"]["password_minrounds"], $sso_settings["sso_login"]["password_mintime"]);
			if (!$passwordinfo["success"])  BB_SetPageMessage("error", "Unexpected cryptography error.");
			else
			{
				$userinfo["salt"] = $salt;
				$userinfo["rounds"] = (int)$passwordinfo["rounds"];
				$userinfo["password"] = bin2hex($passwordinfo["hash"]);

				echo BB_Translate("Initial password for '%s' - '%s' has been set to '%s'.\n", $username, $email, $phrase);
			}
		}
		else
		{
			$userinfo["salt"] = "";
			$userinfo["rounds"] = 0;
			$userinfo["password"] = "";
		}

		$sso_db_sso_login_users = SSO_DB_PREFIX . "p_sso_login_users";
		$userinfo2 = SSO_EncryptDBData($userinfo);

		try
		{
			if ($sso_settings["sso_login"]["install_type"] == "email_username")
			{
				$sso_db->Query("INSERT", array($sso_db_sso_login_users, array(
					"username" => $username,
					"email" => $email,
					"verified" => (int)$verified,
					"created" => CSDB::ConvertToDBTime(time()),
					"info" => $userinfo2,
				), "AUTO INCREMENT" => "id"));
			}
			else if ($sso_settings["sso_login"]["install_type"] == "email")
			{
				$sso_db->Query("INSERT", array($sso_db_sso_login_users, array(
					"email" => $email,
					"verified" => (int)$verified,
					"created" => CSDB::ConvertToDBTime(time()),
					"info" => $userinfo2,
				), "AUTO INCREMENT" => "id"));
			}
			else if ($sso_settings["sso_login"]["install_type"] == "username")
			{
				$sso_db->Query("INSERT", array($sso_db_sso_login_users, array(
					"username" => $username,
					"created" => CSDB::ConvertToDBTime(time()),
					"info" => $userinfo2,
				), "AUTO INCREMENT" => "id"));
			}
			else
			{
				echo BB_Translate("Fatal error:  Login system is broken.\n");
				exit();
			}

			$userid = $sso_db->GetInsertID();

			$userrow = $sso_db->GetRow("SELECT", array(
					"*",
					"FROM" => "?",
					"WHERE" => "id = ?",
				), $sso_db_sso_login_users, $userid);
		}
		catch (Exception $e)
		{
			echo BB_Translate("Database query error.  %s\n", $e->getMessage());
			continue;
		}

		// Activate the user.
		if ($sso_settings["sso_login"]["install_type"] == "email_username" || $sso_settings["sso_login"]["install_type"] == "email")  $mapinfo[$sso_settings["sso_login"]["map_email"]] = $userrow->email;
		if ($sso_settings["sso_login"]["install_type"] == "email_username" || $sso_settings["sso_login"]["install_type"] == "username")  $mapinfo[$sso_settings["sso_login"]["map_username"]] = $userrow->username;

		SSO_ActivateUser($userrow->id, $userinfo["extra"], $mapinfo, false, false);

		$numrows++;
	}
?>

The code is fairly well written with comments. It should be easy to use it as a good baseline starting point and make the necessary modifications to integrate with an existing system to import and activate accounts. This script is intended to be run from the SSO server root from a command-line.

Porting the SSO Client

The SSO server is written in PHP and likely won't be ported to another language. It is big, complex, and has a lot of PHP-specific stuff in it and is intended to be installed standalone. The SSO client, however, is intended to be lightweight and relatively easy to port to other languages. This section aims to be a guide to porting the SSO client.

The SSO client has four primary responsibilities: Staying out of the way of someone integrating with it (i.e. avoiding naming conflicts), exposing classes and functions to the user that make sense, communicating with the SSO server endpoint and correctly processing replies including dealing with server outages, and managing everything in a secure fashion while being bandwidth-friendly (e.g. encrypting certain cookies while minimizing cookie length).

In theory, the SSO client can be ported by following a similar naming convention to the existing PHP code and doing a one-to-one port. However, each language is different and nice language features like "optional parameters" or "passing arrays/maps as a parameter" may not be possible or behave differently. Try to maintain similarities when naming variables and functions as much as possible.

The most difficult part of porting the SSO client will be communication with the SSO server. The endpoint expects communication to be single or dual encrypted using either the Blowfish or AES-256 cipher all with a specialized packet interface. Both the server and client know the shared secret used to encrypt the underlying data, which contains information on which encryption method is expected. You will either need to use native cipher implementations (rare) or find or write a library with the correct implementation and then write a couple of packet wrapper routines that perform identically to the PHP encrypt/decrypt packet routines. You will also need a JSON encoder and decoder, which any rational, modern, competent language has. You will also need to create a way to obtain secure random bytes of data and may want to port part of the CSPRNG that comes with the SSO client to make life easier for the rest of the port.

Endpoint API

The SSO server endpoint serves as the initiator of all things related to the SSO server and the only method by which a SSO client can use to talk directly to the SSO server. The endpoint API is an important aspect of all SSO server related communication. This section is really only relevant to those implementing custom endpoint API extensions to the SSO server and those porting the SSO client to other languages.

Access to the endpoint API is, by default, restricted to those who know the endpoint API URL, have a valid API key, and are accessing the endpoint API from a matching IP address according to the API key's IP address pattern restrictions. The endpoint also supports hooks in a couple of locations but is fairly limited. The actions listed below are reserved for the SSO server and can't be used in the EndpointHook_CustomHandler() callback.

The endpoint API will return errors to the client whenever a situation is encountered that is deemed a failure condition. Most error messages are encrypted but it is possible to get back an unencrypted response (e.g. an invalid API key or the wrong version of the SSO client is being used). The SSO client is responsible for handling both scenarios. All communication is done with encoded JSON objects.

Once an API key has been validated and loaded, the next step is to locate the correct 'action', verify that the API key is allowed to perform that action, and then execute the action. What follows is the list of available endpoint API actions.

Action: 'test'

API key types: normal, remote, custom

Inputs: None

Returns:

This action is used by the SSO client installer to verify that the endpoint API URL and API key and secret are working as expected. Diagnosing these issues post-installation is a bit more difficult.

Action: 'initlogin'

API key type: normal

Inputs:

Returns:

This action is used by the SSO client to initiate a sign in. The SSO client is expected to save the recovery ID to retrieve 'info' later and then redirect the browser to the returned URL.

Action: 'setlogin'

API key type: remote

Inputs:

Returns:

This action is used by the SSO client to authorize a remote sign in. The SSO client is expected to redirect the browser to the returned URL. See the remote provider for details.

Action: 'getlogin'

API key type: normal

Inputs:

Returns:

This action retrieves user sign in information and request recovery information that was sent when the 'initlogin' action was called. When 'delete_old' is specified, the returned object only contains 'success' and is intended to be executed shortly after the first request returns so that the original session information is deleted from the SSO server. When 'sso_id2' and 'rid' are specified, the original recovery data is returned via 'rinfo' and the real session ID via 'sso_id'. The SSO client is responsible for restoring the state of the application as best as possible to the original state so that processing may continue where it left off when the original redirect happened to avoid data loss since data loss results in frustrated users.

Ideally, this action is called twice by the SSO client: The first call is to retrieve the user's sign in information and application recovery information. The second call is to delete the original session off the server and therefore secure the sign in. Two steps are necessary because a correctly written SSO client will try an operation multiple times to counteract any server communication failures because failures do happen. The SSO client should never reveal the real session ID across the wire.

Action: 'logout'

API key type: normal

Inputs:

Returns:

This action signs out the user from the SSO server across all sign ins within the same namespace as the session specified by sso_id.

SSO Server/Client Reserved Variables

To minimize naming conflicts, the SSO server and client have very specific naming conventions so as to not interfere with the proper operation of applications, each other, and the various providers. There is a lot to the core system, so avoiding conflicts is a bit difficult.

This section of documentation deviates a bit from the method I used for Barebones CMS and the rules for the naming convention are a bit more lax.

First up is the '$sso_' variable name prefix. This prefix is reserved for official SSO server and client use. Do not use the prefix for global variable names for providers. Do not access these variables directly from a SSO client except if you need to display the contents to debug some specific issue.

Next is the '$bb_' variable name prefix. The SSO server utilizes Admin Pack, so this prefix is also reserved.

When creating global variable names for providers, prefix them with '$g_' for throwaway variables - that is, you don't care if another provider replaces it later. If you need a permanent global, use a '$g_providername_' prefix. In general, correctly written providers will not need global variables.

Both the SSO server and client have a set of define()'d constants. These are carefully constructed so they won't conflict with each other as well as third-party applications such that there is seamless integration. The constants are defined in 'config.php', which is generated by each installer.

What follows is a breakdown of each '$sso_' prefix variable in the SSO server, its type, and what it is for:

Since Admin Pack is in use, standard Admin Pack variables are also available but mostly for internal use only. The only exception is $bb_usertoken documented above.

The list of globals above are not exhaustive but the listed variables should be sufficient enough coverage for nearly all development purposes.

What follows is a breakdown of the '$sso_' prefix variable in the SSO client, its type, and what it is for:

With the sole exception of $sso_removekeys, these variables are for internal use only and should never be used by an application except for debugging issues. The names used by the SSO client might be modified in the future to help make them more unique since there is a chance some application out there might use them, the SSO client would replace those variables, and the universe would subsequently implode.

SSO Server Functions

The SSO server has a number of functions available that make common tasks easier. Since the SSO server uses Admin Pack, all the functions for Admin Pack are available in addition to the server functions. The SSO server also uses a number of other baseline Barebones CMS components in the 'support' directory that are documented in the Extra Components documentation.

Most functions begin with the prefix of 'SSO_'. This is a reserved prefix.

The available functions below are broken down by the file they are defined in. If the file is not specified, the function appears in 'support/sso_functions.php' Without further ado, here are the SSO server functions:

admin.php: SSO_CreateConfigURL($action2, $extra = array())

Parameters:

Returns: A string containing a URL to the target configuration page.

Defined in 'admin.php'. This function is for a provider to easily generate a URL that gets to the correct page of the provider configuration.

admin.php: SSO_CreateConfigLink($title, $action2, $extra = array(), $confirm = "")

Parameters:

Returns: A string containing a hyperlink to the target configuration page. Includes surrounding 'a' tags.

Defined in 'admin.php'. This function is similar to SSO_CreateConfigURL() but surrounds the link in HTML 'a' tags.

admin.php: SSO_ConfigRedirect($action2, $extra = array(), $msgtype = "", $msg = "")

Parameters:

Returns: Nothing.

Defined in 'admin.php'. This function is similar to SSO_CreateConfigURL() but performs a redirect to the URL with an optional Admin Pack message.

endpoint.php: SSO_EndpointOutput($result)

Parameters:

Returns: Nothing.

Defined in 'endpoint.php'. This function outputs the $result in JSON format, possibly encrypted using the shared secret, then exits.

endpoint.php: SSO_EndpointError($msg, $info = "")

Parameters:

Returns: Nothing.

Defined in 'endpoint.php'. This function outputs an error result using the SSO_EndpointOutput() function.

index.php: SSO_DisplayError($msg)

Parameters:

Returns: Nothing.

Defined in 'index.php'. This function outputs the error message and exits.

index.php: SSO_OutputHeartbeat()

Parameters: None.

Returns: Nothing.

Defined in 'index.php'. This function outputs the Javascript logic for the AJAX heartbeat that keeps the temporary server session alive for the maximum configured amount of time. Without the heartbeat, the temporary session usually expires in 30 minutes.

index.php: SSO_FrontendField($name)

Parameters:

Returns: A string containing a hashed name based on a random seed, session ID, API key, and $name.

Defined in 'index.php'. This function takes the usual human-readable name and turns it into a hashed name. Designed for use in forms that users fill out. Nearly transparent to the average user but introduces a serious problem for bots.

This function introduces minor hash collision issues. It is theoretically possible that two different $name values will hash to the same result for the same session ID and API key.

index.php: SSO_FrontendFieldValue($name, $default = false)

Parameters:

Returns: A UTF-8 encoded string if the hashed name exists in $_REQUEST, otherwise $default.

Defined in 'index.php'. This function locates the SSO_FrontendField() hashed name in $_REQUEST and returns it if it exists, otherwise it returns the value of $default. A convenient function to deal with retrieving data from a submitted form that used SSO_FrontendField() to generate names.

SSO_IsSSLRequest()

Parameters: None.

Returns: A boolean of true if the browser is loading the page via SSL, false otherwise.

This function is a clone of BB_IsSSLRequest() for the endpoint to utilize in order to keep RAM usage down a bit.

SSO_GetRemoteIP()

Parameters: None.

Returns: An array containing normalized IP address information.

This function normalizes the incoming IP address for various operations such as checking whitelists and DNSRBLs. This is the result of a IPAddr::GetRemoteIP() call that includes any trusted proxies.

SSO_GetSupportedDatabases()

Parameters: None.

Returns: An array containing information about supported databases.

This function is used by the installer and SSO_DBConnect() to determine allowed database types and the generic features that those databases support. The returned array will generally mirror supported CSDB databases.

SSO_DBConnect($full)

Parameters:

Returns: Nothing but throws an exception on error.

This function initializes the database connection and the various '$sso_db_...' global variables to be able to run queries against the database. The admin, cron, install, and upgrade code loads the full CSDB database class. The endpoint and frontend load the 'lite' version for reduced memory usage.

SSO_LoadFields($loadselect)

Parameters:

Returns: A boolean of true if it succeeds, false otherwise.

This function loads in the enabled fields into $sso_fields as well as the encryption status of each field. If $loadselect is true, $sso_select_fields is set with the options for the SSO field mapping dropdowns used by providers.

SSO_GenerateNamespaceKeys()

Parameters: None.

Returns: Nothing.

This function resets the namespace keys and initialization vectors. The caller still has to call SSO_SaveSettings() to apply the changes.

SSO_LoadSettings()

Parameters: None.

Returns: Nothing.

This function loads in the SSO server settings array and sets defaults if necessary.

SSO_CreatePHPStorageData($data)

Parameters:

Returns: A string that is ready to be stored in a .php file assigned to a variable.

This function prepares variables to save them to a file. It uses the SSO_USE_LESS_SAFE_STORAGE configuration item to determine how to generate the data. When less safe storage is disabled, data is serialized and then base64 encoded. When less safe storage is enabled, data is var_export()'ed. Both methods perform about the same but the latter is slightly less secure but readability is vastly improved.

SSO_SaveSettings()

Parameters: None.

Returns: A boolean of true if the settings were saved successfully, false otherwise.

This function saves the SSO server settings.

SSO_GetProviderList()

Parameters: None.

Returns: An array containing internal provider names.

This function reads the list of available providers and returns the directory names. This is only preliminary information, it is up to the caller to do the actual loading, initialization, and perform various checks.

SSO_GetDirectoryList($path)

Parameters:

Returns: An array of two arrays of subdirectories ("dirs") and files ("files") within the directory specified by $path.

This function retrieves a list of subdirectories and files within the specified directory and sorts them with natcasesort(). Subdirectories go into one array of the array that is returned and is called "dirs". Files go into another array and is called "files".

SSO_RandomSleep()

Parameters: None.

Returns: Nothing.

This function sleeps for a random amount of time up to 250 milliseconds for a timing attack defense and forces all clients to take longer per request.

SSO_AddSortedOutput(&$outputmap, $numkey, $strkey, $data)

Parameters:

Returns: Nothing.

This function is used to order a collection of HTML output that has been captured and stored until the output has been sorted.

SSO_DisplaySortedOutput($outputmap)

Parameters:

Returns: Nothing.

This function sorts and outputs the array contents.

SSO_IsField($name)

Parameters:

Returns: A boolean of true if the field name exists, false otherwise.

This function checks to see if $name is in the $sso_fields. During development, this function used to do a lot more, then it was drastically simplified at some point.

SSO_SaveIPAddrInfo()

Parameters: None.

Returns: A boolean of true if the $sso_ipaddr_info array was saved successfully, false otherwise.

This function is intended to be called after $sso_ipaddr_info has been modified.

SSO_GetGeoIPOpts()

Parameters: None.

Returns: An array of key-value pairs where the key is an internal GeoIP name and value is a boolean of true.

This is an internal function used by other Geolocation IP address functions.

SSO_InitIPFields()

Parameters: None.

Returns: An array initialized for use with other Geolocation IP address functions.

This function is intended to be used in the Init() function of a provider's 'iprestrict' configuration settings.

SSO_ProcessIPFields($full = false)

Parameters:

Returns: An array containing Geolocation IP address information.

This function validates the inputs from SSO_AppendIPFields() and returns an array of Geolocation IP information to the caller.

SSO_AppendIPFields(&$contentopts, $info, $full = false)

Parameters:

Returns: Nothing.

This function appends standard Geolocation IP address configuration fields to $contentopts. Fields are validated with SSO_ProcessIPFields().

SSO_GenerateSearchOutputCheckbox($name, $checked)

Parameters:

Returns: A string containing a HTML 'input' element with clickable text that says 'Include in output'.

This function generates a custom checkbox for use with the 'htmldesc' option in search forms in the admin interface.

SSO_GetGeoIPInfo()

Parameters: None.

Returns: An array containing the location information for the remote IP address if a GeoIP database exists, a boolean of false otherwise.

This function looks up the remote IPv6 address in a GeoIP database and returns the information to the caller.

SSO_IsSpammer($info)

Parameters:

Returns: A boolean of true if the IP address is a known spammer, false otherwise.

This function checks DNSRBL database records and GeoIP location blacklists for both the global configuration and a provider configuration against the remote IP address. The results are cached so that the queries against the various databases and services are minimized. Information is cached for the specified length of time determined by the configuration.

SSO_IsIPAllowed($info)

Parameters:

Returns: A boolean of true if the remote IP address passes all whitelist filters, false otherwise.

This function checks the global IP address whitelist as well as any provider or API key filter. Note that IP addresses can be spoofed, so this isn't flawless protection.

SSO_GetNISTNumBits($password, $repeatcalc = false)

Parameters:

Returns: A numeric value representing the number of bits of strength present according to the NIST entropy calculation recommendations.

This function calculates password strength according to NIST rules. When $repeatcalc is true, alternate non-NIST rules are used that reduces the strength of repeat characters by 25% every time a character is used.

As of SSO Server/Client 3.0, this function has been renamed to GetNISTNumBits() and relocated to the Password Requirements module.

SSO_IsStrongPassword($password, $minbits = 18, $usedict = false, $minwordlen = 4)

Parameters:

Returns: A boolean of true if the password exceeds the $minbits threshold, false otherwise.

This function checks a password against a large set of rules that users have come up with over time that create really bad passwords. At the 18 bits of entropy level with dictionary checks, 99% of all bad passwords are rejected outright.

As of SSO Server/Client 3.0, this function has been renamed to IsStrongPassword() and relocated to the Password Requirements module.

SSO_GetRandomWord($randcapital = true, $words = array())

Parameters:

Returns: A string containing a randomly selected word.

When $words is empty (the default), a random dictionary word is chosen. If $words contains a set of words, one of the words in the array is chosen. The Generic Login anti-phishing module utilizes the latter when constructing a random sentence.

SSO_SaveSessionInfo()

Parameters: None.

Returns: A boolean of true is the session information was successfully saved, false otherwise.

This function is intended to be called after modifying $sso_session_info.

SSO_SendEmail($fromaddr, $toaddr, $subject, $htmlmsg, $textmsg)

Parameters:

Returns: A boolean of true if the e-mail was successfully sent, false otherwise.

This function sends a simple e-mail message using the Ultimate E-mail Toolkit.

SSO_EncryptDBData($data)

Parameters:

Returns: A string containing the serialized, single/dual encrypted (Blowfish or AES-256), and base64 encoded data.

This function prepares encrypted data to be used in a SQL query.

SSO_DecryptDBData($data)

Parameters:

Returns: A string containing the base64 decoded, single/dual decryptyed (Blowfish or AES-256), and unserialized data on success, a boolean of false otherwise.

This function decrypts data from the database.

SSO_LoadDecryptedUserInfo($row)

Parameters:

Returns: An array containing both the decrypted and regular user information.

This function loads the unencrypted user information field and then decrypts the encrypted user information field into a single array of information from a row containing both 'info' and 'info2' object variables and returns the combined array to the caller.

SSO_CreateEncryptedUserInfo(&$userinfo)

Parameters:

Returns: A string containing encrypted user information ready for a SQL query.

This function checks $sso_fields for the encryption status of the field and moves fields that are supposed to be encrypted out of $userinfo and into a separate array. Then the separate array is encrypted and returned to the caller. The net effect is that unencrypted fields are left alone in the $userinfo array such that both sets of data are ready for a SQL query.

SSO_AddGeoIPMapFields(&$info)

Parameters:

Returns: Nothing.

This internal function looks at the global Geolocation IP address to SSO field mappings and writes the information to the user information array based on the remote IP address' cached information.

SSO_IsLockedUser($id)

Parameters:

Returns: A boolean of true if the user account is locked, false otherwise.

This function checks to see if a user account is locked.

SSO_ActivateUserSession($row, $automate)

Parameters:

Returns: A boolean of false if activation fails. The browser is redirected on success.

This function activates a user and then proceeds to the next step (validation).

SSO_ActivateNamespaceUser()

Parameters: None.

Returns: A boolean of false if the namespace is invalid or activation fails. The browser is redirected on success.

This function locates the namespace of the current API key, looks for an existing session in the same namespace, and activates a new session if a match is found.

SSO_ActivateUser($id, $entropy, $info, $automate = false, $activatesession = true)

Parameters:

Returns: A boolean of false if activation fails. The browser is redirected on success.

This function activates a user and then proceeds to the next step (validation). Activating a user either inserts a new record in the database or updates an existing record with the information from the provider.

The $activatesession variable is only used from within the SSO server admin where it makes sense to create an account but not sign in to it. This option is used primarily for the Generic Login provider when creating new accounts within the SSO server admin.

SSO_SetUserVersion($version)

Parameters:

Returns: A boolean of true if the version was successfully set, false otherwise.

This function sets the version of the user account as well as saving any changes to the $sso_user_info array.

SSO_ExternalRedirect($url)

Parameters:

Returns: Nothing.

This function outputs HTML that causes the browser to redirect to the URL. Javascript appears first for those browsers that have Javascript enabled and a meta refresh of three seconds is used for Javascript disabled scenarios. This function is called wherever it becomes possible to exceed browser internal redirection limits (e.g. Internet Explorer will give up after 10 redirects). This function bypasses those limits.

SSO_ValidateUser()

Parameters: None.

Returns: A boolean of false if validation fails. The browser is redirected on success.

This function validates the session, clears temporary SSO server cookies, sets the namespace session to the new session ID, and redirects the browser back to the SSO client with a fully validated session.

SSO Client Class and Functions

The SSO client is a class that is creatively named "SSO_Client". If you prefer working with functions instead, a single class instance ($sso__client) and a set of wrapper functions are available via 'client/functions.php'. The SSO client also uses a number of other baseline Barebones CMS components in the 'support' directory that are documented in the Extra Components documentation. A couple of the extra components have been made conflict-free by prefixing the function and class names with 'SSO_' because they are required for proper operation of the SSO client.

Most classes and functions begin with the prefix of 'SSO_'. This is a reserved prefix.

Without further ado, here are the SSO client class member functions:

SSO_Client::SendRequest($action, $options, $endpoint, $apikey, $secretkey)

Parameters:

Returns: An array containing status and results of the query.

This member function is the workhorse behind how the SSO client communicates with a SSO server endpoint. In general, there should be no need to call this function directly.

SSO_Client::GetRemoteIP()

Parameters: None.

Returns: An array containing normalized IP address information.

This static function normalizes the incoming IP address for various operations such as communicating with the SSO server. This is the result of a SSO_IPAddr::GetRemoteIP() call that includes any trusted proxies.

SSO_Client::ProcPOSTStr($data)

Parameters:

Returns: A string that is trim()'ed and magic quote free.

This private member function is called by SSO_ProcessSingleInput() to clean up strings so there is no surrounding whitespace and no magic quotes (if enabled).

SSO_Client::ProcessSingleInput($data)

Parameters:

Returns: Nothing.

This private member function is called by SSO_ProcessAllInput() to clean up a PHP superglobal and overwrite existing values in $sso_request.

SSO_Client::ProcessAllInput()

Parameters: None.

Returns: Nothing.

This private member function processes and filters $_COOKIE, $_GET, and $_POST into the $sso_request global. This function allows $_GET and $_POST to override any $_COOKIE variables that were set of the same name (useful for SWFUpload), trim()'s user input, and removes magic quotes.

SSO_Client::SetCookieFixDomain($name, $value = "", ...)

Parameters:

Returns: Nothing.

This member function is identical to the Extra Components SetCookieFixDomain() except it updates any superglobals.

SSO_Client::IsSSLRequest()

Parameters: None.

Returns: A boolean of true if the browser is loading the page via SSL, false otherwise.

This function is a clone of BB_IsSSLRequest().

SSO_Client::GetRequestHost($protocol = "")

Parameters:

Returns: A string containing the host in URL format.

This static function is identical to BB_GetRequestHost() except it attempts to avoid name conflicts.

SSO_Client::GetRequestURLBase()

Parameters: None.

Returns: A string containing the path part of the request URL (excludes query string).

This static function is identical to BB_GetRequestURLBase().

SSO_Client::GetFullRequestURLBase($protocol = "")

Parameters:

Returns: A string containing the full request URL.

This member function combines SSO_GetRequestHost() and SSO_GetRequestURLBase() to obtain the full request URL.

SSO_Client::Translate()

Parameters:

Returns: A translated string containing output from sprintf().

This private member function provides multilingual translation of an input formatting string into a single output string based on the information in SSO_Client::$langmap, $this->client_lang, and $this->client_def_lang.

SSO_Client::PostTranslate($str)

Parameters:

Returns: A translated version of the string.

This private member function runs specialized partial or complete translations of the input string based on the information in SSO_Client::$langmap, $this->client_lang, and $this->client_def_lang.

SSO_Client::SetLanguage($path, $lang)

Parameters:

Returns: An array that indicates success and contains an error string on failure.

This member function loads in the specified language pack (if not already loaded) and sets the $sso_client_lang global variable to the language.

SSO_Client::InitLangmap($path, $default = "")

Parameters:

Returns: Nothing.

This private member function initializes SSO_Client::$langmap, $this->client_lang, and $this->client_def_lang based on the browser's preferences and available language packs. Failures are ignored.

SSO_Client::ProcessLogin($info, $fromserver = false)

Parameters:

Returns: Nothing.

This private member function processes the results of the 'getlogin' API if the call was successful. If return information was requested - that is, this request just came back from the SSO server - that information is decrypted and loaded into the PHP superglobals so that the script can pick up where it left off.

The second parameter is stored so a future call to SSO_FromSSOServer() can be used to determine if the current request came from the return from the SSO server. The SSO client tends to overwrite all input variables, so this helps preserve enough information to be able to avoid entering infinite loops (e.g. if the return url is 'login.php', then this avoids returning to the SSO server and the browser can be redirected to another URL).

SSO_Client::RSG_WriteSeedData(&$data, &$hash, $str, $reduce = true)

Parameters:

Returns: A numeric value to be used to calculate a new cryptographic strength.

This private static function is used to build upon previous calls to this function with new sources of entropy. When $hash is false, $reduce is used to help save memory by hashing the data being input and packing the result. When $hash is a resource, all data is run through the SHA-512 hash algorithm in a streaming fashion, so reduction doesn't matter.

SSO_Client::RSG_GenerateToken($rootseed, $entropy = "")

Parameters:

Returns: A hex encoded string containing a token.

This private static function takes a root seed, assumed to be secure and have sufficient cryptographic strength for the application, along with optional, additional entropy, and generates a new token. This function is much faster than RSG_GenerateRootSeed() and is designed to generate any number of tokens.

SSO_Client::SafeRedirect($removekeys)

Parameters:

Returns: Nothing.

This private member function performs a redirect to a URL similar to the current URL but without any of the keys specified in the $removekeys and $sso_removekeys arrays.

SSO_Client::LoggedIn()

Parameters: None.

Returns: A boolean of true if the user is logged in, false otherwise.

This member function checks for an encrypted cookie, decrypts it, loads decrypted cookie information, checks the timestamp, and may query the SSO server using the 'getlogin' API to determine if the user is actually logged in under a number of different conditions. The results of this call are cached.

The SSO client may also be configured to force logging in whenever the IP address changes via SSO_COOKIE_RESET_IPADDR_CHANGES in 'config.php'.

SSO_Client::FromSSOServer()

Parameters: None.

Returns: A boolean of true if the current request is from the SSO server, false otherwise.

This member function is used to differentiate regular requests from requests that are from the SSO server. The SSO client tends to overwrite all input variables when a request comes in from the SSO server, so this helps preserve enough information to be able to avoid entering infinite loops. For example, if the return url is 'login.php', then this can be used to avoid returning to the SSO server and the browser can sensibly be redirected to another URL.

SSO_Client::Init($removekeys = array())

Parameters:

Returns: Nothing.

This member function performs all of the initialization steps required for correct SSO client operations. This function is called automatically when the client 'functions.php' file is 'require'd by another PHP script. Including 'index.php' and initializing an instance of 'SSO_Client' should be done as soon as possible during a script's execution cycle to minimize potential issues with returning from the SSO server.

SSO_Client::Login($lang = "", $msg = "", $extra = array())

Parameters:

Returns: Nothing.

This member function uses the SSO server 'initlogin' API to initialize a session. The current request is encrypted and sent to the SSO server as part of the session setup for later retrieval so that the application can continue processing the request as if nothing happened. The only circumstance where this won't work is if a file was uploaded to the web server. The file will have to be uploaded again after returning to the SSO client.

When $msg is "insufficient_permissions", the SSO server treats the request differently and will always display the sign in options so the user has the opportunity to sign in with a different account with access to the requested resource.

The $extra array is information passed onto the SSO server that ends up as part of the URL that the browser will be redirected to. Used primarily by the Remote Login provider.

SSO_Client::CanRemoteLogin()

Parameters: None.

Returns: A boolean of true if there is valid Remote Login information in the request, false otherwise.

This member function is used by the Deployer while integrating a Remote Login into their sign in infrastructure. There is no point in continuing to execute code if this returns false as it would be impossible to complete the sign in.

SSO_Client::RemoteLogin($userid, $fieldmap = array(), $endpoint, $apikey, $secretkey)

Parameters:

Returns: Nothing.

This member function performs a remote login using a Remote API key and secret. Information is pushed into an existing temporary session in the SSO server and the browser is redirected to a preconfigured URL in the SSO server front end. This function will only work when SSO_CanRemoteLogin() returns true. On error, this function outputs the error message and immediately stops execution.

SSO_Client::Logout()

Parameters: None.

Returns: Nothing.

This member function uses the SSO server 'logout' API to delete a session. The cookies for the current session are also deleted. It is up to the application to redirect the browser somewhere after logging out.

SSO_Client::HasDBData()

Parameters: None.

Returns: A boolean of true if there is database-backed data storage for the user, false otherwise.

This member function is only useful when there is a local database with data storage for user information. Use this function to determine if there is cached data to be stored in a database to avoid hitting the SSO server endpoint later. The database data storage mechanism of the SSO client is optional.

SSO_Client::LoadDBData($data)

Parameters:

Returns: A boolean of true if the data was loaded successfully, false otherwise.

This member function accepts the data of a previous SSO_SaveDBData() call, base64 decodes, decrypts (Blowfish), uncompresses, and unserializes the data into internal information structures. The database data storage mechanism of the SSO client is optional.

SSO_Client::SaveDBData()

Parameters: None.

Returns: A string containing encrypted data for storage in a database.

This member function serializes, compresses, encrypts (Blowfish), and base64 encodes internal information suitable for storage into a database cache. The database data storage mechanism of the SSO client is optional.

SSO_Client::IsSiteAdmin()

Parameters: None.

Returns: A boolean of true if the user is a SSO server site admin, false otherwise.

This member function checks for the SSO server site admin tag and returns its value. This is a special tag that is included in all 'getlogin' API results. Note that if the SSO client has been configured to not accept site admins (via SSO_CLIENT_ACCEPT_SITE_ADMIN in 'config.php'), this will always return false.

SSO_Client::HasTag($name)

Parameters:

Returns: A boolean of true if the user has the tag, false otherwise.

This member function checks the API key mapped tag set for the specified tag.

SSO_Client::LoadUserInfo($savefirst = false)

Parameters:

Returns: A boolean of true if the user information was successfully loaded, false otherwise.

This member function is used to force user information to be loaded from the SSO server via the 'getlogin' API. If $savefirst is true, the SSO client will attempt to update user information before loading it.

SSO_Client::UserLoaded()

Parameters: None.

Returns: A boolean of true if the user information was loaded from the SSO server, false otherwise.

This member function is useful to determine if information needs to be cached. There are both encrypted cookie and database storage mechanisms that can be used to minimize the number of requests to the SSO server endpoint.

SSO_Client::GetField($key, $default = false)

Parameters:

Returns: A string containing the field information from the SSO server or $default if no information is found.

This member function looks for a SSO client field from the API key field mapping and returns the value. $default is returned if the information is not found.

SSO_Client::GetEditableFields()

Parameters: None.

Returns: An array containing the fields that can be written to in the SSO server.

This member function returns the writeable SSO server fields that the API key has access to. The field must be read/write in the API key and not protected by the provider. Useful for building a user profile editor.

SSO_Client::SetField($key, $value)

Parameters:

Returns: A boolean of true if the field information was successfully set, false otherwise.

This member function checks the writeable fields to make sure $key can be written to and then sets the user information to $value. Useful for building a user profile editor.

SSO_Client::GetData($key, $default = false)

Parameters:

Returns: A string containing the information or $default if no information is found.

This member function retrieves user data from either the encrypted cookie information or database storage. Cached data is trustworthy because it is timestamped and encrypted with a key utilized solely by the SSO client.

SSO_Client::SetData($key, $value, $maxcookielen = 50)

Parameters:

Returns: A boolean of true if the $value was cached, false otherwise.

This member function caches the key-value pair for saving later to the encrypted cookie and optional encrypted database storage. The main purpose for using this function is to cache SSO server data locally so that the SSO client doesn't constantly make requests to the SSO server endpoint. Cached data is trustworthy because it is timestamped and encrypted with a key utilized solely by the SSO client. Fewer requests to the SSO server will significantly improve the performance of an application. Note that data that is too large to include in the cookie is made available for local database storage. Database storage is optional but can further improve application performance.

SSO_Client::SaveUserInfo($usedb = false)

Parameters:

Returns: Nothing.

This member function sends the cookies to the web browser containing the encrypted cookie and validation cookies. Cookies are only sent if user information changed.

SSO_Client::GetUserID()

Parameters: None.

Returns: A large integer containing the ID of the user.

This member function returns the unique user ID in the SSO server.

SSO_Client::GetSecretToken()

Parameters: None.

Returns: A string containing a secret token.

This member function returns a secret token for the current user that can be used to generate CSRF/XSRF defense tokens.

© CubicleSoft