The Shibboleth V2 IdP and SP software have reached End of Life and are no longer supported. This documentation is available for historical purposes only. See the IDP v4 and SP v3 wiki spaces for current documentation on the supported versions.

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

The <Notify> element represents a resource inside an application protected by Shibboleth that is created to process "application notifications" of significant lifecycle events for a user session that the SP is processing.

Currently there are three "events" that cause notifications:

  • logout/end of a session
  • a change to a user's SAML <NameID> value sent from an IdP
  • the termination of a user's SAML <NameID> value (a deprovisioning event)

There are also two kinds of notifications, "front-channel" and "back-channel", but only logout events support front-channel notifications in the current implementation.

Attributes

  • Channel ("front" or "back")
    • Required attribute to identify the method of notification.
  • Location (absolute URL)
    • The URL to send the notification message to.

Front-Channel Notifications

Front-channel notifications take place through the user's browser using redirection. The advantage is that you get real-time control over the UI as well as access to the active session (if you protect the endpoint itself). The downside is that the application has to ensure that it provides a response and properly forwards the browser to the "next" location in the notification chain.

The front-channel "protocol" is a redirection to the endpoint with a query string containing the following parameters:

  • action
    • Currently only the value "logout" is possible.
  • return
    • The URL to redirect the browser to when finished processing the event. The application MUST send the browser here to prevent problems during single logout.

Back-Channel Notifications

Back-channel notifications are done behind the scenes using SOAP messages sent by the SP to the notification endpoint. The advantage is speed and invisibility to the user. The downside is that only limited information is made available to the application, and usually the application will require changes in how it maintains its own state to take advantage of the feature.

The back-channel protocol is a SOAP message using a custom schema supplied with the SP. A description follows. The XML namespace is urn:mace:shibboleth:2.0:sp:notify

Notification endpoints should respond to the event with a response containing an empty element named <notify:OK> or a SOAP fault.

Logout Events

Logout event notification uses the <notify:LogoutNotification> element.

Attributes
  • type ("local" or "global")
    • Indicates the type of logout event. Local logout is confined to the SP, while global logout involves the IdP as well.
Child Elements
  • <SessionID> (one or more)
    • The ID of a session being logged out. The application is responsible for connecting the SP's session ID to its own state.

NameID Management Events

NameID management event notification uses the <notify:NameIDNotification> element.

Child Elements
  • <saml:NameID> (required)
    • The original identifier associated with the user.
  • <samlp:NewID> or <samlp:Terminate> (one or the other)
    • The new identifier to associate with the user, or the termination notice. These elements come directly from the SAML protocol schema and are simply copied from the actual protocol message.

Sample PHP 5 logout code

The following code can be used as a sample to adapt an application for front and back-channel logout.

<?
// Sample PHP 5 Shibboleth logout code by lukas.haemmerle@switch.change_user
// History:
// - December 8 2008:    Uploaded initial version that also was used for Moodle

// Just for debugging the WSDL part
ini_set("soap.wsdl_cache_enabled", "0"); // disabling WSDL cache

/************************************************/
/* Sample code to log out user from application */
/************************************************/

// Requirements:
// PHP 5 with SOAP support (should be available in default deployment)


//////////////////////////
// Front channel logout //
//////////////////////////

// Note: Generally the back-channel logout should be used once the Shibboleth
//       Identity Provider supports Single Log Out!
//       Front-channel logout is not of much use.

if (
		isset($_GET['return'])
		&& isset($_GET['action'])
		&& $_GET['action'] == 'logout'
   ){

	// Logout out user from application
	// E.g. destroy application session/cookie etc

	/* Example Code: Start */

	// Unset all of the session variables.
	$_SESSION = array();

	// Destroy cookie
	if (isset($_COOKIE[session_name()])) {
		setcookie(session_name(), '', time()-42000, '/');
	}

	session_destroy();

	/* Example Code: End */

	// Finally, send user to the return URL
	header('Location: '.$_GET['return']);
	exit;
}

/////////////////////////
// Back channel logout //
/////////////////////////

// Note: This is the preferred logout channel because it also allows
//       administrative logout. However, it requires your application to be
//       adapated in the sense that the user's Shibboleth session ID must be
//       stored in the application's session data.
//       See function LogoutNotification below

elseif (!empty($HTTP_RAW_POST_DATA)) {
	// Set SOAP header
	$server = new SoapServer('https://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl');
	$server->addFunction("LogoutNotification");
	$server->handle();
}

/////////////////
// Return WSDL //
/////////////////

// Note: This is needed for the PHP SoapServer class.
//       Since I'm not a web service guru it might be that the code below is not
//       absolutely correct but at least it seems to to its job properly when it
//       comes to Shibboleth logout

else {

	header('Content-Type: text/xml');

	echo <<<WSDL
<?xml version ="1.0" encoding ="UTF-8" ?>
<definitions name="LogoutNotification"
  targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
  xmlns:notify="urn:mace:shibboleth:2.0:sp:notify"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns="http://schemas.xmlsoap.org/wsdl/">

	<types>
	   <schema targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
		   xmlns="http://www.w3.org/2000/10/XMLSchema"
		   xmlns:notify="urn:mace:shibboleth:2.0:sp:notify">

			<simpleType name="string">
				<restriction base="string">
					<minLength value="1"/>
				</restriction>
			</simpleType>

			<element name="OK" type="notify:OKType"/>
			<complexType name="OKType">
				<sequence/>
			</complexType>

		</schema>
	</types>

	<message name="getLogoutNotificationRequest">
		<part name="SessionID" type="notify:string" />
	</message>

	<message name="getLogoutNotificationResponse" >
		<part name="OK"/>
	</message>

	<portType name="LogoutNotificationPortType">
		<operation name="LogoutNotification">
			<input message="getLogoutNotificationRequest"/>
			<output message="getLogoutNotificationResponse"/>
		</operation>
	</portType>

	<binding name="LogoutNotificationBinding" type="notify:LogoutNotificationPortType">
		<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
		<operation name="LogoutNotification">
			<soap:operation soapAction="urn:xmethods-logout-notification#LogoutNotification"/>
		</operation>
	</binding>

	<service name="LogoutNotificationService">
		  <port name="LogoutNotificationPort" binding="notify:LogoutNotificationBinding">
			<soap:address location="https://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}"/>
		  </port>
	</service>
</definitions>
WSDL;
	exit;

}

/******************************************************************************/
/// This function does the actual logout
function LogoutNotification($SessionID){

	// Delete session of user using $SessionID to locate the user's session file
	// on the file system or in the database
	// Then delete this entry or record to clear the session
	// However, for that to work it is essential that the user's Shibboleth
	// SessionID is stored in the user session data!

	$dbsession = true;
	if ($dbsession){
		// INSERT AND ADAPT CODE HERE
		/*
		if ($user_session_data = #YOUR-DB-QUERY-FUNCTION#('SELECT sesskey, sessdata FROM '.#YOUR-SESSION-TABLE#.' WHERE expiry > NOW()')) {
			foreach ($user_session_data as $session_data) {

				$user_session = unserialize($session_data->sessdata);

				// We assume that the user's Shibboleth session ID is stored in $user_session['SESSION']->shibboleth_session_id
				if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
					// If there is a match, delete entry
					if ($user_session['SESSION']->shibboleth_session_id == $SessionID){
						// Delete this session entry
						if (#YOUR-SESSION-DESTRUCTION-FUNCTION#($session_data->sesskey) !== true){
							return new SoapFault('LogoutError', 'Could not delete session entry in database.');
						}
					}
				}
			}
		}
		*/
	} else {
		$dir = session_save_path();
		if (is_dir($dir)) {
			if ($dh = opendir($dir)) {
				while (($file = readdir($dh)) !== false) {
					if (is_file($dir.'/'.$file)){
						$session_key = ereg_replace('sess_', '', $file);

						$data = file($dir.'/'.$file);

						if (isset($data[0])){
							$session = unserializesession ($data[0]);
							// If there is a match, delete file
							if (isset($session['user']['sessionID']) && $session['user']['sessionID'] == $SessionID){
								// Delete this file
								if (!unlink($dir.'/'.$file)){
									return new SoapFault('LogoutError', 'Could not delete session file.');
								}
							}
						}
					}
				}
				closedir($dh);
			} else {
				return new SoapFault('LogoutError', 'Couldn\'t read directory content of '.$dir);
			}
		} else {
			return new SoapFault('LogoutError', 'There is no directory '.$dir);
		}
	}
}

/******************************************************************************/
// Deserializes session data and returns it in a hash array of arrays
function unserializesession( $serialized_string ){
	$variables = array( );
	$a = preg_split( "/(\w+)\|/", $serialized_string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
	for( $i = 0; $i < count( $a ); $i = $i+2 ) {
		$variables[$a[$i]] = unserialize( $a[$i+1] );
	}
	return( $variables );
}

?>

With code like this you would use notify elements like:

<Notify
	Channel="back"
	Location="https://#YOUR_HOSTNAME#/#PATH_TO#/logout.php" />

<!--
If possible, you should use only the back channel logout once it is working.
-->
<!--
<Notify
	Channel="front"
	Location="https://#YOUR_HOSTNAME#/#PATH_TO#/logout.php" />
-->
  • No labels