diff --git modules/openid/openid.inc modules/openid/openid.inc
index 9223a4d..16cd12e 100644
--- modules/openid/openid.inc
+++ modules/openid/openid.inc
@@ -24,6 +24,16 @@ define('OPENID_NS_1_1', 'http://openid.net/signon/1.1');
 define('OPENID_NS_1_0', 'http://openid.net/signon/1.0');
 
 /**
+ * OpenID Simple Registration extension.
+ */
+define('OPENID_NS_SREG', 'http://openid.net/extensions/sreg/1.1');
+
+/**
+ * OpenID Attribute Exchange extension.
+ */
+define('OPENID_NS_AX', 'http://openid.net/srv/ax/1.0');
+
+/**
  * Performs an HTTP 302 redirect (for the 1.x protocol).
  */
 function openid_redirect_http($url, $message) {
@@ -49,6 +59,120 @@ function openid_redirect($url, $message) {
   exit;
 }
 
+/**
+ * Select a service element.
+ *
+ * The procedure is described in OpenID Authentication 2.0, section 7.3.2.
+ *
+ * A new entry is added to the returned array with the key 'version' and the
+ * value 1 or 2 specifying the protocol version used by the service.
+ *
+ * @param $services
+ *   An array of service arrays as returned by openid_discovery().
+ * @return
+ *   The selected service array, or NULL if no valid services were found.
+ */
+function _openid_select_service($services) {
+  // Extensible Resource Identifier (XRI) Resolution Version 2.0, section 4.3.3:
+  // Find the service with the highest priority (lowest integer value). If there
+  // is a tie, select a random one, not just the first in the XML document.
+  $selected_service = NULL;
+  shuffle($services);
+
+  // Search for an OP Identifier Element.
+  foreach ($services as $service) {
+    if (!empty($service['uri'])) {
+      if (in_array('http://specs.openid.net/auth/2.0/server', $service['types'])) {
+        $service['version'] = 2;
+      }
+      elseif (in_array(OPENID_NS_1_0, $service['types']) || in_array(OPENID_NS_1_1, $service['types'])) {
+        $service['version'] = 1;
+      }
+      if (isset($service['version']) && (!$selected_service || $service['priority'] < $selected_service['priority'])) {
+        $selected_service = $service;
+      }
+    }
+  }
+
+  if (!$selected_service) {
+    // Search for Claimed Identifier Element.
+    foreach ($services as $service) {
+      if (!empty($service['uri']) && in_array('http://specs.openid.net/auth/2.0/signon', $service['types'])) {
+        $service['version'] = 2;
+        if (!$selected_service || $service['priority'] < $selected_service['priority']) {
+          $selected_service = $service;
+        }
+      }
+    }
+  }
+
+  if ($selected_service) {
+    // Unset SimpleXMLElement instances that cannot be saved in $_SESSION.
+    unset($selected_service['xrd']);
+    unset($selected_service['service']);
+  }
+
+  return $selected_service;
+}
+
+/**
+ * Extract all the parameters belonging to an extension in a response message.
+ *
+ * OpenID 2.0 defines a simple extension mechanism, based on a namespace prefix.
+ *
+ * Each request or response can define a prefix using:
+ * @code
+ *   openid.ns.[prefix] = [extension_namespace]
+ *   openid.[prefix].[key1] = [value1]
+ *   openid.[prefix].[key2] = [value2]
+ *   ...
+ * @endcode
+ *
+ * This function extracts all the keys belonging to an extension namespace in a
+ * response, optionally using a fallback prefix if none is provided in the response.
+ *
+ * Note that you cannot assume that a given extension namespace will use the same
+ * prefix on the response and the request: each party may use a different prefix
+ * to refer to the same namespace.
+ *
+ * @param $response
+ *   The response array.
+ * @param $extension_namespace
+ *   The namespace of the extension.
+ * @param $fallback_prefix
+ *   An optional prefix that will be used in case no prefix is found for the
+ *   target extension namespace.
+ * @return
+ *   An associative array containing all the parameters in the response message
+ *   that belong to the extension. The keys are stripped from their namespace
+ *   prefix.
+ * @see http://openid.net/specs/openid-authentication-2_0.html#extensions
+ */
+function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL) {
+  // Find the namespace prefix.
+  $prefix = $fallback_prefix;
+  foreach ($response as $key => $value) {
+    if ($value == $extension_namespace && preg_match('/^openid\.ns\.([^.]+)$/', $key, $matches)) {
+      $prefix = $matches[1];
+      break;
+    }
+  }
+
+  // Now extract the namespace keys from the response.
+  $output = array();
+  if (!isset($prefix)) {
+    return $output;
+  }
+  foreach ($response as $key => $value) {
+    if (preg_match('/^openid\.' . $prefix . '\.(.+)$/', $key, $matches)) {
+      $local_key = $matches[1];
+      $output[$local_key] = $value;
+    }
+  }
+
+  return $output;
+}
+
 function openid_redirect_form(&$form_state, $url, $message) {
   $form = array();
   $form['#action'] = $url;
@@ -392,7 +516,7 @@ function _openid_get_bytes($num_bytes) {
 
 function _openid_response($str = NULL) {
   $data = array();
-  
+
   if (isset($_SERVER['REQUEST_METHOD'])) {
     $data = _openid_get_params($_SERVER['QUERY_STRING']);
 
@@ -443,3 +567,38 @@ if (!function_exists('bcpowmod')) {
     return $result;
   }
 }
+
+/**
+ * Extracts values from an OpenID AX Response.
+ *
+ * The values can be returned in two forms:
+ *   - only openid.ax.value.<alias> (for single-valued answers)
+ *   - both openid.ax.count.<alias> and openid.ax.value.<alias>.<count> (for both
+ *     single and multiple-valued answers)
+ *
+ * @param $values
+ *   An array as returned by openid_extract_namespace(..., OPENID_NS_AX).
+ * @param $uris
+ *   An array of identifier URIs.
+ * @return
+ *   An array of values.
+ * @see http://openid.net/specs/openid-attribute-exchange-1_0.html#fetch_response
+ */
+function openid_extract_ax_values($values, $uris) {
+  $output = array();
+  foreach ($values as $key => $value) {
+    if (in_array($value, $uris) && preg_match('/^type\.([^.]+)$/', $key, $matches)) {
+      $alias = $matches[1];
+      if (isset($values['count.' . $alias])) {
+        for ($i = 1; $i <= $values['count.' . $alias]; $i++) {
+          $output[] = $values['value.' . $alias . '.' . $i];
+        }
+      }
+      elseif (isset($values['value.' . $alias])) {
+        $output[] = $values['value.' . $alias];
+      }
+      break;
+    }
+  }
+  return $output;
+}
diff --git modules/openid/openid.module modules/openid/openid.module
index 2db36e1..2e373c2 100644
--- modules/openid/openid.module
+++ modules/openid/openid.module
@@ -44,17 +44,17 @@ function openid_menu() {
 function openid_help($path, $arg) {
   switch ($path) {
     case 'user/%/openid':
-      $output = '<p>'. t('This site supports <a href="@openid-net">OpenID</a>, a secure way to log into many websites using a single username and password. OpenID can reduce the necessity of managing many usernames and passwords for many websites.', array('@openid-net' => 'http://openid.net')) .'</p>';
-      $output .= '<p>'. t('To use OpenID you must first establish an identity on a public or private OpenID server. If you do not have an OpenID and would like one, look into one of the <a href="@openid-providers">free public providers</a>. You can find out more about OpenID at <a href="@openid-net">this website</a>.', array('@openid-providers' => 'http://openid.net/get/', '@openid-net' => 'http://openid.net')) .'</p>';
-      $output .= '<p>'. t('If you already have an OpenID, enter the URL to your OpenID server below (e.g. myusername.openidprovider.com). Next time you login, you will be able to use this URL instead of a regular username and password. You can have multiple OpenID servers if you like; just keep adding them here.') .'</p>';
+      $output = '<p>' . t('This site supports <a href="@openid-net">OpenID</a>, a secure way to log into many websites using a single username and password. OpenID can reduce the necessity of managing many usernames and passwords for many websites.', array('@openid-net' => 'http://openid.net')) . '</p>';
+      $output .= '<p>' . t('To use OpenID you must first establish an identity on a public or private OpenID server. If you do not have an OpenID and would like one, look into one of the <a href="@openid-providers">free public providers</a>. You can find out more about OpenID at <a href="@openid-net">this website</a>.', array('@openid-providers' => 'http://openid.net/get/', '@openid-net' => 'http://openid.net')) . '</p>';
+      $output .= '<p>' . t('If you already have an OpenID, enter the URL to your OpenID server below (e.g. myusername.openidprovider.com). Next time you login, you will be able to use this URL instead of a regular username and password. You can have multiple OpenID servers if you like; just keep adding them here.') . '</p>';
       return $output;
 
     case 'admin/help#openid':
-      $output = '<p>'. t('OpenID is a secure method for logging into many websites with a single username and password. It does not require special software, and it does not share passwords with any site to which it is associated; including your site.') .'</p>';
-      $output .= '<p>'. t('Users can create accounts using their OpenID, assign one or more OpenIDs to an existing account, and log in using an OpenID. This lowers the barrier to registration, which is good for the site, and offers convenience and security to the users. OpenID is not a trust system, so email verification is still necessary. The benefit stems from the fact that users can have a single password that they can use on many websites. This means they can easily update their single password from a centralized location, rather than having to change dozens of passwords individually.') .'</p>';
-      $output .= '<p>'. t('The basic concept is as follows: A user has an account on an OpenID server. This account provides them with a unique URL (such as myusername.openidprovider.com). When the user comes to your site, they are presented with the option of entering this URL. Your site then communicates with the OpenID server, asking it to verify the identity of the user. If the user is logged into their OpenID server, the server communicates back to your site, verifying the user. If they are not logged in, the OpenID server will ask the user for their password. At no point does your site record, or need to record the user\'s password.') .'</p>';
-      $output .= '<p>'. t('More information on OpenID is available at <a href="@openid-net">OpenID.net</a>.', array('@openid-net' => url('http://openid.net'))) .'</p>';
-      $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@handbook">OpenID module</a>.', array('@handbook' => 'http://drupal.org/handbook/modules/openid')) .'</p>';
+      $output = '<p>' . t('OpenID is a secure method for logging into many websites with a single username and password. It does not require special software, and it does not share passwords with any site to which it is associated; including your site.') . '</p>';
+      $output .= '<p>' . t('Users can create accounts using their OpenID, assign one or more OpenIDs to an existing account, and log in using an OpenID. This lowers the barrier to registration, which is good for the site, and offers convenience and security to the users. OpenID is not a trust system, so email verification is still necessary. The benefit stems from the fact that users can have a single password that they can use on many websites. This means they can easily update their single password from a centralized location, rather than having to change dozens of passwords individually.') . '</p>';
+      $output .= '<p>' . t('The basic concept is as follows: A user has an account on an OpenID server. This account provides them with a unique URL (such as myusername.openidprovider.com). When the user comes to your site, they are presented with the option of entering this URL. Your site then communicates with the OpenID server, asking it to verify the identity of the user. If the user is logged into their OpenID server, the server communicates back to your site, verifying the user. If they are not logged in, the OpenID server will ask the user for their password. At no point does your site record, or need to record the user\'s password.') . '</p>';
+      $output .= '<p>' . t('More information on OpenID is available at <a href="@openid-net">OpenID.net</a>.', array('@openid-net' => url('http://openid.net'))) . '</p>';
+      $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@handbook">OpenID module</a>.', array('@handbook' => 'http://drupal.org/handbook/modules/openid')) . '</p>';
       return $output;
   }
 }
@@ -77,8 +77,8 @@ function openid_user($op, &$edit, &$account, $category = NULL) {
  */
 function openid_form_alter(&$form, $form_state, $form_id) {
   if ($form_id == 'user_login_block' || $form_id == 'user_login') {
-    drupal_add_css(drupal_get_path('module', 'openid') .'/openid.css', 'module');
-    drupal_add_js(drupal_get_path('module', 'openid') .'/openid.js');
+    drupal_add_css(drupal_get_path('module', 'openid') . '/openid.css', 'module');
+    drupal_add_js(drupal_get_path('module', 'openid') . '/openid.js');
     if (!empty($form_state['post']['openid_identifier'])) {
       $form['name']['#required'] = FALSE;
       $form['pass']['#required'] = FALSE;
@@ -163,20 +163,22 @@ function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
   $claimed_id = _openid_normalize($claimed_id);
 
   $services = openid_discovery($claimed_id);
-  if (count($services) == 0) {
+  $service = _openid_select_service($services);
+
+  if (!$service) {
     form_set_error('openid_identifier', t('Sorry, that is not a valid OpenID. Please ensure you have spelled your ID correctly.'));
     return;
   }
 
   // Store discovered information in the users' session so we don't have to rediscover.
-  $_SESSION['openid']['service'] = $services[0];
+  $_SESSION['openid']['service'] = $service;
   // Store the claimed id
   $_SESSION['openid']['claimed_id'] = $claimed_id;
   // Store the login form values so we can pass them to
   // user_exteral_login later.
   $_SESSION['openid']['user_login_values'] = $form_values;
 
-  $op_endpoint = $services[0]['uri'];
+  $op_endpoint = $service['uri'];
   // If bcmath is present, then create an association
   $assoc_handle = '';
   if (function_exists('bcadd')) {
@@ -187,26 +189,26 @@ function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
   // to request authentication from the IdP
   // First check for LocalID. If not found, check for Delegate. Fall
   // back to $claimed_id if neither is found.
-  if (!empty($services[0]['localid'])) {
-    $identity = $services[0]['localid'];
+  if (!empty($service['localid'])) {
+    $identity = $service['localid'];
   }
-  else if (!empty($services[0]['delegate'])) {
-    $identity = $services[0]['delegate'];
+  else if (!empty($service['delegate'])) {
+    $identity = $service['delegate'];
   }
   else {
     $identity = $claimed_id;
   }
 
-  if (isset($services[0]['types']) && is_array($services[0]['types']) && in_array(OPENID_NS_2_0 .'/server', $services[0]['types'])) {
+  if (isset($service['types']) && is_array($service['types']) && in_array(OPENID_NS_2_0 . '/server', $service['types'])) {
     $claimed_id = $identity = 'http://specs.openid.net/auth/2.0/identifier_select';
   }
-  $authn_request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $services[0]['version']);
+  $request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $service);
 
-  if ($services[0]['version'] == 2) {
-    openid_redirect($op_endpoint, $authn_request);
+  if ($service['version'] == 2) {
+    openid_redirect($op_endpoint, $request);
   }
   else {
-    openid_redirect_http($op_endpoint, $authn_request);
+    openid_redirect_http($op_endpoint, $request);
   }
 }
 
@@ -259,7 +261,7 @@ function openid_complete($response = array()) {
           $return_to_parts = parse_url($response['openid.return_to']);
 
           $base_url_parts = parse_url($base_url);
-          $current_parts = parse_url($base_url_parts['scheme'] .'://'. $base_url_parts['host'] . request_uri());
+          $current_parts = parse_url($base_url_parts['scheme'] . '://' . $base_url_parts['host'] . request_uri());
 
           if ($return_to_parts['scheme'] != $current_parts['scheme'] ||
               $return_to_parts['host'] != $current_parts['host'] ||
@@ -300,7 +302,7 @@ function openid_discovery($claimed_id) {
 
   $xrds_url = $claimed_id;
   if (_openid_is_xri($claimed_id)) {
-    $xrds_url = 'http://xri.net/'. $claimed_id;
+    $xrds_url = 'http://xri.net/' . $claimed_id;
   }
   $url = @parse_url($xrds_url);
   if ($url['scheme'] == 'http' || $url['scheme'] == 'https') {
@@ -396,7 +398,7 @@ function openid_association($op_endpoint) {
       $assoc_response['mac_key'] = base64_encode(_openid_dh_xorsecret($shared, $enc_mac_key));
     }
     db_query("INSERT INTO {openid_association} (idp_endpoint_uri, session_type, assoc_handle, assoc_type, expires_in, mac_key, created) VALUES('%s', '%s', '%s', '%s', %d, '%s', %d)",
-             $op_endpoint, $assoc_response['session_type'], $assoc_response['assoc_handle'], $assoc_response['assoc_type'], $assoc_response['expires_in'], $assoc_response['mac_key'], time());
+      $op_endpoint, $assoc_response['session_type'], $assoc_response['assoc_handle'], $assoc_response['assoc_type'], $assoc_response['expires_in'], $assoc_response['mac_key'], time());
 
     $assoc_handle = $assoc_response['assoc_handle'];
   }
@@ -424,11 +426,37 @@ function openid_authentication($response) {
     }
   }
   elseif (variable_get('user_register', 1)) {
-    // Register new user
+    // Register new user.
+
+    // Extract Simple Registration keys from the response.
+    $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg');
+    // Extract Attribute Exchanges keys from the response.
+    $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax');
+
     $form_state['redirect'] = NULL;
-    $form_state['values']['name'] = (empty($response['openid.sreg.nickname'])) ? '' : $response['openid.sreg.nickname'];
-    $form_state['values']['mail'] = (empty($response['openid.sreg.email'])) ? '' : $response['openid.sreg.email'];
-    $form_state['values']['pass']  = user_password();
+
+    if (!empty($sreg_values['nickname'])) {
+      // Use the nickname returned by Simple Registration if available.
+      $form_state['values']['name'] = $sreg_values['nickname'];
+    }
+    elseif ($ax_name_values = openid_extract_ax_values($ax_values, array('http://axschema.org/namePerson/friendly', 'http://schema.openid.net/namePerson/friendly'))) {
+      // Else, use the first nickname returned by AX if available.
+      $form_state['values']['name'] = current($ax_name_values);
+    }
+    else {
+      $form_state['values']['name'] = '';
+    }
+
+    if (!empty($sreg_values['email'])) {
+      // Use the email returned by Simple Registration if available.
+      $form_state['values']['mail'] = $sreg_values['email'];
+    }
+    elseif ($ax_mail_values = openid_extract_ax_values($ax_values, array('http://axschema.org/contact/email', 'http://schema.openid.net/contact/email'))) {
+      // Else, use the first nickname returned by AX if available.
+      $form_state['values']['mail'] = current($ax_mail_values);
+    }
+
+    $form_state['values']['pass'] = user_password();
     $form_state['values']['status'] = variable_get('user_register', 1) == 1;
     $form_state['values']['response'] = $response;
     $form_state['values']['auth_openid'] = $identity;
@@ -447,7 +475,7 @@ function openid_authentication($response) {
         // Append form validation errors below the above warning.
         $messages = drupal_get_messages('error');
         foreach ($messages['error'] as $message) {
-          drupal_set_message( $message, 'error');
+          drupal_set_message($message, 'error');
         }
       }
     }
@@ -496,11 +524,11 @@ function openid_association_request($public) {
   return $request;
 }
 
-function openid_authentication_request($claimed_id, $identity, $return_to = '', $assoc_handle = '', $version = 2) {
+function openid_authentication_request($claimed_id, $identity, $return_to = '', $assoc_handle = '', $service) {
   module_load_include('inc', 'openid');
 
-  $ns = ($version == 2) ? OPENID_NS_2_0 : OPENID_NS_1_0;
-  $request =  array(
+  $ns = ($service['version'] == 2) ? OPENID_NS_2_0 : OPENID_NS_1_0;
+  $request = array(
     'openid.ns' => $ns,
     'openid.mode' => 'checkid_setup',
     'openid.identity' => $identity,
@@ -509,16 +537,39 @@ function openid_authentication_request($claimed_id, $identity, $return_to = '',
     'openid.return_to' => $return_to,
   );
 
-  if ($version == 2) {
+  if ($service['version'] == 2) {
     $request['openid.realm'] = url('', array('absolute' => TRUE));
   }
   else {
     $request['openid.trust_root'] = url('', array('absolute' => TRUE));
   }
 
-  // Simple Registration
+  // Always request Simple Registration. The specification doesn't mandate
+  // that the Endpoint advertise OPENID_NS_SREG in the service description.
+  $request['openid.ns.sreg'] = OPENID_NS_SREG;
   $request['openid.sreg.required'] = 'nickname,email';
-  $request['openid.ns.sreg'] = "http://openid.net/extensions/sreg/1.1";
+
+  // Request Attribute Exchange, if available.
+  // We only request the minimum attributes we need here, contributed modules
+  // can alter the request to add more attribute, and map them to profile fields.
+  if (in_array(OPENID_NS_AX, $service['types'])) {
+    $request['openid.ns.ax'] = OPENID_NS_AX;
+    $request['openid.ax.mode'] = 'fetch_request';
+    $request['openid.ax.required'] = 'mail_ao,name_ao,mail_son,name_son';
+
+    // Implementors disagree on which URIs to use, even for simple
+    // attributes like name and email (*sigh*). We ask for both axschema.org
+    // attributes (which are supposed to be newer), and schema.openid.net ones
+    // (which are supposed to be legacy).
+
+    // Attributes as defined by axschema.org.
+    $request['openid.ax.type.mail_ao'] = 'http://axschema.org/contact/email';
+    $request['openid.ax.type.name_ao'] = 'http://axschema.org/namePerson/friendly';
+
+    // Attributes as defined by schema.openid.net.
+    $request['openid.ax.type.mail_son'] = 'http://schema.openid.net/contact/email';
+    $request['openid.ax.type.name_son'] = 'http://schema.openid.net/namePerson/friendly';
+  }
 
   $request = array_merge($request, module_invoke_all('openid', 'request', $request));
 
