yii\authclient\OpenId::discover PHP Method

discover() public method

Performs Yadis and HTML discovery.
public discover ( string $url ) : array
$url string Identity URL.
return array OpenID provider info, following keys will be available: - url: string, OP Endpoint (i.e. OpenID provider address). - version: int, OpenID protocol version used by provider. - identity: string, identity value. - identifier_select: bool, whether to request OP to select identity for an user in OpenID 2, does not affect OpenID 1. - ax: bool, whether AX attributes should be used. - sreg: bool, whether SREG attributes should be used.
    public function discover($url)
    {
        if (empty($url)) {
            throw new Exception('No identity supplied.');
        }
        $result = ['url' => null, 'version' => null, 'identity' => $url, 'identifier_select' => false, 'ax' => false, 'sreg' => false];
        // Use xri.net proxy to resolve i-name identities
        if (!preg_match('#^https?:#', $url)) {
            $url = 'https://xri.net/' . $url;
        }
        /* We save the original url in case of Yadis discovery failure.
           It can happen when we'll be lead to an XRDS document
           which does not have any OpenID2 services.*/
        $originalUrl = $url;
        // A flag to disable yadis discovery in case of failure in headers.
        $yadis = true;
        // We'll jump a maximum of 5 times, to avoid endless redirections.
        for ($i = 0; $i < 5; $i++) {
            if ($yadis) {
                $headers = $this->sendRequest($url, 'HEAD');
                $next = false;
                if (isset($headers['x-xrds-location'])) {
                    $url = $this->buildUrl($url, trim($headers['x-xrds-location']));
                    $next = true;
                }
                if (isset($headers['content-type']) && (strpos($headers['content-type'], 'application/xrds+xml') !== false || strpos($headers['content-type'], 'text/xml') !== false)) {
                    /* Apparently, some providers return XRDS documents as text/html.
                       While it is against the spec, allowing this here shouldn't break
                       compatibility with anything.
                       ---
                       Found an XRDS document, now let's find the server, and optionally delegate.*/
                    $content = $this->sendRequest($url, 'GET');
                    preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m);
                    foreach ($m[1] as $content) {
                        $content = ' ' . $content;
                        // The space is added, so that strpos doesn't return 0.
                        // OpenID 2
                        $ns = preg_quote('http://specs.openid.net/auth/2.0/');
                        if (preg_match('#<Type>\\s*' . $ns . '(server|signon)\\s*</Type>#s', $content, $type)) {
                            if ($type[1] == 'server') {
                                $result['identifier_select'] = true;
                            }
                            preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
                            preg_match('#<(Local|Canonical)ID>(.*)</\\1ID>#', $content, $delegate);
                            if (empty($server)) {
                                throw new Exception('No servers found!');
                            }
                            // Does the server advertise support for either AX or SREG?
                            $result['ax'] = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>');
                            $result['sreg'] = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
                            $server = $server[1];
                            if (isset($delegate[2])) {
                                $result['identity'] = trim($delegate[2]);
                            }
                            $result['url'] = $server;
                            $result['version'] = 2;
                            return $result;
                        }
                        // OpenID 1.1
                        $ns = preg_quote('http://openid.net/signon/1.1');
                        if (preg_match('#<Type>\\s*' . $ns . '\\s*</Type>#s', $content)) {
                            preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
                            preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate);
                            if (empty($server)) {
                                throw new Exception('No servers found!');
                            }
                            // AX can be used only with OpenID 2.0, so checking only SREG
                            $result['sreg'] = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
                            $server = $server[1];
                            if (isset($delegate[1])) {
                                $result['identity'] = $delegate[1];
                            }
                            $result['url'] = $server;
                            $result['version'] = 1;
                            return $result;
                        }
                    }
                    $next = true;
                    $yadis = false;
                    $url = $originalUrl;
                    $content = null;
                    break;
                }
                if ($next) {
                    continue;
                }
                // There are no relevant information in headers, so we search the body.
                $content = $this->sendRequest($url, 'GET');
                $location = $this->extractHtmlTagValue($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
                if ($location) {
                    $url = $this->buildUrl($url, $location);
                    continue;
                }
            }
            if (!isset($content)) {
                $content = $this->sendRequest($url, 'GET');
            }
            // At this point, the YADIS Discovery has failed, so we'll switch to openid2 HTML discovery, then fallback to openid 1.1 discovery.
            $server = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid2.provider', 'href');
            if (!$server) {
                // The same with openid 1.1
                $server = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid.server', 'href');
                $delegate = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid.delegate', 'href');
                $version = 1;
            } else {
                $delegate = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid2.local_id', 'href');
                $version = 2;
            }
            if ($server) {
                // We found an OpenID2 OP Endpoint
                if ($delegate) {
                    // We have also found an OP-Local ID.
                    $result['identity'] = $delegate;
                }
                $result['url'] = $server;
                $result['version'] = $version;
                return $result;
            }
            throw new Exception('No servers found!');
        }
        throw new Exception('Endless redirection!');
    }

Usage Example

 public function testDiscover()
 {
     $url = 'https://www.google.com/accounts/o8/id';
     $client = new OpenId();
     $info = $client->discover($url);
     $this->assertNotEmpty($info);
     $this->assertNotEmpty($info['url']);
     $this->assertNotEmpty($info['identity']);
     $this->assertEquals(2, $info['version']);
     $this->assertArrayHasKey('identifier_select', $info);
     $this->assertArrayHasKey('ax', $info);
     $this->assertArrayHasKey('sreg', $info);
 }