Source code for file /openid/Auth/OpenID/AX.php
Documentation is available at AX.php
* Implements the OpenID attribute exchange specification, version 1.0
* as of svn revision 370 from openid.net svn.
// Do not allow direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
* Require utility classes and functions for the consumer.
require_once "Auth/OpenID/Extension.php";
require_once "Auth/OpenID/Message.php";
require_once "Auth/OpenID/TrustRoot.php";
define('Auth_OpenID_AX_NS_URI',
'http://openid.net/srv/ax/1.0');
// Use this as the 'count' value for an attribute in a FetchRequest to
// ask for as many values as the OP can provide.
define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited');
// Minimum supported alias length in characters. Here for
define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32);
* @param mixed $thing Any object which may be an
* Auth_OpenID_AX_Error object.
* @return bool true if $thing is an Auth_OpenID_AX_Error; false
return is_a($thing, 'Auth_OpenID_AX_Error');
* Check an alias for invalid characters; raise AXError if any are
* found. Return None if the alias is valid.
if (strpos($alias, ',') !==
false) {
"Alias %s must not contain comma", $alias));
if (strpos($alias, '.') !==
false) {
"Alias %s must not contain period", $alias));
* Results from data that does not meet the attribute exchange 1.0
$this->message =
$message;
* Abstract class containing common code for attribute exchange
* ns_alias: The preferred namespace alias for attribute exchange
* mode: The type of this attribute exchange message. This must be
* overridden in subclasses.
var $ns_uri =
Auth_OpenID_AX_NS_URI;
* Return Auth_OpenID_AX_Error if the mode in the attribute
* exchange arguments does not match what is expected for this
function _checkMode($ax_args)
$mode =
Auth_OpenID::arrayGet($ax_args, 'mode');
if ($mode !=
$this->mode) {
"Expected mode '%s'; got '%s'",
* Return a set of attribute exchange arguments containing the
* basic information that must be in every attribute exchange
return array('mode' =>
$this->mode);
* Represents a single attribute in an attribute exchange
* request. This should be added to an AXRequest object in order to
* Construct an attribute information object. Do not call this
* directly; call make(...) instead.
* @param string $type_uri The type URI for this attribute.
* @param int $count The number of values of this type to request.
* @param bool $required Whether the attribute will be marked as
* required in the request.
* @param string $alias The name that should be given to this
* attribute in the request.
* required: Whether the attribute will be marked as required
* when presented to the subject of the attribute exchange
$this->required =
$required;
* count: How many values of this type to request from the
* subject. Defaults to one.
* type_uri: The identifier that determines what the attribute
* represents and how it is serialized. For example, one type
* URI representing dates could represent a Unix timestamp in
* base 10 and another could represent a human-readable
$this->type_uri =
$type_uri;
* alias: The name that should be given to this attribute in
* the request. If it is not supplied, a generic name will be
* assigned. For example, if you want to call a Unix timestamp
* value 'tstamp', set its alias to that value. If two
* attributes in the same message request to use the same
* alias, the request will fail to be generated.
* Construct an attribute information object. For parameter
* details, see the constructor.
function make($type_uri, $count=
1, $required=
false,
* When processing a request for this attribute, the OP should
* call this method to determine whether all available attribute
* values were requested. If self.count == UNLIMITED_VALUES, this
* returns True. Otherwise this returns False, in which case
* self.count is an integer.
* Given a namespace mapping and a string containing a comma-separated
* list of namespace aliases, return a list of type URIs that
* correspond to those aliases.
* @param $namespace_map The mapping from namespace URI to alias
* @param $alias_list_s The string containing the comma-separated
* list of aliases. May also be None for convenience.
* @return $seq The list of namespace URIs that corresponds to the
* supplied list of aliases. If the string was zero-length or None, an
* empty list will be returned.
* return null If an alias is present in the list of aliases but
* is not present in the namespace map.
foreach (explode(',', $alias_list_s) as $alias) {
$type_uri =
$namespace_map->getNamespaceURI($alias);
if ($type_uri ===
null) {
// 'No type is defined for attribute name %r' % (alias,))
sprintf('No type is defined for attribute name %s',
* An attribute exchange 'fetch_request' message. This message is sent
* by a relying party when it wishes to obtain attributes about the
* subject of an OpenID authentication request.
var $mode =
'fetch_request';
* requested_attributes: The attributes that have been
* requested thus far, indexed by the type URI.
$this->requested_attributes =
array();
* update_url: A URL that will accept responses for this
* attribute exchange request, even in the absence of the user
$this->update_url =
$update_url;
* Add an attribute to this attribute exchange request.
* @param attribute: The attribute that is being requested
* @return true on success, false when the requested attribute is
* already present in this fetch request.
if ($this->contains($attribute->type_uri)) {
sprintf("The attribute %s has already been requested",
$this->requested_attributes[$attribute->type_uri] =
$attribute;
* Get the serialized form of this attribute fetch request.
* @returns Auth_OpenID_AX_FetchRequest The fetch request message parameters
$ax_args =
$this->_newArgs();
foreach ($this->requested_attributes as $type_uri =>
$attribute) {
if ($attribute->alias ===
null) {
$alias =
$aliases->add($type_uri);
$alias =
$aliases->addAlias($type_uri, $attribute->alias);
sprintf("Could not add alias %s for URI %s",
$attribute->alias, $type_uri
if ($attribute->required) {
$if_available[] =
$alias;
if ($attribute->count !=
1) {
$ax_args['count.' .
$alias] =
strval($attribute->count);
$ax_args['type.' .
$alias] =
$type_uri;
$ax_args['required'] =
implode(',', $required);
$ax_args['if_available'] =
implode(',', $if_available);
* Get the type URIs for all attributes that have been marked as
* @return A list of the type URIs for attributes that have been
foreach ($this->requested_attributes as $type_uri =>
$attribute) {
if ($attribute->required) {
* Extract a FetchRequest from an OpenID message
* @param request: The OpenID request containing the attribute
* @returns mixed An Auth_OpenID_AX_Error or the
* Auth_OpenID_AX_FetchRequest extracted from the request message if
$ax_args =
$m->getArgs($obj->ns_uri);
$result =
$obj->parseExtensionArgs($ax_args);
// Update URL must match the openid.realm of the
// underlying OpenID 2 message.
sprintf("Cannot validate update_url %s " .
"against absent realm", $obj->update_url));
sprintf("Update URL %s failed validation against realm %s",
$obj->update_url, $realm));
* Given attribute exchange arguments, populate this FetchRequest.
* @return $result Auth_OpenID_AX_Error if the data to be parsed
* does not follow the attribute exchange specification. At least
* when 'if_available' or 'required' is not specified for a
* particular attribute type. Returns true otherwise.
$result =
$this->_checkMode($ax_args);
foreach ($ax_args as $key =>
$value) {
if (strpos($key, 'type.') ===
0) {
$alias =
$aliases->addAlias($type_uri, $alias);
sprintf("Could not add alias %s for URI %s",
$count_s =
Auth_OpenID::arrayGet($ax_args, 'count.' .
$alias);
$count =
Auth_OpenID::intval($count_s);
if (($count ===
false) &&
sprintf("Integer value expected for %s, got %s",
'count.' .
$alias, $count_s));
Auth_OpenID::arrayGet($ax_args, 'required'));
foreach ($required as $type_uri) {
$attrib =
& $this->requested_attributes[$type_uri];
$attrib->required =
true;
Auth_OpenID::arrayGet($ax_args, 'if_available'));
$all_type_uris =
array_merge($required, $if_available);
foreach ($aliases->iterNamespaceURIs() as $type_uri) {
if (!in_array($type_uri, $all_type_uris)) {
sprintf('Type URI %s was in the request but not ' .
'present in "required" or "if_available"',
$this->update_url =
Auth_OpenID::arrayGet($ax_args, 'update_url');
* Iterate over the AttrInfo objects that are contained in this
* Is the given type URI present in this fetch_request?
* An abstract class that implements a message that has attribute keys
* and values. It contains the common code between fetch_response and
* Add a single value for the given attribute type to the
* message. If there are already values specified for this type,
* this value will be sent in addition to the values already
* @param type_uri: The URI for the attribute
* @param value: The value to add to the response to the relying
* party for this attribute
$this->data[$type_uri] =
array();
$values =
& $this->data[$type_uri];
* Set the values for the given attribute type. This replaces any
* values that have already been set for this attribute.
* @param type_uri: The URI for the attribute
* @param values: A list of values to send for this attribute.
$this->data[$type_uri] =
& $values;
* Get the extension arguments for the key/value pairs contained
* @param aliases: An alias mapping. Set to None if you don't care
* about the aliases for this request.
function _getExtensionKVArgs(&$aliases)
foreach ($this->data as $type_uri =>
$values) {
$alias =
$aliases->add($type_uri);
$ax_args['type.' .
$alias] =
$type_uri;
foreach ($values as $i =>
$value) {
$key =
sprintf('value.%s.%d', $alias, $i +
1);
* Parse attribute exchange key/value arguments into this object.
* @param ax_args: The attribute exchange fetch_response
* arguments, with namespacing removed.
* @return Auth_OpenID_AX_Error or true
$result =
$this->_checkMode($ax_args);
foreach ($ax_args as $key =>
$value) {
if (strpos($key, 'type.') ===
0) {
$alias =
$aliases->addAlias($type_uri, $alias);
sprintf("Could not add alias %s for URI %s",
foreach ($aliases->iteritems() as $pair) {
list
($type_uri, $alias) =
$pair;
$count_key =
'count.' .
$alias;
$count_s =
$ax_args[$count_key];
$count =
Auth_OpenID::intval($count_s);
sprintf("Integer value expected for %s, got %s",
'count. %s' .
$alias, $count_s,
for ($i =
1; $i <
$count +
1; $i++
) {
$value_key =
sprintf('value.%s.%d', $alias, $i);
"No value found for key %s",
$value =
$ax_args[$value_key];
$key =
'value.' .
$alias;
"No value found for key %s",
$value =
$ax_args['value.' .
$alias];
$this->data[$type_uri] =
$values;
* Get a single value for an attribute. If no value was sent for
* this attribute, use the supplied default. If there is more than
* one value for this attribute, this method will fail.
* @param type_uri: The URI for the attribute
* @param default: The value to return if the attribute was not
* sent in the fetch_response.
* @return $value Auth_OpenID_AX_Error on failure or the value of
* the attribute in the fetch_response message, or the default
$values =
Auth_OpenID::arrayGet($this->data, $type_uri);
} else if (count($values) ==
1) {
sprintf('More than one value present for %s',
* Get the list of values for this attribute in the
* XXX: what to do if the values are not present? default
* parameter? this is funny because it's always supposed to return
* a list, so the default may break that, though it's provided by
* the user's code, so it might be okay. If no default is
* supplied, should the return be None or []?
* @param type_uri: The URI of the attribute
* @return $values The list of values for this attribute in the
* response. May be an empty list. If the attribute was not sent
* in the response, returns Auth_OpenID_AX_Error.
return $this->data[$type_uri];
sprintf("Type URI %s not found in response",
* Get the number of responses for a particular attribute in this
* fetch_response message.
* @param type_uri: The URI of the attribute
* @returns int The number of values sent for this attribute. If
* the attribute was not sent in the response, returns
function count($type_uri)
sprintf("Type URI %s not found in response",
* A fetch_response attribute exchange message.
var $mode =
'fetch_response';
$this->update_url =
$update_url;
* Serialize this object into arguments in the attribute exchange
* @return $args The dictionary of unqualified attribute exchange
* arguments that represent this fetch_response, or
* Auth_OpenID_AX_Error on error.
$zero_value_types =
array();
// Validate the data in the context of the request (the
// same attributes should be present in each, and the
// counts in the response must be no more than the counts
foreach ($this->data as $type_uri =>
$unused) {
if (!$request->contains($type_uri)) {
sprintf("Response attribute not present in request: %s",
foreach ($request->iterAttrs() as $attr_info) {
// Copy the aliases from the request so that reading
// the response in light of the request is easier
if ($attr_info->alias ===
null) {
$aliases->add($attr_info->type_uri);
$alias =
$aliases->addAlias($attr_info->type_uri,
sprintf("Could not add alias %s for URI %s",
$attr_info->alias, $attr_info->type_uri)
$values =
$this->data[$attr_info->type_uri];
$zero_value_types[] =
$attr_info;
($attr_info->count <
count($values))) {
sprintf("More than the number of requested values " .
$kv_args =
$this->_getExtensionKVArgs($aliases);
// Add the KV args into the response with the args that are
// unique to the fetch_response
$ax_args =
$this->_newArgs();
// For each requested attribute, put its type/alias and count
// into the response even if no data were returned.
foreach ($zero_value_types as $attr_info) {
$alias =
$aliases->getAlias($attr_info->type_uri);
$kv_args['type.' .
$alias] =
$attr_info->type_uri;
$kv_args['count.' .
$alias] =
'0';
$update_url =
$request->update_url;
$update_url =
$this->update_url;
$ax_args['update_url'] =
$update_url;
Auth_OpenID::update(&$ax_args, $kv_args);
* @return $result Auth_OpenID_AX_Error on failure or true on
$this->update_url =
Auth_OpenID::arrayGet($ax_args, 'update_url');
* Construct a FetchResponse object from an OpenID library
* SuccessResponse object.
* @param success_response: A successful id_res response object
* @param signed: Whether non-signed args should be processsed. If
* True (the default), only signed arguments will be processsed.
* @return $response A FetchResponse containing the data from the
$ax_args =
$success_response->getSignedNS($obj->ns_uri);
$ax_args =
$success_response->message->getArgs($obj->ns_uri);
if ($ax_args ===
null ||
Auth_OpenID::isFailure($ax_args) ||
$result =
$obj->parseExtensionArgs($ax_args);
* A store request attribute exchange message representation.
var $mode =
'store_request';
* @param array $aliases The namespace aliases to use when making
* this store response. Leave as None to use defaults.
$ax_args =
$this->_newArgs();
$kv_args =
$this->_getExtensionKVArgs($aliases);
Auth_OpenID::update(&$ax_args, $kv_args);
* An indication that the store request was processed along with this
* OpenID transaction. Use make(), NOT the constructor, to create
* Returns Auth_OpenID_AX_Error on error or an
* Auth_OpenID_AX_StoreResponse object on success.
function &make($succeeded=
true, $error_message=
null)
if (($succeeded) &&
($error_message !==
null)) {
'included in a failing fetch response');
$this->error_message =
$error_message;
* Was this response a success response?
$ax_args =
$this->_newArgs();
if ((!$this->succeeded()) &&
$this->error_message) {
$ax_args['error'] =
$this->error_message;