Skip to content
Snippets Groups Projects
Commit d3b826b2 authored by Jaime Perez's avatar Jaime Perez
Browse files

Extract the openID module out of SimpleSAMLphp.

parent 2d8652f7
No related branches found
No related tags found
No related merge requests found
This file indicates that the default state of this module
is disabled. To enable, create a file named enable in the
same directory as this file.
{
"openidtestpage": {
"en": "OpenID Consumer Authentication Test"
}
}
\ No newline at end of file
<?php
/*
* Disable strict error reporting, since the OpenID library
* used is PHP4-compatible, and not PHP5 strict-standards compatible.
*/
SimpleSAML_Utilities::maskErrors(E_STRICT);
if (defined('E_DEPRECATED')) {
/* PHP 5.3 also has E_DEPRECATED. */
SimpleSAML_Utilities::maskErrors(constant('E_DEPRECATED'));
}
/* Add the OpenID library search path. */
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))) . '/lib');
require_once('Auth/OpenID/AX.php');
require_once('Auth/OpenID/SReg.php');
require_once('Auth/OpenID/Server.php');
require_once('Auth/OpenID/ServerRequest.php');
/**
* Authentication module which acts as an OpenID Consumer
*
* @author Andreas Åkre Solberg, <andreas.solberg@uninett.no>, UNINETT AS.
* @package simpleSAMLphp
*/
class sspmod_openid_Auth_Source_OpenIDConsumer extends SimpleSAML_Auth_Source {
/**
* Static openid target to use.
*
* @var string|NULL
*/
private $target;
/**
* Custom realm to use.
*
* @var string|NULL
*/
private $realm;
/**
* List of optional attributes.
*/
private $optionalAttributes;
private $optionalAXAttributes;
/**
* List of required attributes.
*/
private $requiredAttributes;
private $requiredAXAttributes;
/**
* Validate SReg responses.
*/
private $validateSReg;
/**
* List of custom extension args
*/
private $extensionArgs;
/**
* Prefer HTTP Redirect over HTML Form Redirection (POST)
*/
private $preferHttpRedirect;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*/
public function __construct($info, $config) {
/* Call the parent constructor first, as required by the interface. */
parent::__construct($info, $config);
$cfgParse = SimpleSAML_Configuration::loadFromArray($config,
'Authentication source ' . var_export($this->authId, TRUE));
$this->target = $cfgParse->getString('target', NULL);
$this->realm = $cfgParse->getString('realm', NULL);
$this->optionalAttributes = $cfgParse->getArray('attributes.optional', array());
$this->requiredAttributes = $cfgParse->getArray('attributes.required', array());
$this->optionalAXAttributes = $cfgParse->getArray('attributes.ax_optional', array());
$this->requiredAXAttributes = $cfgParse->getArray('attributes.ax_required', array());
$this->validateSReg = $cfgParse->getBoolean('sreg.validate',TRUE);
$this->extensionArgs = $cfgParse->getArray('extension.args', array());
$this->preferHttpRedirect = $cfgParse->getBoolean('prefer_http_redirect', FALSE);
}
/**
* Initiate authentication. Redirecting the user to the consumer endpoint
* with a state Auth ID.
*
* @param array &$state Information about the current authentication.
*/
public function authenticate(&$state) {
assert('is_array($state)');
$state['openid:AuthId'] = $this->authId;
if ($this->target !== NULL) {
/* We know our OpenID target URL. Skip the page where we ask for it. */
$this->doAuth($state, $this->target);
/* doAuth() never returns. */
assert('FALSE');
}
$id = SimpleSAML_Auth_State::saveState($state, 'openid:init');
$url = SimpleSAML_Module::getModuleURL('openid/consumer.php');
SimpleSAML_Utilities::redirectTrustedURL($url, array('AuthState' => $id));
}
/**
* Retrieve the Auth_OpenID_Consumer instance.
*
* @param array &$state The state array we are currently working with.
* @return Auth_OpenID_Consumer The Auth_OpenID_Consumer instance.
*/
private function getConsumer(array &$state) {
$store = new sspmod_openid_StateStore($state);
$session = new sspmod_openid_SessionStore();
return new Auth_OpenID_Consumer($store, $session);
}
/**
* Retrieve the URL we should return to after successful authentication.
*
* @return string The URL we should return to after successful authentication.
*/
private function getReturnTo($stateId) {
assert('is_string($stateId)');
return SimpleSAML_Module::getModuleURL('openid/linkback.php', array(
'AuthState' => $stateId,
));
}
/**
* Retrieve the trust root for this openid site.
*
* @return string The trust root.
*/
private function getTrustRoot() {
if (!empty($this->realm)) {
return $this->realm;
} else {
return SimpleSAML_Utilities::selfURLhost();
}
}
/**
* Send an authentication request to the OpenID provider.
*
* @param array &$state The state array.
* @param string $openid The OpenID we should try to authenticate with.
*/
public function doAuth(array &$state, $openid) {
assert('is_string($openid)');
$stateId = SimpleSAML_Auth_State::saveState($state, 'openid:auth');
$consumer = $this->getConsumer($state);
// Begin the OpenID authentication process.
$auth_request = $consumer->begin($openid);
// No auth request means we can't begin OpenID.
if (!$auth_request) {
throw new SimpleSAML_Error_BadRequest('Not a valid OpenID: ' . var_export($openid, TRUE));
}
$sreg_request = Auth_OpenID_SRegRequest::build(
$this->requiredAttributes,
$this->optionalAttributes
);
if ($sreg_request) {
$auth_request->addExtension($sreg_request);
}
// Create attribute request object
$ax_attribute = array();
foreach($this->requiredAXAttributes as $attr) {
$ax_attribute[] = Auth_OpenID_AX_AttrInfo::make($attr,1,true);
}
foreach($this->optionalAXAttributes as $attr) {
$ax_attribute[] = Auth_OpenID_AX_AttrInfo::make($attr,1,false);
}
if (count($ax_attribute) > 0) {
// Create AX fetch request
$ax_request = new Auth_OpenID_AX_FetchRequest;
// Add attributes to AX fetch request
foreach($ax_attribute as $attr){
$ax_request->add($attr);
}
// Add AX fetch request to authentication request
$auth_request->addExtension($ax_request);
}
foreach($this->extensionArgs as $ext_ns => $ext_arg) {
if (is_array($ext_arg)) {
foreach($ext_arg as $ext_key => $ext_value) {
$auth_request->addExtensionArg($ext_ns, $ext_key, $ext_value);
}
}
}
// Redirect the user to the OpenID server for authentication.
// Store the token for this authentication so we can verify the
// response.
// For OpenID 1, send a redirect. For OpenID 2, use a Javascript form
// to send a POST request to the server or use redirect if
// prefer_http_redirect is enabled and redirect URL size
// is less than 2049
$should_send_redirect = $auth_request->shouldSendRedirect();
if ($this->preferHttpRedirect || $should_send_redirect) {
$redirect_url = $auth_request->redirectURL($this->getTrustRoot(), $this->getReturnTo($stateId));
// If the redirect URL can't be built, display an error message.
if (Auth_OpenID::isFailure($redirect_url)) {
throw new SimpleSAML_Error_AuthSource($this->authId, 'Could not redirect to server: ' . var_export($redirect_url->message, TRUE));
}
// For OpenID 2 failover to POST if redirect URL is longer than 2048
if ($should_send_redirect || strlen($redirect_url) <= 2048) {
SimpleSAML_Utilities::redirectTrustedURL($redirect_url);
assert('FALSE');
}
}
// Generate form markup and render it.
$form_id = 'openid_message';
$form_html = $auth_request->formMarkup($this->getTrustRoot(), $this->getReturnTo($stateId), FALSE, array('id' => $form_id));
// Display an error if the form markup couldn't be generated; otherwise, render the HTML.
if (Auth_OpenID::isFailure($form_html)) {
throw new SimpleSAML_Error_AuthSource($this->authId, 'Could not redirect to server: ' . var_export($form_html->message, TRUE));
} else {
echo '<html><head><title>OpenID transaction in progress</title></head>
<body onload=\'document.getElementById("' . $form_id . '").submit()\'>' .
$form_html . '</body></html>';
exit;
}
}
/**
* Process an authentication response.
*
* @param array &$state The state array.
*/
public function postAuth(array &$state) {
$consumer = $this->getConsumer($state);
$return_to = SimpleSAML_Utilities::selfURL();
// Complete the authentication process using the server's
// response.
$response = $consumer->complete($return_to);
// Check the response status.
if ($response->status == Auth_OpenID_CANCEL) {
// This means the authentication was cancelled.
throw new SimpleSAML_Error_UserAborted();
} else if ($response->status == Auth_OpenID_FAILURE) {
// Authentication failed; display the error message.
throw new SimpleSAML_Error_AuthSource($this->authId, 'Authentication failed: ' . var_export($response->message, TRUE));
} else if ($response->status != Auth_OpenID_SUCCESS) {
throw new SimpleSAML_Error_AuthSource($this->authId, 'General error. Try again.');
}
// This means the authentication succeeded; extract the
// identity URL and Simple Registration data (if it was
// returned).
$openid = $response->identity_url;
$attributes = array('openid' => array($openid));
$attributes['openid.server_url'] = array($response->endpoint->server_url);
if ($response->endpoint->canonicalID) {
$attributes['openid.canonicalID'] = array($response->endpoint->canonicalID);
}
if ($response->endpoint->local_id) {
$attributes['openid.local_id'] = array($response->endpoint->local_id);
}
$sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response, $this->validateSReg);
$sregresponse = $sreg_resp->contents();
if (is_array($sregresponse) && count($sregresponse) > 0) {
$attributes['openid.sregkeys'] = array_keys($sregresponse);
foreach ($sregresponse AS $sregkey => $sregvalue) {
$attributes['openid.sreg.' . $sregkey] = array($sregvalue);
}
}
// Get AX response information
$ax = new Auth_OpenID_AX_FetchResponse();
$ax_resp = $ax->fromSuccessResponse($response);
if (($ax_resp instanceof Auth_OpenID_AX_FetchResponse) && (!empty($ax_resp->data))) {
$axresponse = $ax_resp->data;
$attributes['openid.axkeys'] = array_keys($axresponse);
foreach ($axresponse AS $axkey => $axvalue) {
if (preg_match("/^\w+:/",$axkey)) {
$attributes[$axkey] = (is_array($axvalue)) ? $axvalue : array($axvalue);
} else {
SimpleSAML_Logger::warning('Invalid attribute name in AX response: ' . var_export($axkey, TRUE));
}
}
}
SimpleSAML_Logger::debug('OpenID Returned Attributes: '. implode(", ",array_keys($attributes)));
$state['Attributes'] = $attributes;
SimpleSAML_Auth_Source::completeAuth($state);
}
}
<?php
/**
* Class which implements the openid session store logic.
*
* This class has the interface specified in the constructor of the
* Auth_OpenID_Consumer class.
*
* @package simpleSAMLphp
*/
class sspmod_openid_SessionStore {
/**
* Retrieve a key from the session store.
*
* @param string $key The key we should retrieve.
* @return mixed The value stored with the given key, or NULL if the key isn't found.
*/
public function get($key) {
assert('is_string($key)');
$session = SimpleSAML_Session::getSessionFromRequest();
return $session->getData('openid.session', $key);
}
/**
* Save a value to the session store under the given key.
*
* @param string $key The key we should save.
* @param mixed NULL $value The value we should save.
*/
public function set($key, $value) {
assert('is_string($key)');
$session = SimpleSAML_Session::getSessionFromRequest();
$session->setData('openid.session', $key, $value);
}
/**
* Delete a key from the session store.
*
* @param string $key The key we should delete.
*/
public function del($key) {
assert('is_string($key)');
$session = SimpleSAML_Session::getSessionFromRequest();
$session->deleteData('openid.session', $key);
}
}
<?php
/* The OpenID library relies on manual loading of classes. */
require_once('Auth/OpenID/Interface.php');
require_once('Auth/OpenID/Association.php');
/**
* Implementation of Auth_OpenID_OpenIDStore which saves the state in
* an state-array.
*
* @package simpleSAMLphp
*/
class sspmod_openid_StateStore extends Auth_OpenID_OpenIDStore{
/**
* Reference to the state array.
*/
private $state;
/**
* Reference to the array with associations in the state array.
*/
private $associations;
/**
* Initializes the store object.
*
* @param array &$state Reference to the state array.
*/
public function __construct(&$state) {
assert('is_array($state)');
$this->state =& $state;
if (!array_key_exists('openid:Assocs', $state)) {
$state['openid:Assocs'] = array();
}
$this->associations =& $state['openid:Assocs'];
}
/**
* Determine whether a given nonce can be used.
*
* This implementation accepts all nonces, and relies on the state array
* being invalidated when login completes to prevent replay attacks.
*
* @return bool This function always returns TRUE.
*/
public function useNonce($server_url, $timestamp, $salt) {
return TRUE;
}
/**
* Retrieve all associations for a given server.
*
* The associations are returned as an associative array with the
* association handle as the index and the association object as
* the value.
*
* @param string $server_url The server.
* @return array Associative array with associations.
*/
private function getServerAssociations($server_url) {
assert('is_string($server_url)');
if (!array_key_exists($server_url, $this->associations)) {
return array();
}
$ret = array();
foreach ($this->associations[$server_url] as $handle => $association) {
$association = Auth_OpenID_Association::deserialize(
'Auth_OpenID_Association', $association);
if ($association === NULL) {
continue;
}
if ($association->getExpiresIn() == 0) {
continue;
}
$ret[$handle] = $association;
}
return $ret;
}
/**
* Retrieve an association with the given handle.
*
* @param string $server_url The server.
* @param string $handle The handle of the association.
* @return Auth_OpenID_Association|NULL The association object, if it is found.
*/
private function readAssociation($server_url, $handle) {
assert('is_string($server_url)');
assert('is_string($handle)');
$sassoc = $this->getServerAssociations($server_url);
if (!array_key_exists($handle, $sassoc)) {
return NULL;
}
return $sassoc[$handle];
}
/**
* Retrieve an association.
*
* This function retrieves an association with the given handle, or the most
* recent association if no handle is given.
*
* @param string $server_url The server.
* @param string|NULL $handle The association handle.
* @return Auth_OpenID_Association|NULL The association object, if it is found.
*/
public function getAssociation($server_url, $handle = NULL) {
assert('is_string($server_url)');
assert('is_null($handle) || is_string($handle)');
if ($handle !== NULL) {
return $this->readAssociation($server_url, $handle);
}
/* $handle is NULL - we should retrieve the most recent association. */
$sassoc = $this->getServerAssociations($server_url);
$recentAssoc = NULL;
foreach ($sassoc as $handle => $association) {
if ($recentAssoc === NULL) {
/* No $recentAssoc - this is the most recent association. */
$recentAssoc = $association;
continue;
}
if ($association->issued > $recentAssoc->issued) {
/* More recently issued than $recentAssoc. */
$recentAssoc = $association;
}
}
return $recentAssoc;
}
/**
* Store an association.
*
* This function stores an association.
* @param string $server_url The server.
* @param Auth_OpenID_Association $association The association which should be stored.
* @return bool TRUE if the association is stored, FALSE if not.
*/
public function storeAssociation($server_url, Auth_OpenID_Association $association) {
assert('is_string($server_url)');
if (!array_key_exists($server_url, $this->associations)) {
$this->associations[$server_url] = array();
}
$handle = $association->handle;
assert('is_string($handle)');
$this->associations[$server_url][$handle] = $association->serialize();
/* We rely on saveState saving with the same id as before. */
SimpleSAML_Auth_State::saveState($this->state, 'openid:auth');
return TRUE;
}
}
?>
\ No newline at end of file
<?php
$this->data['header'] = 'OpenID Login';
$this->data['autofocus'] = 'openid-identifier';
$this->includeAtTemplateBase('includes/header.php');
?>
<style>
input.openid-identifier {
background: url(http://stat.livejournal.com/img/openid-inputicon.gif) no-repeat;
/* background-color: #fff; */
border-left: 1px solid #ccc;
border-right: 1px solid #aaa;
border-top: 1px solid #aaa;
border-bottom: 1px solid #ccc;
color: #555;
background-position: 0 50%;
padding-left: 18px;
}
fieldset {
border-left: 1px solid #aaa;
border-right: 1px solid #ccc;
border-top: 1px solid #ccc;
border-bottom: 1px solid #aaa;
padding: 1em;
}
legend {
padding-left: .3em;
padding-right: .3em;
color: #555;
}
div.error {
padding: 1em; margin: 1em;
background: red;
color: white;
border: 1px solid #600;
}
</style>
<?php if (isset($this->data['error'])) { print "<div class=\"error\">" . $this->data['error'] . "</div>"; } ?>
<form method="get" action="consumer.php">
<fieldset>
<legend>OpenID Login</legend>
Identity&nbsp;URL:
<input type="hidden" name="action" value="verify" />
<input id="openid-identifier" class="openid-identifier" type="text" name="openid_url" value="http://" />
<input type="hidden" name="AuthState" value="<?php echo htmlspecialchars($this->data['AuthState']); ?>" />
<input type="submit" value="Login with OpenID" />
</fieldset>
</form>
<p style="margin-top: 2em">
OpenID is a free and easy way to use a single digital identity across the Internet. Enter your OpenID identity URL in the box above to authenticate.
</p>
<?php
$this->includeAtTemplateBase('includes/footer.php');
?>
\ No newline at end of file
<?php
/* Find the authentication state. */
if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) {
throw new SimpleSAML_Error_BadRequest('Missing mandatory parameter: AuthState');
}
$authState = $_REQUEST['AuthState'];
// sanitize the input
$sid = SimpleSAML_Utilities::parseStateID($authState);
if (!is_null($sid['url'])) {
SimpleSAML_Utilities::checkURLAllowed($sid['url']);
}
$state = SimpleSAML_Auth_State::loadState($authState, 'openid:init');
$sourceId = $state['openid:AuthId'];
$authSource = SimpleSAML_Auth_Source::getById($sourceId);
if ($authSource === NULL) {
throw new SimpleSAML_Error_BadRequest('Invalid AuthId \'' . $sourceId . '\' - not found.');
}
$error = NULL;
try {
if (!empty($_GET['openid_url'])) {
$authSource->doAuth($state, (string)$_GET['openid_url']);
}
} catch (Exception $e) {
$error = $e->getMessage();
}
$config = SimpleSAML_Configuration::getInstance();
$t = new SimpleSAML_XHTML_Template($config, 'openid:consumer.php', 'openid');
$t->data['error'] = $error;
$t->data['AuthState'] = $authState;
$t->show();
<?php
/* Find the authentication state. */
if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) {
throw new SimpleSAML_Error_BadRequest('Missing mandatory parameter: AuthState');
}
$authState = $_REQUEST['AuthState'];
// sanitize the input
$sid = SimpleSAML_Utilities::parseStateID($authState);
if (!is_null($sid['url'])) {
SimpleSAML_Utilities::checkURLAllowed($sid['url']);
}
$state = SimpleSAML_Auth_State::loadState($authState, 'openid:auth');
$sourceId = $state['openid:AuthId'];
$authSource = SimpleSAML_Auth_Source::getById($sourceId);
if ($authSource === NULL) {
throw new SimpleSAML_Error_BadRequest('Invalid AuthId \'' . $sourceId . '\' - not found.');
}
try {
$authSource->postAuth($state);
/* postAuth() should never return. */
assert('FALSE');
} catch (SimpleSAML_Error_Exception $e) {
SimpleSAML_Auth_State::throwException($state, $e);
} catch (Exception $e) {
SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_AuthSource($sourceId, 'Error on OpenID linkback endpoint.', $e));
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment