18ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen<?php 28ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 38ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * This class provides a simple interface for OpenID (1.1 and 2.0) authentication. 48ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Supports Yadis discovery. 58ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The authentication process is stateless/dumb. 68ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 78ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Usage: 88ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Sign-on with OpenID is a two step process: 98ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Step one is authentication with the provider: 108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * <code> 118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid = new LightOpenID; 128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid->identity = 'ID supplied by user'; 138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * header('Location: ' . $openid->authUrl()); 148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * </code> 158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The provider then sends various parameters via GET, one of them is openid_mode. 168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Step two is verification: 178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * <code> 188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * if ($this->data['openid_mode']) { 198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid = new LightOpenID; 208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * echo $openid->validate() ? 'Logged in.' : 'Failed'; 218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * } 228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * </code> 238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Optionally, you can set $returnUrl and $realm (or $trustRoot, which is an alias). 258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The default values for those are: 268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid->realm = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; 278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI']; 288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * If you don't know their meaning, refer to any openid tutorial, or specification. Or just guess. 298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * AX and SREG extensions are supported. 318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * To use them, specify $openid->required and/or $openid->optional. 328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * These are arrays, with values being AX schema paths (the 'path' part of the URL). 338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * For example: 348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid->required = array('namePerson/friendly', 'contact/email'); 358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $openid->optional = array('namePerson/first'); 368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * If the server supports only SREG or OpenID 1.1, these are automaticaly 378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * mapped to SREG names, so that user doesn't have to know anything about the server. 388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * To get the values, use $openid->getAttributes(). 408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The library depends on curl, and requires PHP 5. 438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @author Mewp 448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @copyright Copyright (c) 2010, Mewp 458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @license http://www.opensource.org/licenses/mit-license.php MIT 468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenclass LightOpenID 488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen{ 498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen public $returnUrl 508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen , $required = array() 518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen , $optional = array(); 528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen private $identity, $claimed_id; 538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected $server, $version, $trustRoot, $aliases, $identifier_select = false 548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen , $ax = false, $sreg = false, $data; 558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen static protected $ax_to_sreg = array( 568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'namePerson/friendly' => 'nickname', 578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'contact/email' => 'email', 588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'namePerson' => 'fullname', 598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'birthDate' => 'dob', 608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'person/gender' => 'gender', 618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'contact/postalCode/home' => 'postcode', 628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'contact/country/home' => 'country', 638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'pref/language' => 'language', 648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'pref/timezone' => 'timezone', 658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ); 668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function __construct() 688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->trustRoot = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; 708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->returnUrl = $this->trustRoot . $_SERVER['REQUEST_URI']; 718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!function_exists('curl_exec')) { 738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen throw new ErrorException('Curl extension is required.'); 748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->data = $_POST + $_GET; # OPs may send data as POST or GET. 778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function __set($name, $value) 808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen switch ($name) { 828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen case 'identity': 838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (strlen($value = trim($value))) { 848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (preg_match('#^xri:/*#i', $value, $m)) { 858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $value = substr($value, strlen($m[0])); 868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) { 878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $value = "http://$value"; 888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (preg_match('#^https?://[^/]+$#i', $value, $m)) { 908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $value .= '/'; 918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->$name = $this->claimed_id = $value; 948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen break; 958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen case 'trustRoot': 968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen case 'realm': 978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->trustRoot = trim($value); 988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function __get($name) 1028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 1038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen switch ($name) { 1048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen case 'identity': 1058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We return claimed_id instead of identity, 1068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # because the developer should see the claimed identifier, 1078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # i.e. what he set as identity, not the op-local identifier (which is what we verify) 1088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->claimed_id; 1098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen case 'trustRoot': 1108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen case 'realm': 1118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->trustRoot; 1128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function request($url, $method='GET', $params=array()) 1168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 1178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params = http_build_query($params, '', '&'); 1188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : '')); 1198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 1208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_HEADER, false); 1218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 1228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 1238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($method == 'POST') { 1248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_POST, true); 1258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_POSTFIELDS, $params); 1268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } elseif ($method == 'HEAD') { 1278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_HEADER, true); 1288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_NOBODY, true); 1298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } else { 1308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen curl_setopt($curl, CURLOPT_HTTPGET, true); 1318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $response = curl_exec($curl); 1338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (curl_errno($curl)) { 1358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen throw new ErrorException(curl_error($curl), curl_errno($curl)); 1368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $response; 1398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function build_url($url, $parts) 1428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 1438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (isset($url['query'], $parts['query'])) { 1448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $parts['query'] = $url['query'] . '&' . $parts['query']; 1458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $url = $parts + $url; 1488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $url = $url['scheme'] . '://' 1498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen . (empty($url['username'])?'' 1508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen :(empty($url['password'])? "{$url['username']}@" 1518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen :"{$url['username']}:{$url['password']}@")) 1528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen . $url['host'] 1538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen . (empty($url['port'])?'':":{$url['port']}") 1548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen . (empty($url['path'])?'':$url['path']) 1558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen . (empty($url['query'])?'':"?{$url['query']}") 1568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen . (empty($url['fragment'])?'':":{$url['fragment']}"); 1578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $url; 1588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 1618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Helper function used to scan for <meta>/<link> tags and extract information 1628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * from them 1638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName) 1658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 1668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1); 1678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2); 1688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $result = array_merge($matches1[1], $matches2[1]); 1708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return empty($result)?false:$result[0]; 1718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 1748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Performs Yadis and HTML discovery. Normally not used. 1758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param $url Identity URL. 1768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return String OP Endpoint (i.e. OpenID provider address). 1778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @throws ErrorException 1788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function discover($url) 1808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 1818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!$url) throw new ErrorException('No identity supplied.'); 1828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Use xri.net proxy to resolve i-name identities 1838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!preg_match('#^https?:#', $url)) { 1848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $url = "https://xri.net/$url"; 1858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We save the original url in case of Yadis discovery failure. 1888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # It can happen when we'll be lead to an XRDS document 1898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # which does not have any OpenID2 services. 1908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $originalUrl = $url; 1918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # A flag to disable yadis discovery in case of failure in headers. 1938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $yadis = true; 1948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We'll jump a maximum of 5 times, to avoid endless redirections. 1968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen for ($i = 0; $i < 5; $i ++) { 1978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($yadis) { 1988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $headers = explode("\n",$this->request($url, 'HEAD')); 1998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $next = false; 2018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($headers as $header) { 2028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (preg_match('#X-XRDS-Location\s*:\s*(.*)#', $header, $m)) { 2038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $url = $this->build_url(parse_url($url), parse_url(trim($m[1]))); 2048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $next = true; 2058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (preg_match('#Content-Type\s*:\s*application/xrds\+xml#i', $header)) { 2088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Found an XRDS document, now let's find the server, and optionally delegate. 2098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $content = $this->request($url, 'GET'); 2108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # OpenID 2 2128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We ignore it for MyOpenID, as it breaks sreg if using OpenID 2.0 2138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $ns = preg_quote('http://specs.openid.net/auth/2.0/'); 2148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (preg_match('#<Service.*?>(.*)<Type>\s*'.$ns.'(.*?)\s*</Type>(.*)</Service>#s', $content, $m)) { 2158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $content = ' ' . $m[1] . $m[3]; # The space is added, so that strpos doesn't return 0. 2168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($m[2] == 'server') $this->identifier_select = true; 2178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen preg_match('#<URI.*?>(.*)</URI>#', $content, $server); 2198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate); 2208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (empty($server)) { 2218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return false; 2228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Does the server advertise support for either AX or SREG? 2248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>'); 2258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') 2268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); 2278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $server = $server[1]; 2298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (isset($delegate[2])) $this->identity = trim($delegate[2]); 2308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->version = 2; 2318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->server = $server; 2338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $server; 2348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # OpenID 1.1 2378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $ns = preg_quote('http://openid.net/signon/1.1'); 2388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (preg_match('#<Service.*?>(.*)<Type>\s*'.$ns.'\s*</Type>(.*)</Service>#s', $content, $m)) { 2398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $content = ' ' . $m[1] . $m[2]; 2408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen preg_match('#<URI.*?>(.*)</URI>#', $content, $server); 2428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate); 2438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (empty($server)) { 2448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return false; 2458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # AX can be used only with OpenID 2.0, so checking only SREG 2478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') 2488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); 2498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $server = $server[1]; 2518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (isset($delegate[1])) $this->identity = $delegate[1]; 2528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->version = 1; 2538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->server = $server; 2558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $server; 2568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $next = true; 2598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $yadis = false; 2608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $url = $originalUrl; 2618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $content = null; 2628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen break; 2638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($next) continue; 2668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # There are no relevant information in headers, so we search the body. 2688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $content = $this->request($url, 'GET'); 2698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'value')) { 2708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $url = $this->build_url(parse_url($url), parse_url($location)); 2718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen continue; 2728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!$content) $content = $this->request($url, 'GET'); 2768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # At this point, the YADIS Discovery has failed, so we'll switch 2788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # to openid2 HTML discovery, then fallback to openid 1.1 discovery. 2798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href'); 2808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href'); 2818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->version = 2; 2828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!$server) { 2848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # The same with openid 1.1 2858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href'); 2868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href'); 2878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->version = 1; 2888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($server) { 2918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We found an OpenID2 OP Endpoint 2928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($delegate) { 2938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We have also found an OP-Local ID. 2948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->identity = $delegate; 2958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->server = $server; 2978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $server; 2988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen throw new ErrorException('No servers found!'); 3018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen throw new ErrorException('Endless redirection!'); 3038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function sregParams() 3068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 3078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params = array(); 3088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We always use SREG 1.1, even if the server is advertising only support for 1.0. 3098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # That's because it's fully backwards compatibile with 1.0, and some providers 3108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com 3118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1'; 3128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($this->required) { 3138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.sreg.required'] = array(); 3148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->required as $required) { 3158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!isset(self::$ax_to_sreg[$required])) continue; 3168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.sreg.required'][] = self::$ax_to_sreg[$required]; 3178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']); 3198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($this->optional) { 3228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.sreg.optional'] = array(); 3238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->optional as $optional) { 3248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!isset(self::$ax_to_sreg[$optional])) continue; 3258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional]; 3268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']); 3288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $params; 3308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function axParams() 3328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 3338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params = array(); 3348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($this->required || $this->optional) { 3358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; 3368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ax.mode'] = 'fetch_request'; 3378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->aliases = array(); 3388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $counts = array(); 3398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $required = array(); 3408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $optional = array(); 3418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach (array('required','optional') as $type) { 3428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->$type as $alias => $field) { 3438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (is_int($alias)) $alias = strtr($field, '/', '_'); 3448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->aliases[$alias] = 'http://axschema.org/' . $field; 3458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (empty($counts[$alias])) $counts[$alias] = 0; 3468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $counts[$alias] += 1; 3478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ${$type}[] = $alias; 3488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->aliases as $alias => $ns) { 3518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ax.type.' . $alias] = $ns; 3528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($counts as $alias => $count) { 3548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($count == 1) continue; 3558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ax.count.' . $alias] = $count; 3568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Don't send empty ax.requied and ax.if_available. 3598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Google and possibly other providers refuse to support ax when one of these is empty. 3608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if($required) { 3618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ax.required'] = implode(',', $required); 3628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if($optional) { 3648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ax.if_available'] = implode(',', $optional); 3658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $params; 3688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function authUrl_v1() 3718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 3728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $returnUrl = $this->returnUrl; 3738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # If we have an openid.delegate that is different from our claimed id, 3748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # we need to somehow preserve the claimed id between requests. 3758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # The simplest way is to just send it along with the return_to url. 3768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if($this->identity != $this->claimed_id) { 3778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id; 3788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params = array( 3818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.return_to' => $returnUrl, 3828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.mode' => 'checkid_setup', 3838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.identity' => $this->identity, 3848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.trust_root' => $this->trustRoot, 3858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ) + $this->sregParams(); 3868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->build_url(parse_url($this->server) 3888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen , array('query' => http_build_query($params, '', '&'))); 3898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function authUrl_v2($identifier_select) 3928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 3938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params = array( 3948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.ns' => 'http://specs.openid.net/auth/2.0', 3958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.mode' => 'checkid_setup', 3968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.return_to' => $this->returnUrl, 3978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.realm' => $this->trustRoot, 3988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ); 3998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($this->ax) { 4008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params += $this->axParams(); 4018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($this->sreg) { 4038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params += $this->sregParams(); 4048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!$this->ax && !$this->sreg) { 4068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # If OP doesn't advertise either SREG, nor AX, let's send them both 4078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # in worst case we don't get anything in return. 4088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params += $this->axParams() + $this->sregParams(); 4098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($identifier_select) { 4128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.identity'] = $params['openid.claimed_id'] 4138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen = 'http://specs.openid.net/auth/2.0/identifier_select'; 4148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } else { 4158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.identity'] = $this->identity; 4168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.claimed_id'] = $this->claimed_id; 4178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->build_url(parse_url($this->server) 4208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen , array('query' => http_build_query($params, '', '&'))); 4218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 4248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Returns authentication url. Usually, you want to redirect your user to it. 4258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return String The authentication url. 4268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1. 4278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @throws ErrorException 4288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 4298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function authUrl($identifier_select = null) 4308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 4318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!$this->server) $this->discover($this->identity); 4328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($this->version == 2) { 4348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if ($identifier_select === null) { 4358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->authUrl_v2($this->identifier_select); 4368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->authUrl_v2($identifier_select); 4388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->authUrl_v1(); 4408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 4438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Performs OpenID verification with the OP. 4448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return Bool Whether the verification was successful. 4458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @throws ErrorException 4468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 4478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function validate() 4488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 4498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity']; 4508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params = array( 4518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.assoc_handle' => $this->data['openid_assoc_handle'], 4528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.signed' => $this->data['openid_signed'], 4538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 'openid.sig' => $this->data['openid_sig'], 4548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ); 4558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (isset($this->data['openid_op_endpoint'])) { 4578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We're dealing with an OpenID 2.0 server, so let's set an ns 4588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Even though we should know location of the endpoint, 4598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # we still need to verify it by discovery, so $server is not set here 4608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; 4618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $server = $this->discover($this->data['openid_identity']); 4638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach (explode(',', $this->data['openid_signed']) as $item) { 4658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Checking whether magic_quotes_gpc is turned on, because 4668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # the function may fail if it is. For example, when fetching 4678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # AX namePerson, it might containg an apostrophe, which will be escaped. 4688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # In such case, validation would fail, since we'd send different data than OP 4698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # wants to verify. stripslashes() should solve that problem, but we can't 4708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # use it when magic_quotes is off. 4718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $value = $this->data['openid_' . str_replace('.','_',$item)]; 4728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.' . $item] = get_magic_quotes_gpc() ? stripslashes($value) : $value; 4738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $params['openid.mode'] = 'check_authentication'; 4768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $response = $this->request($server, 'POST', $params); 4788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 4798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return preg_match('/is_valid\s*:\s*true/i', $response); 4808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function getAxAttributes() 4828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 4838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $alias = null; 4848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (isset($this->data['openid_ns_ax']) 4858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0' 4868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ) { # It's the most likely case, so we'll check it before 4878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $alias = 'ax'; 4888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } else { 4898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # 'ax' prefix is either undefined, or points to another extension, 4908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # so we search for another prefix 4918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->data as $key => $val) { 4928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_' 4938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen && $val == 'http://openid.net/srv/ax/1.0' 4948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ) { 4958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $alias = substr($key, strlen('openid_ns_')); 4968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen break; 4978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!$alias) { 5018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # An alias for AX schema has not been found, 5028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # so there is no AX data in the OP's response 5038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return array(); 5048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 5068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->data as $key => $value) { 5078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $keyMatch = 'openid_' . $alias . '_value_'; 5088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { 5098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen continue; 5108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $key = substr($key, strlen($keyMatch)); 5128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!isset($this->data['openid_' . $alias . '_type_' . $key])) { 5138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # OP is breaking the spec by returning a field without 5148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # associated ns. This shouldn't happen, but it's better 5158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # to check, than cause an E_NOTICE. 5168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen continue; 5178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $key = substr($this->data['openid_' . $alias . '_type_' . $key], 5198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen strlen('http://axschema.org/')); 5208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $attributes[$key] = $value; 5218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # Found the AX attributes, so no need to scan for SREG. 5238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $attributes; 5248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen protected function getSregAttributes() 5268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 5278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $attributes = array(); 5288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $sreg_to_ax = array_flip(self::$ax_to_sreg); 5298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen foreach ($this->data as $key => $value) { 5308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $keyMatch = 'openid_sreg_'; 5318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { 5328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen continue; 5338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $key = substr($key, strlen($keyMatch)); 5358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!isset($sreg_to_ax[$key])) { 5368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # The field name isn't part of the SREG spec, so we ignore it. 5378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen continue; 5388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $attributes[$sreg_to_ax[$key]] = $value; 5408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $attributes; 5428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 5448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Gets AX/SREG attributes provided by OP. should be used only after successful validaton. 5458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Note that it does not guarantee that any of the required/optional parameters will be present, 5468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * or that there will be no other attributes besides those specified. 5478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * In other words. OP may provide whatever information it wants to. 5488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * * SREG names will be mapped to AX names. 5498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email' 5508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @see http://www.axschema.org/types/ 5518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 5528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen function getAttributes() 5538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen { 5548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen $attributes; 5558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (isset($this->data['openid_ns']) 5568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0' 5578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ) { # OpenID 2.0 5588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen # We search for both AX and SREG attributes, with AX taking precedence. 5598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->getAxAttributes() + $this->getSregAttributes(); 5608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return $this->getSregAttributes(); 5628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 5638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 564