I am integrating in my application a sign on using facebook and twitter. I intend to later on down the line include other sign on providors. I've actually build it from some open source code I found online but I think I've made a mess of things here.
My db sturcture is as below:
Users
ID|NAME|....|EMAIL|PASSWORD
USER SIGNONS
USER_ID | SIGNIN_TYPE| SIGNIN_ID
Wheneevr anyone creates an account using by signing in using facebook or twitter account - an entry is made in the user signons table indicating that the user has a signintype of 'facebook' or 'twitter'.
I've used the following code below for authentication:
public function loginAction() {
$this->ajaxInit();
// get an instace of Zend_Auth
$auth = Zend_Auth::getInstance();
$p = $this->_getAllParams();
if(isset($p['redirectto'])){
$this->setRedirect($p['redirectto']);
}else{
$redirect = explode('?', $_SERVER['HTTP_REFERER']);
$this->setRedirect($redirect[0]);
}
// check if a user is already logged
// this checks if he is logged into an open id providor
/*if ($auth->hasIdentity()) {
return $this->_redirect('/index/index');
}*/
// if the user is not logged, the do the logging
// $openid_identifier will be set when users 'clicks' on the account provider
$openid_identifier = $this->getRequest()->getParam('openid_identifier', null);
if($this->getRequest()->getParam('rememberme', null)>0){
//Zend_Session::rememberMe(60 * 60 * 24 * 30);
}
// $openid_mode will be set after first query to the openid provider
$openid_mode = $this->getRequest()->getParam('openid_mode', null);
// this one will be set by facebook connect
$code = $this->getRequest()->getParam('code', null);
// while this one will be set by twitter
$oauth_token = $this->getRequest()->getParam('oauth_token', null);
// do the first query to an authentication provider
if ($openid_identifier) {
if ('https://www.twitter.com' == $openid_identifier) {
$adapter = $this->_getTwitterAdapter($redirect);
_log('inside here');
} else if ('https://www.facebook.com' == $openid_identifier) {
$adapter = $this->_getFacebookAdapter($redirect);
} else {
// for openid
$adapter = $this->_getOpenIdAdapter($openid_identifier);
// specify what to grab from the provider and what extension to use
// for this purpose
$toFetch = _config('openid', 'tofetch');
// for google and yahoo use AtributeExchange Extension
if ('https://www.google.com/accounts/o8/id' == $openid_identifier || 'http://me.yahoo.com/' == $openid_identifier) {
$ext = $this->_getOpenIdExt('ax', $toFetch);
} else {
$ext = $this->_getOpenIdExt('sreg', $toFetch);
}
$adapter->setExtensions($ext);
}
// here a user is redirect to the provider for loging
$result = $auth->authenticate($adapter);
// the following two lines should never be executed unless the redirection faild.
//$this->_helper->FlashMessenger('Redirection faild');
if(strstr($redirect, 'import')){
return $this->_redirect($redirect.'?cmsg=redirection-failure');
}
return $this->_redirect('/accounts/sign-in?error=redirection-failure');
}else if ($openid_mode || $code || $oauth_token) {
// this will be exectued after provider redirected the user back to us
if ($code) {
// for facebook
$adapter = $this->_getFacebookAdapter();
} else if ($oauth_token) {
// for twitter
$adapter = $this->_getTwitterAdapter()->setQueryData($_GET);
} else {
// for openid
$adapter = $this->_getOpenIdAdapter(null);
// specify what to grab from the provider and what extension to use
// for this purpose
$ext = null;
$toFetch = _config('openid');
// for google and yahoo use AtributeExchange Extension
if (isset($_GET['openid_ns_ext1']) || isset($_GET['openid_ns_ax'])) {
$ext = $this->_getOpenIdExt('ax', $toFetch);
} else if (isset($_GET['openid_ns_sreg'])) {
$ext = $this->_getOpenIdExt('sreg', $toFetch);
}
if ($ext) {
$ext->parseResponse($_GET);
$adapter->setExtensions($ext);
}
}
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
$toStore = array('identity' => $auth->getIdentity());
$options = array();
if ($ext) {
// for openId
$toStore['properties'] = $ext->getProperties();
$options['signin_type'] = 'open_id';
$toStore['signin_type'] = 'open_id';
$options['signin_id'] = $auth->getIdentity();
} else if ($code) {
// for facebook
$msgs = $result->getMessages();
$toStore['properties'] = (array) $msgs['user'];
$options['signin_type'] = 'facebook';
$toStore['signin_type'] = 'facebook';
$options['signin_id'] = $auth->getIdentity();
} else if ($oauth_token) {
$identity = $result->getIdentity();
$twitterUserData = (array) $adapter->verifyCredentials();
$toStore = array('identity' => $identity['user_id']);
if (isset($twitterUserData['status'])) {
$twitterUserData['status'] = (array) $twitterUserData['status'];
}
_log($twitterUserData);
$toStore['properties'] = $twitterUserData;
$options['signin_type'] = 'twitter';
$toStore['signin_type'] = 'twitter';
$options['signin_id'] = $identity['user_id'];
}
$user = _factory('people')->get(false, $options);
if(count($user)>0){
$user = array_pop($user);
$auth->getStorage()->write($user['account_email']);
return $this->_redirect($this->setRedirect);
//return $this->_redirect('/accounts/index');
}else{
$auth->getStorage()->write($toStore);
return $this->_redirect('/accounts/welcome');
}
} else {
return $this->_redirect('/index/index');
}
}
}
The problem I've run into is I'm building a search for your friends feature. I got it to work with facebook that was easy. However for twitter I wanted to use the Zend frameworks Zend_Service_Twitter code. However I found out I needed the username inorder to login and use an access token as well - I found out I'm not exactly storing the username at this point.
CUrrently theres only one place in my entire program which does the authenticating and its the code posted above accessible via www.mysite.com/accounts/login
There's something wrong with my design but I can't uite tell what. The thing is that now I can't login to a twitter account to retrieve any user details. However I can sign in via twitter since I'm storing just enough to authenticate that a user has signed in using a twitter account and that a corresponding user exists with the authenticated account. Apart from that once the user has signed in I can't access the users twitter information.
Facebook makes it easy I guess because they have a dedicated api to handle this. This issue will defnitely cause me trouble later on if I choose to add more signons.
Any help would be most appreciated here. The code above of course doesn't have provision to associate acocunts to users already logged in.
How should I structure my login code and tables.
I'm using the zend framework here.
From what I understand your problem is that you do not store username and access_token after authenticating with Twitter. I think you should be able to get this info, and subsequently, store it as follows (the new bits are marked by comments /* new line */):
} else if ($oauth_token) {
$identity = $result->getIdentity();
$twitterUserData = (array) $adapter->verifyCredentials();
$toStore = array('identity' => $identity['user_id']);
if (isset($twitterUserData['status'])) {
$twitterUserData['status'] = (array) $twitterUserData['status'];
}
_log($twitterUserData);
$toStore['properties'] = $twitterUserData;
$options['signin_type'] = 'twitter';
$toStore['signin_type'] = 'twitter';
/* new line */
$accessToken = $adapter->getAccessToken();
// $accessToken should be an instance of Zend_Oauth_Token_Access
// and it should contain screen_name, auth_token, etc.
// to make sure it contains what it should do:
// var_dump($accessToken); exit;
/* new line */
$toStore['access_token'] = $accessToken;
$options['signin_id'] = $identity['user_id'];
}
So assuming that everything went fine here, your $accessToken should be stored together with your identity. So, to use it somewhere else, you could do as follows:
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$identity = $auth->getIdentity();
$accessToken = $identity['access_token'];
// Zend_Service_Twitter can accept instance of Zend_Oauth_Token_Access
$twitter = new Zend_Service_Twitter(array(
'accessToken' => $accessToken
));
try {
// do whatever twitter operation you want (or is permitted) ,e.g.
$response = $twitter->status->update('My Great Tweets');
var_dump($response);
} catch (Exception $e) {
// In case something went wrong, e.g. our access token expired or is wrong
var_dump($e->getMessage());
}
}
Hope this helps or at least give some clues what to do.