Initial import
This commit is contained in:
478
htdocs/lib/GoogleOpenID.php
Normal file
478
htdocs/lib/GoogleOpenID.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* The GoogleOpenID class
|
||||
*
|
||||
* This class implements a gateway to the Google OpenID federated login API
|
||||
*
|
||||
* By Andrew Peace (http://www.andrewpeace.com)
|
||||
* December 6, 2008
|
||||
* Contact me at http://www.andrewpeace.com/contact.html
|
||||
*
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* To redirect users to a Google login page, simply create a GoogleOpenID:
|
||||
*
|
||||
* $googleGateway = GoogleOpenID::createRequest("http://www.mydomain.com/check.php",
|
||||
* "ABCDEFG",
|
||||
* true);
|
||||
*
|
||||
* The first argument is the URL you want Google to redirect to when it sends
|
||||
* its response
|
||||
*
|
||||
* The second argument is the association handle you've obtained from Google.
|
||||
* This parameter is OPTIONAL and can be set to null. However, if it is not
|
||||
* given or it is set to null, GoogleOpenID will automatically request an
|
||||
* association handle when it is constructed. This is wastefull! Association
|
||||
* handles last for two weeks. So, it is better to get and save an association
|
||||
* handle on your own using GoogleOpenID::getAssociationHandle().
|
||||
*
|
||||
* The third argument should be set to true if you want the response to include
|
||||
* the user's email address. It is optional.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $googleGateway = GoogleOpenID::createRequest("http://www.mydomain.com/checkauth.php", "ABCDEFG", true);
|
||||
* $googleGateway->redirect();
|
||||
*
|
||||
* OR
|
||||
*
|
||||
* $handle = GoogleOpenID::getAssociationHandle(); // <--save this! valid for two weeks!
|
||||
*
|
||||
* $googleGateway = GoogleOpenID::createRequest("http://www.mydomain.com/checkauth.php", $handle, true);
|
||||
* $googleGateway->redirect();
|
||||
*
|
||||
*
|
||||
* When you want to recieve a Google OpenID response, simply pass the given
|
||||
* parameters to GoogleOpenID::create(). In most cases, you should just be able
|
||||
* to pass the $_GET variable.
|
||||
*
|
||||
* To continue the previous example, the following code would go in checkauth.php
|
||||
*
|
||||
* $googleResponse = GoogleOpenID::create($_GET);
|
||||
* $sucess = $googleResponse->success();//true or false
|
||||
* $user_identity = $googleResponse->identity();//the user's ID
|
||||
* $user_email = $googleResponse->email();//the user's email
|
||||
*
|
||||
* OR, even easier
|
||||
*
|
||||
* $googleResponse = GoogleOpenID::getResponse(); // <-- automatically reads $_GET
|
||||
*
|
||||
* Advanced users can create a slightly more customized request using the create
|
||||
* method. It accepts an associative array of openid parameters and sets those
|
||||
* that it deems appropriate to set (mostly, the parameters that don't have a
|
||||
* definite value when interacting with Google)
|
||||
*
|
||||
*
|
||||
* Full class signature:
|
||||
*
|
||||
* public static createRequest(String, [String], [Boolean])
|
||||
* public static getResponse()
|
||||
* public static create(Array)
|
||||
* public static getAssociationHandle([String])
|
||||
* public static getEndPoint()
|
||||
* public redirect()
|
||||
* public getArray()
|
||||
* public endPoint()
|
||||
* public success()
|
||||
* public assoc_handle()
|
||||
* public email()
|
||||
* public identity()
|
||||
*
|
||||
* Features to implement:
|
||||
*
|
||||
* -In constructor, fix relative->absolute URL conversion (it is messy/buggy)
|
||||
* -In getAssociationHandle(), use encryption
|
||||
* -Verify Google's response with signed, sig etc
|
||||
****************************************************************************/
|
||||
|
||||
class GoogleOpenID{
|
||||
//the google discover url
|
||||
const google_discover_url = "https://www.google.com/accounts/o8/id";
|
||||
|
||||
//some constant parameters
|
||||
const openid_ns = "http://specs.openid.net/auth/2.0";
|
||||
//required for email attribute exchange
|
||||
const openid_ns_ext1 = "http://openid.net/srv/ax/1.0";
|
||||
const openid_ext1_mode = "fetch_request";
|
||||
const openid_ext1_type_email = "http://schema.openid.net/contact/email";
|
||||
const openid_ext1_required = "email";
|
||||
|
||||
//parameters set by constructor
|
||||
private $mode;//the mode (checkid_setup, id_res, or cancel)
|
||||
private $response_nonce;
|
||||
private $return_to;//return URL
|
||||
private $realm;//the realm that the user is being asked to trust
|
||||
private $assoc_handle;//the association handle between this service and Google
|
||||
private $claimed_id;//the id claimed by the user
|
||||
private $identity;//for google, this is the same as claimed_id
|
||||
private $signed;
|
||||
private $sig;
|
||||
private $email;//the user's email address
|
||||
|
||||
//if true, fetch email address
|
||||
private $require_email;
|
||||
|
||||
//private constructor
|
||||
private function GoogleOpenID($mode, $op_endpoint, $response_nonce, $return_to, $realm, $assoc_handle, $claimed_id, $signed, $sig, $email, $require_email){
|
||||
|
||||
//if assoc_handle is null, fetch one
|
||||
if(is_null($assoc_handle))
|
||||
$assoc_handle = GoogleOpenID::getAssociationHandle();
|
||||
|
||||
//if return_to is a relative URL, make it absolute
|
||||
if(stripos($return_to, "http://")!==0 &&
|
||||
stripos($return_to, "https://")!==0){
|
||||
//if the first character is a slash, delete it
|
||||
if(substr($return_to, 0, 1)=="/")
|
||||
$return_to = substr($return_to, 1);
|
||||
//get the position of server_name
|
||||
$server_name_pos = stripos($return_to, $_SERVER['SERVER_NAME']);
|
||||
//if server_name is already at position zero
|
||||
if($server_name_pos != false && $server_name_pos==0){
|
||||
$return_to = "http://".$return_to;
|
||||
} else {
|
||||
$return_to = "http://".$_SERVER['SERVER_NAME']."/".$return_to;
|
||||
}//else (server name not at position zero)
|
||||
}//if return_to is relative
|
||||
|
||||
//if realm is null, attempt to set it via return_to
|
||||
if(is_null($realm)){
|
||||
//if return_to is set
|
||||
if(!is_null($return_to)){
|
||||
$pieces = parse_url($return_to);
|
||||
$realm = $pieces['scheme']."://".$pieces['host'];
|
||||
}//if return_to set
|
||||
}//if realm null
|
||||
|
||||
$this->mode = $mode;
|
||||
$this->op_endpoint = $op_endpoint;
|
||||
$this->response_nonce = $response_nonce;
|
||||
$this->return_to = $return_to;
|
||||
$this->realm = $realm;
|
||||
$this->assoc_handle = $assoc_handle;
|
||||
$this->claimed_id = $claimed_id;
|
||||
$this->identity = $claimed_id;
|
||||
$this->signed = $signed;
|
||||
$this->sig = $sig;
|
||||
$this->email = $email;
|
||||
$this->require_email = ($require_email) ? true : false;
|
||||
}//GoogleOpenID
|
||||
|
||||
//static creator that accepts only a return_to URL
|
||||
//this creator should be used when creating a GoogleOpenID for a redirect
|
||||
public static function createRequest($return_to, $assoc_handle=null, $require_email=false){
|
||||
return new GoogleOpenID("checkid_setup", null, null, $return_to, null, $assoc_handle, "http://specs.openid.net/auth/2.0/identifier_select", null, null, null, $require_email);
|
||||
}//createRequest
|
||||
|
||||
//static creator that accepts an associative array of parameters and
|
||||
//sets only the setable attributes (does not overwrite constants)
|
||||
public static function create($params){
|
||||
//loop through each parameter
|
||||
foreach($params as $param => $value){
|
||||
switch($param){
|
||||
case "openid_mode":
|
||||
//check validity of mode
|
||||
if($value=="checkid_setup" ||
|
||||
$value=="id_res" ||
|
||||
$value=="cancel")
|
||||
$mode = $value;
|
||||
else
|
||||
$mode = "cancel";
|
||||
continue 2;
|
||||
|
||||
case "openid_op_endpoint":
|
||||
$op_endpoint = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_response_nonce":
|
||||
$response_nonce = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_return_to":
|
||||
$return_to = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_realm":
|
||||
$realm = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_assoc_handle":
|
||||
$assoc_handle = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_claimed_id":
|
||||
$claimed_id = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_identity":
|
||||
$claimed_id = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_signed":
|
||||
$signed = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_sig":
|
||||
$sig = $value;
|
||||
continue 2;
|
||||
|
||||
case "openid_ext1_value_email":
|
||||
$email = $value;
|
||||
continue 2;
|
||||
|
||||
case "require_email":
|
||||
$require_email = $value;
|
||||
continue 2;
|
||||
|
||||
default:
|
||||
continue 2;
|
||||
}//switch param
|
||||
}//loop through params
|
||||
|
||||
//if require email is not set, set it to false
|
||||
if(!is_bool($require_email))
|
||||
$require_email = false;
|
||||
//if mode is not set, set to default for redirection
|
||||
if(is_null($mode))
|
||||
$mode = "checkid_setup";
|
||||
//if return_to is not set and mode is checkid_setup, throw an error
|
||||
if(is_null($return_to) && $mode=="checkid_setup")
|
||||
throw new Exception("GoogleOpenID.create() needs parameter openid.return_to");
|
||||
|
||||
//return a new GoogleOpenID with the given parameters
|
||||
return new GoogleOpenID($mode, $op_endpoint, $response_nonce, $return_to, $realm, $assoc_handle, $claimed_id, $signed, $sig, $email, $require_email);
|
||||
}//create
|
||||
|
||||
//creates and returns a GoogleOpenID from the $_GET variable
|
||||
public static function getResponse(){
|
||||
return GoogleOpenID::create($_GET);
|
||||
}//getResponse
|
||||
|
||||
//fetches an association handle from google. association handles are valid
|
||||
//for two weeks, so coders should do their best to save association handles
|
||||
//externally and pass them to createRequest()
|
||||
//NOTE: This function does not use encryption, but it SHOULD! At the time
|
||||
//I wrote this I wanted it done fast, and I couldn't seem to find a good
|
||||
//two-way SHA-1 or SHA-256 library for PHP. Encryption is not my thing, so
|
||||
//it remains unimplemented.
|
||||
public static function getAssociationHandle($endpoint=null){
|
||||
//if no endpoint given
|
||||
if(is_null($endpoint))
|
||||
//fetch one from Google
|
||||
$request_url = GoogleOpenID::getEndPoint();
|
||||
//if endpoint given, set it
|
||||
else
|
||||
$request_url = $endpoint;
|
||||
|
||||
//append parameters (these never change)
|
||||
$request_url .= "?openid.ns=".urlencode(GoogleOpenID::openid_ns);
|
||||
$request_url .= "&openid.mode=associate";
|
||||
$request_url .= "&openid.assoc_type=HMAC-SHA1";
|
||||
$request_url .= "&openid.session_type=no-encryption";
|
||||
|
||||
//create a CURL session with the request URL
|
||||
$c = curl_init($request_url);
|
||||
|
||||
//set a few options
|
||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($c, CURLOPT_HEADER, false);
|
||||
|
||||
//get the contents of request URL
|
||||
$request_contents = curl_exec($c);
|
||||
|
||||
//close the CURL session
|
||||
curl_close($c);
|
||||
|
||||
//a handle to be returned
|
||||
$assoc_handle = null;
|
||||
|
||||
//split the response into lines
|
||||
$lines = explode("\n", $request_contents);
|
||||
|
||||
//loop through each line
|
||||
foreach($lines as $line){
|
||||
//if this line is assoc_handle
|
||||
if(substr($line, 0, 13)=="assoc_handle:"){
|
||||
//save the assoc handle
|
||||
$assoc_handle = substr($line, 13);
|
||||
//exit the loop
|
||||
break;
|
||||
}//if this line is assoc_handle
|
||||
}//loop through lines
|
||||
|
||||
//return the handle
|
||||
return $assoc_handle;
|
||||
}//getAssociationHandle
|
||||
|
||||
//fetches an endpoint from Google
|
||||
public static function getEndPoint(){
|
||||
//fetch the request URL
|
||||
$request_url = GoogleOpenID::google_discover_url;
|
||||
|
||||
//create a CURL session with the request URL
|
||||
$c = curl_init($request_url);
|
||||
|
||||
//set a few options
|
||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($c, CURLOPT_HEADER, false);
|
||||
|
||||
//fetch the contents of the request URL
|
||||
$request_contents = curl_exec($c);
|
||||
|
||||
//close the CURL session
|
||||
curl_close($c);
|
||||
|
||||
//create a DOM document so we can extract the URI element
|
||||
$domdoc = new DOMDocument();
|
||||
$domdoc->loadXML($request_contents);
|
||||
|
||||
//fetch the contents of the URI element
|
||||
$uri = $domdoc->getElementsByTagName("URI");
|
||||
$uri = $uri->item(0)->nodeValue;
|
||||
|
||||
//return the given URI
|
||||
return $uri;
|
||||
}//getEndPoint
|
||||
|
||||
//returns an associative array of all openid parameters for this openid
|
||||
//session. the array contains all the GET attributes that would be sent
|
||||
//or that have been recieved, meaning:
|
||||
//
|
||||
//if mode = "cancel" returns only the mode and ns attributes
|
||||
//if mode = "id_res" returns all attributes that are not null
|
||||
//if mode = "checkid_setup" returns only attributes that need to be sent
|
||||
// in the HTTP request
|
||||
public function getArray(){
|
||||
//an associative array to return
|
||||
$ret = array();
|
||||
|
||||
$ret['openid.ns'] = GoogleOpenID::openid_ns;
|
||||
|
||||
//if mode is cancel, return only ns and mode
|
||||
if($this->mode=="cancel"){
|
||||
$ret['openid.mode'] = "cancel";
|
||||
return $ret;
|
||||
}//if cancel
|
||||
|
||||
//set attributes that are returned for all cases
|
||||
if(!is_null($this->claimed_id)){
|
||||
$ret['openid.claimed_id'] = $this->claimed_id;
|
||||
$ret['openid.identity'] = $this->claimed_id;
|
||||
}
|
||||
if(!is_null($this->return_to))
|
||||
$ret['openid.return_to'] = $this->return_to;
|
||||
if(!is_null($this->realm))
|
||||
$ret['openid.realm'] = $this->realm;
|
||||
if(!is_null($this->assoc_handle))
|
||||
$ret['openid.assoc_handle'] = $this->assoc_handle;
|
||||
if(!is_null($this->mode))
|
||||
$ret['openid.mode'] = $this->mode;
|
||||
|
||||
//set attributes that are returned only if this is a request
|
||||
//and if getting email is required OR if this is a response and the
|
||||
//email is given
|
||||
if(($this->mode=="checkid_setup" AND $this->require_email) OR
|
||||
($this->mode=="id_res" AND !is_null($this->email))){
|
||||
$ret['openid.ns.ext1'] = GoogleOpenID::openid_ns_ext1;
|
||||
$ret['openid.ext1.mode'] = GoogleOpenID::openid_ext1_mode;
|
||||
$ret['openid.ext1.type.email'] = GoogleOpenID::openid_ext1_type_email;
|
||||
$ret['openid.ext1.required'] = GoogleOpenID::openid_ext1_required;
|
||||
if(!is_null($this->email))
|
||||
$ret['openid.ext1.value.email'] = $this->email;
|
||||
}//if redirect and get email
|
||||
|
||||
//set attributes that are returned only if this is a response
|
||||
if($this->mode=="id_res"){
|
||||
$ret['openid.op_endpoint'] = $this->op_endpoint;
|
||||
if(!is_null($this->response_nonce))
|
||||
$ret['openid.response_nonce'] = $this->response_nonce;
|
||||
if(!is_null($this->signed))
|
||||
$ret['openid.signed'] = $this->signed;
|
||||
if(!is_null($this->sig))
|
||||
$ret['openid.sig'] = $this->sig;
|
||||
}
|
||||
|
||||
//return the array
|
||||
return $ret;
|
||||
}//getArray
|
||||
|
||||
//sends a request to google and fetches the url to which google is asking
|
||||
//us to redirect (unless the endpoint is already known, in which case the
|
||||
//function simply returns it)
|
||||
public function endPoint(){
|
||||
//if we know the value of op_endpoint already
|
||||
if(!is_null($this->op_endpoint))
|
||||
return $this->op_endpoint;
|
||||
|
||||
//fetch the endpoint from Google
|
||||
$endpoint = GoogleOpenID::getEndPoint();
|
||||
|
||||
//save it
|
||||
$this->op_endpoint = $endpoint;
|
||||
|
||||
//return the endpoint
|
||||
return $endpoint;
|
||||
}//getedPoint
|
||||
|
||||
//returns the URL to which we should send a request (including all GET params)
|
||||
public function getRequestURL(){
|
||||
//get all parameters
|
||||
$params = $this->getArray();
|
||||
|
||||
//the base URL
|
||||
$url = $this->endPoint();
|
||||
|
||||
//flag indicating whether to set a '?' or an '&'
|
||||
$first_attribute = true;
|
||||
|
||||
//loop through all params
|
||||
foreach($params as $param => $value){
|
||||
//if first attribute print a ?, else print a &
|
||||
if($first_attribute){
|
||||
$url .= "?";
|
||||
$first_attribute = false;
|
||||
} else {
|
||||
$url .= "&";
|
||||
}//else (not first attribute)
|
||||
|
||||
$url .= urlencode($param) . "=" . urlencode($value);
|
||||
}//loop through params
|
||||
|
||||
//return the URL
|
||||
return $url;
|
||||
}//getRequestURL
|
||||
|
||||
//redirects the browser to the appropriate request URL
|
||||
public function redirect(){
|
||||
header("Location: ".$this->getRequestURL());
|
||||
}//redirect
|
||||
|
||||
//returns true if the response was a success
|
||||
public function success(){
|
||||
return ($this->mode=="id_res");
|
||||
}//success
|
||||
|
||||
//returns the identity given in the response
|
||||
public function identity(){
|
||||
if($this->mode!="id_res")
|
||||
return null;
|
||||
else
|
||||
return $this->claimed_id;
|
||||
}//identity
|
||||
|
||||
//returns the email given in the response
|
||||
public function email(){
|
||||
if($this->mode!="id_res")
|
||||
return null;
|
||||
else
|
||||
return $this->email;
|
||||
}//email
|
||||
|
||||
//returns the assoc_handle
|
||||
public function assoc_handle(){
|
||||
return $this->assoc_handle();
|
||||
}//assoc_handle
|
||||
}//class GoogleOpenID
|
||||
?>
|
||||
Reference in New Issue
Block a user