I already did a lot of research on this topic and have implemented a lot of solutions myself.
Including OpenID, Facebook Connect (using the old Rest API and the new
As original author of this answer, I want to note that I regard it as OUTDATED. Since most providers decided to exclusively implement Oauth instead of Openid. Newer Openid services will also likely use openid connect, which is based on oauth. There are good libraries like for example: https://github.com/hybridauth/hybridauth
After the discussion of the already existing answer i sum up:
Almost every major provider is an openid provider / endpoint including Google, Yahoo, Aol.
Some of them requrie the user to specify the username to construct the openid endpoint. Some of them (the ones mentioned above) do have discovery urls, where the user id is automatically returned so that the user only has to click. (i would be glad if someone could explain the technical background)
However the only pain in the ass is Facebook, because they have their Facebook connect where they use an adapted version of OAuth for authentication.
Now what I did for my project is to set up an openid provider that authenticates the user with the credentials of my facebook Application - so the user gets connected to my application - and returns a user id that looks like:
http://my-facebook-openid-proxy-subdomain.mydomain.com/?id=facebook-user-id
I also configured it to fetch email adress and name and return it as AX attributes.
So my website just has to implement opend id and i am fine :)
I build it upon the classes you can find here: http://gitorious.org/lightopenid
In my index.php file i just call it like this:
<?php
require 'LightOpenIDProvider.php';
require 'FacebookProvider.php';
$op = new FacebookProvider;
$op->appid = 148906418456860; // your facebook app id
$op->secret = 'mysecret'; // your facebook app secret
$op->baseurl = 'http://fbopenid.2xfun.com'; // needs to be allowed by facebook
$op->server();
?>
and the source code of FacebookProvider.php follows:
<?php
class FacebookProvider extends LightOpenIDProvider
{
public $appid = "";
public $appsecret = "";
public $baseurl = "";
// i have really no idea what this is for. just copied it from the example.
public $select_id = true;
function __construct() {
$this->baseurl = rtrim($this->baseurl,'/'); // no trailing slash as it will be concatenated with
// request uri wich has leading slash
parent::__construct();
# If we use select_id, we must disable it for identity pages,
# so that an RP can discover it and get proper data (i.e. without select_id)
if(isset($_GET['id'])) {
// i have really no idea what happens here. works with or without! just copied it from the example.
$this->select_id = false;
}
}
function setup($identity, $realm, $assoc_handle, $attributes)
{
// here we should check the requested attributes and adjust the scope param accordingly
// for now i just hardcoded email
$attributes = base64_encode(serialize($attributes));
$url = "https://graph.facebook.com/oauth/authorize?client_id=".$this->appid."&redirect_uri=";
$redirecturl = urlencode($this->baseurl.$_SERVER['REQUEST_URI'].'&attributes='.$attributes);
$url .= $redirecturl;
$url .= "&display=popup";
$url .= "&scope=email";
header("Location: $url");
exit();
}
function checkid($realm, &$attributes)
{
// try authenticating
$code = isset($_GET["code"]) ? $_GET["code"] : false;
if(!$code) {
// user has not authenticated yet, lets return false so setup redirects him to facebook
return false;
}
// we have the code parameter set so it looks like the user authenticated
$url = "https://graph.facebook.com/oauth/access_token?client_id=148906418456860&redirect_uri=";
$redirecturl = ($this->baseurl.$_SERVER['REQUEST_URI']);
$redirecturl = strstr($redirecturl, '&code', true);
$redirecturl = urlencode($redirecturl);
$url .= $redirecturl;
$url .= "&client_secret=".$this->secret;
$url .= "&code=".$code;
$data = $this->get_data($url);
parse_str($data,$data);
$token = $data['access_token'];
$data = $this->get_data('https://graph.facebook.com/me?access_token='.urlencode($token));
$data = json_decode($data);
$id = $data->id;
$email = $data->email;
$attribute_map = array(
'namePerson/friendly' => 'name', // we should parse the facebook link to get the nickname
'contact/email' => 'email',
);
if($id > 0) {
$requested_attributes = unserialize(base64_decode($_GET["attributes"]));
// lets be nice and return everything we can
$requested_attributes = array_merge($requested_attributes['required'],$requested_attributes['optional']);
$attributes = array();
foreach($requested_attributes as $requsted_attribute) {
if(!isset($data->{$attribute_map[$requsted_attribute]})) {
continue; // unknown attribute
}
$attributes[$requsted_attribute] = $data->{$attribute_map[$requsted_attribute]};
}
// yeah authenticated!
return $this->serverLocation . '?id=' . $id ;
}
die('login failed'); // die so we dont retry bouncing back to facebook
return false;
}
function get_data($url) {
$ch = curl_init();
$timeout = 5;
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
}
Its just a first working version (quick and dirty) Some dynamic stuff is hardcoded to my needs. It should show how and that it can be done. I am happy if someone picks up and improves it or re writes it or whatever :)
Well i consider this question answered
but I add a bounty just to get discussion. I would like to know what you think of my solution.
I will award the bounty to the best answer/comment beside this one.
OpenID is going to be your best bet for this application. It is supported by many, providers:
The Only problem is that twitter has not implemented OpenID yet. This is probably due to the fact that they are a proprietery based company, so they wanted their 'own' solution.
To solve that solution, you might write a wrapper class to provide compatibility with OpenID, but the chance is that even if your users don't have a twitter account, they might have a Facebook, Google, or Yahoo account.
Facebook Supports oauth, so you will have to port oauth to OpenID
Some PHP libraries for OpenID can be found here.
Now, some questions have been raised about facebook being an oauth provider.
Their oauth URL is "https://graph.facebook.com/oauth/authorize"
If you still do not belive me, then you can look at this javascript file, where I got that URL. If you don't believe that javascript file, then notice that it is hosted by stackexchange, the provider of this site. Now you must beleive that.
Fast forward two years and the answer of "OpenID is the answer" appears to be falling by the wayside by a number of the big providers. Most of the major third-party integration sites seem to have moved onto some flavor of OAuth (usually OAuth2). Also, if you don't mind NOT using OpenID/OAuth, there is a now complete SSO solution written in PHP (Disclaimer and full disclosure: This product is developed and maintained by myself under the CubicleSoft banner):
Single Sign-On Server/Client
Which didn't exist when this question was originally asked. It has a liberal license (MIT or LGPL) and meets your requirement of being an abstraction layer. The project tends to be focused toward enterprise sign ins but has some social media sign ins in the mix too (Google and Facebook).
You might also want to look at HybridAuth, which is only focused on social media sign ins but is more of a library than a prebuilt solution that you can throw onto a server and be done with it. So there is a bit more work involved with setting it up. It really depends on what you are after.
If you are happy with your OpenID solution, then great, but there are more options today than there were two years ago and people are still finding this thread.