1

I am trying to study class.openid.php because it is simpler and smaller than
lightopenid
. for my purposes 200 lines do matter. But class.openid.php does not work with google openID https://www.google.com/accounts/o8/id, prints to me such error:

ERROR CODE: OPENID_NOSERVERSFOUND
ERROR DESCRIPTION: Cannot find OpenID Server TAG on Identity page.

is it possible to make class.openid.php (any version) work with google openID and how to do such thing?

class.openid.php can be taken here but it did not worked for me out of the box so I had to find all <? and replace tham with <?php in case someone would like to see code I've got:

html interface page:

<?php
require('class.openid.v3.php');

if ($_POST['openid_action'] == "login"){ // Get identity from user and redirect browser to OpenID Server
    $openid = new SimpleOpenID;
    $openid->SetIdentity($_POST['openid_url']);
    $openid->SetTrustRoot('http://' . $_SERVER["HTTP_HOST"]);
    $openid->SetRequiredFields(array('email','fullname'));
    $openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone'));
    if ($openid->GetOpenIDServer()){
        $openid->SetApprovedURL('http://' . $_SERVER["HTTP_HOST"] . $_SERVER["PATH_INFO"]);     // Send Response from OpenID server to this script
        $openid->Redirect();    // This will redirect user to OpenID Server
    }else{
        $error = $openid->GetError();
        echo "ERROR CODE: " . $error['code'] . "<br>";
        echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
    }
    exit;
}
else if($_GET['openid_mode'] == 'id_res'){  // Perform HTTP Request to OpenID server to validate key
    $openid = new SimpleOpenID;
    $openid->SetIdentity($_GET['openid_identity']);
    $openid_validation_result = $openid->ValidateWithServer();
    if ($openid_validation_result == true){         // OK HERE KEY IS VALID
        echo "VALID";
    }else if($openid->IsError() == true){           // ON THE WAY, WE GOT SOME ERROR
        $error = $openid->GetError();
        echo "ERROR CODE: " . $error['code'] . "<br>";
        echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
    }else{                                          // Signature Verification Failed
        echo "INVALID AUTHORIZATION";
    }
}else if ($_GET['openid_mode'] == 'cancel'){ // User Canceled your Request
    echo "USER CANCELED REQUEST";
}
?>
<html>
<head>
    <title>OpenID Example</title>
</head>
<body>
<div>
<fieldset id="openid">
<legend>OpenID Login</legend>
<form action="<?php echo 'http://' . $_SERVER["HTTP_HOST"] . $_SERVER["PATH_INFO"]; ?>" method="post" onsubmit="this.login.disabled=true;">
<input type="hidden" name="openid_action" value="login">
<div><input type="text" name="openid_url" class="openid_login"><input type="submit" name="login" value="login &gt;&gt;"></div>
<div><a href="http://www.myopenid.com/" class="link" >Get an OpenID</a></div>
</form>
</fieldset>
</div>
<div style="margin-top: 2em; font-family: arial; font-size: 0.8em; border-top:1px solid gray; padding: 4px;">Sponsored by: <a href="http://www.fivestores.com">FiveStores</a> - get your free online store; includes extensive API for developers; <i style="color: gray;">integrated with  <a href="http://en.wikipedia.org/wiki/OpenID">OpenID</a></i></div>
</body>
</html>

and php class

<?php
/*
    FREE TO USE Under License: GPLv3
    Simple OpenID PHP Class 
    Some modifications by Eddie Roosenmaallen, eddie@roosenmaallen.com
*/

class SimpleOpenID{
    var $openid_url_identity;
    var $URLs = array();
    var $error = array();
    var $fields = array(
        'required'   => array(),
        'optional'   => array(),
    );

    function SimpleOpenID(){
        if (!function_exists('curl_exec')) {
            die('Error: Class SimpleOpenID requires curl extension to work');
        }
    }

    function SetOpenIDServer($a){
        $this->URLs['openid_server'] = $a;
    }

    function SetTrustRoot($a){
        $this->URLs['trust_root'] = $a;
    }

    function SetCancelURL($a){
        $this->URLs['cancel'] = $a;
    }

    function SetApprovedURL($a){
        $this->URLs['approved'] = $a;
    }

    function SetRequiredFields($a){
        if (is_array($a)){
            $this->fields['required'] = $a;
        }else{
            $this->fields['required'][] = $a;
        }
    }

    function SetOptionalFields($a){
        if (is_array($a)){
            $this->fields['optional'] = $a;
        }else{
            $this->fields['optional'][] = $a;
        }
    }

    function SetIdentity($a){   // Set Identity URL
            if ((stripos($a, 'http://') === false)
               && (stripos($a, 'https://') === false)){
                $a = 'http://'.$a;
            }
            $this->openid_url_identity = $a;
    }

    function GetIdentity(){     // Get Identity
        return $this->openid_url_identity;
    }

    function GetError(){
        $e = $this->error;
        return array('code'=>$e[0],'description'=>$e[1]);
    }

    function ErrorStore($code, $desc = null){
        $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.';
        if ($desc == null){
            $desc = $errs[$code];
        }
        $this->error = array($code,$desc);
    }

    function IsError(){
        if (count($this->error) > 0){
            return true;
        }else{
            return false;
        }
    }

    function splitResponse($response) {
        $r = array();
        $response = explode("\n", $response);
        foreach($response as $line) {
            $line = trim($line);
            if ($line != "") {
                list($key, $value) = explode(":", $line, 2);
                $r[trim($key)] = trim($value);
            }
        }
        return $r;
    }

    function OpenID_Standarize($openid_identity = null){
        if ($openid_identity === null)
            $openid_identity = $this->openid_url_identity;

        $u = parse_url(strtolower(trim($openid_identity)));

        if (!isset($u['path']) || ($u['path'] == '/')) {
            $u['path'] = '';
        }
        if(substr($u['path'],-1,1) == '/'){
            $u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
        }
        if (isset($u['query'])){ // If there is a query string, then use identity as is
            return $u['host'] . $u['path'] . '?' . $u['query'];
        }else{
            return $u['host'] . $u['path'];
        }
    }

    function array2url($arr){ // converts associated array to URL Query String
        if (!is_array($arr)){
            return false;
        }
        $query = '';
        foreach($arr as $key => $value){
            $query .= $key . "=" . $value . "&";
        }
        return $query;
    }

    function CURL_Request($url, $method="GET", $params = "") { // Remember, SSL MUST BE SUPPORTED
            if (is_array($params)) $params = $this->array2url($params);
            $curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : ""));
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($curl, CURLOPT_HEADER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET"));
            curl_setopt($curl, CURLOPT_POST, ($method == "POST"));
            if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($curl);

            if (curl_errno($curl) == 0){
                $response;
            }else{
                $this->ErrorStore('OPENID_CURL', curl_error($curl));
            }
            return $response;
    }

     function HTML2OpenIDServer($content) {
        $get = array();

        // Get details of their OpenID server and (optional) delegate
        preg_match_all('/<link[^>]*rel=[\'"]openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
        preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"]openid.server[\'"][^>]*\/?>/i', $content, $matches2);
        $servers = array_merge($matches1[1], $matches2[1]);

        preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);

        preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);

        $delegates = array_merge($matches1[1], $matches2[1]);

        $ret = array($servers, $delegates);
        return $ret;
    }

    function GetOpenIDServer(){
        $response = $this->CURL_Request($this->openid_url_identity);
        list($servers, $delegates) = $this->HTML2OpenIDServer($response);
        if (count($servers) == 0){
            $this->ErrorStore('OPENID_NOSERVERSFOUND');
            return false;
        }
        if (isset($delegates[0])
          && ($delegates[0] != "")){
            $this->SetIdentity($delegates[0]);
        }
        $this->SetOpenIDServer($servers[0]);
        return $servers[0];
    }

    function GetRedirectURL(){
        $params = array();
        $params['openid.return_to'] = urlencode($this->URLs['approved']);
        $params['openid.mode'] = 'checkid_setup';
        $params['openid.identity'] = urlencode($this->openid_url_identity);
        $params['openid.trust_root'] = urlencode($this->URLs['trust_root']);

        if (isset($this->fields['required'])
          && (count($this->fields['required']) > 0)) {
            $params['openid.sreg.required'] = implode(',',$this->fields['required']);
        }
        if (isset($this->fields['optional'])
          && (count($this->fields['optional']) > 0)) {
            $params['openid.sreg.optional'] = implode(',',$this->fields['optional']);
        }
        return $this->URLs['openid_server'] . "?". $this->array2url($params);
    }

    function Redirect(){
        $redirect_to = $this->GetRedirectURL();
        if (headers_sent()){ // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
            echo '<script language="JavaScript" type="text/javascript">window.location=\'';
            echo $redirect_to;
            echo '\';</script>';
        }else{  // Default Header Redirect
            header('Location: ' . $redirect_to);
        }
    }

    function ValidateWithServer(){
        $params = array(
            'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']),
            'openid.signed' => urlencode($_GET['openid_signed']),
            'openid.sig' => urlencode($_GET['openid_sig'])
        );
        // Send only required parameters to confirm validity
        $arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed']));
        for ($i=0; $i<count($arr_signed); $i++){
            $s = str_replace('sreg_','sreg.', $arr_signed[$i]);
            $c = $_GET['openid_' . $arr_signed[$i]];
            // if ($c != ""){
                $params['openid.' . $s] = urlencode($c);
            // }
        }
        $params['openid.mode'] = "check_authentication";

        $openid_server = $this->GetOpenIDServer();
        if ($openid_server == false){
            return false;
        }
        $response = $this->CURL_Request($openid_server,'POST',$params);
        $data = $this->splitResponse($response);

        if ($data['is_valid'] == "true") {
            return true;
        }else{
            return false;
        }
    }
}
?>
Rella
  • 65,003
  • 109
  • 363
  • 636
  • The question can be generally answered with **Yes** because you can extend the class and add the missing functionality. But I think that is not what you ask for, I think you would prefer the download link of the new version of the class that has everything you're looking for, right? – hakre Jul 17 '11 at 15:38
  • 1
    I really don't think that these 200 lines of code do matter for you. Nevertheless, if you want to support OpenID 2.0 and attribute exchange, you'll have to use another library. And just to prove that line count doesn't matter, I've removed some code (half of which were comments, that the class in question noticeably lacks) in lightopenid to make it smaller than the class you use: http://pastebin.com/fE8qT3kW. It still supports both versions of OpenID, although it lacks support for retrieving attributes. So in summary, LightOpenID is neither more complicated, nor bloated. – Mewp Jul 18 '11 at 13:26

3 Answers3

4

The problem is that Google doesn't just supply an OpenID endpoint.

OpenId endpoints include an identifier for the user.

What we are having here is called a Discovery Url.

This is a static url that you can direct any user to, and the service itself will recognise the user and return a per-user unique identifying url.

This however is NOT implemented correctly by most openid client libraries, including the majority linked on the official openid website.

Even the Zend Framework libraries are incapable of handling that.

However I found a class that I analysed from various perspectives and that I am very satisfied with. At the company I work at we already integrated it successfully in several production environments and have not experienced any problems.

You may also be interested in another post of mine dealing with the issue of making Facebook an openid Provider. The class I am using, that also supports Google, can also be found there:

Best way to implement Single-Sign-On with all major providers?

Community
  • 1
  • 1
The Surrican
  • 29,118
  • 24
  • 122
  • 168
  • BTW: lightopenid seams to do it correctly with google openID Discovery Url...) – Rella Jul 17 '11 at 15:22
  • i must be honest i didnt really look closely at your question because it seemed to be clear to me from the title. now i see you were alraedy working with lightopenid. i invested a fair amount of research into this and can only recommend to stick with it. – The Surrican Jul 17 '11 at 15:32
1

Are you searching something like : http://wiki.openid.net/w/page/12995176/Libraries ?

There is a PHP section in that.

ChristopheCVB
  • 7,269
  • 1
  • 29
  • 54
1

The class in your question does not support OpenID 2.0 at all. Therefore, it will not work with Google without adding a lot of code.

Mewp
  • 4,715
  • 1
  • 21
  • 24