schema.ymlにUserモデルを以下のように作っておきます。
User:
actAs: { Timestampable: ~ }
tableName: users
options:
type: MyISAM
collate: utf8_unicode_ci
charset: utf8
comment: 'ユーザー'
columns:
id:
type: integer(4)
unsigned: false
primary: true
autoincrement: true
comment: 'ID'
name:
type: string(255)
fixed: false
notnull: true
default: 'no name'
comment: 'ユーザー名'
image:
type: blob
notnull: false
comment: 'アイコン画像'
hybridauth_provider_name:
type: string(20)
fixed: false
notnull: true
comment: 'HybridAuthプロバイダ'
hybridauth_provider_uid:
type: string(255)
fixed: false
notnull: true
comment: 'HybridAuthプロバイダUID'
HybridAuthをダウンロードします。
Download HybridAuth
ちなみにSymfony2用にはHybridAuthのバンドルがあります。1.4用にも欲しい…
$ cd lib/vendor
$ wget http://jaist.dl.sourceforge.net/project/hybridauth/hybridauth-2.0.11.zip
$ unzip ./hybridauth-2.0.11.zip
$ rm ./README.html
$ rm -R ./examples/
$ rm ./hybridauth-2.0.11.zip
$ cd ../..
$ mkdir lib/config
lib/configにsfEnvironmentYamlConfigHandler.class.phpを作成します。sfSimpleYamlConfigHandlerを継承して、環境設定が反映されるように変更します。
<?php
class sfEnvironmentYamlConfigHandler extends sfSimpleYamlConfigHandler
{
public function execute($configFiles)
{
$config = self::getConfiguration($configFiles);
// compile data
$retval = "<?php\n".
"// auto-generated by %s\n".
"// date: %s\nreturn %s;\n";
$retval = sprintf($retval, __CLASS__, date('Y/m/d H:i:s'), var_export($config, true));
return $retval;
}
static public function getConfiguration(array $configFiles)
{
return self::replaceConstants(self::flattenConfigurationWithEnvironment(self::parseYamls($configFiles)));
}
}
configにconfig_handlers.ymlを作成します。設定ファイルの処理の仕方をここで指定しています。
config/hybrid_auth.yml:
class: sfEnvironmentYamlConfigHandler
で、同じくconfigにhybrid_auth.ymlファイルを作成します。
all:
base_url: http://localhost/login/endpoint
providers:
OpenID:
enabled: true
Yahoo:
enabled: true
AOL:
enabled: true
Google:
enabled: true
keys:
id: ""
secret: ""
scope: ""
Facebook:
enabled: true
keys:
id: ""
secret: ""
# A comma-separated list of permissions you want to request from the user.
# See the Facebook docs for a full list of available permissions:
# http://developers.facebook.com/docs/reference/api/permissions.
scope: ""
# The display context to show the authentication page.
# Options are: page, popup, iframe, touch and wap.
# Read the Facebook docs for more details:
# http://developers.facebook.com/docs/reference/dialogs#display.
# Default: page
display: ""
Twitter:
enabled: true
keys:
key: ""
secret: ""
# windows live
Live:
enabled: true
keys:
id: ""
secret: ""
MySpace:
enabled: true
keys:
key: ""
secret: ""
LinkedIn:
enabled: true
keys:
key: ""
secret: ""
Foursquare:
enabled: true
keys:
id: ""
secret: ""
# if you want to enable logging, set 'debug_mode' to true
# then provide a writable file by the web server on "debug_file"
debug_mode: false
debug_file: ""
TwitterとFacebookのKeyとSecretにそれぞれOAuthのConsumer KeyとConsumer Secretを入れておきます。TwitterはCallback URLをダミーでも構わないので入れておかないといけないので注意。
Twitter https://dev.twitter.com/apps
Facebook https://developers.facebook.com/apps/
設定内容はHybridAuthのconfig.phpと内容的には同じものです。YAMLに書き換えたのと、devやprodなど環境ごとに設定を切り替えられるようになっています。デフォルトはall以下に書けばOKです。
lib/vendor以下はオートロードされないので、configにautoload.ymlファイルを作成します。
autoload:
hybridauth:
name: hybridauth
path: %SF_LIB_DIR%/vendor/hybridauth
recursive: true
次にアクション側を実装します。
authモジュールを作ります。
$ ./symfony generate:module frontend auth
routing.ymlにログインとエンドポイントを追加します。
login_endpoint:
url: /login/endpoint
param: { module: auth, action: endPoint }
login:
url: /login/:provider
param: { module: auth, action: index }
requirements:
provider: Facebook|Google|Twitter
apps/frontend/lib/myUser.class.phpを編集してIDとユーザー名を保持できるようにします。
<?php
class myUser extends sfBasicSecurityUser
{
const ATTRIBUTE_NAMESPACE = 'localhost/user/myUser/attributes';
public function setName($name)
{
$this->setAttribute('name', $name, self::ATTRIBUTE_NAMESPACE);
}
public function getName()
{
return $this->getAttribute('name', null, self::ATTRIBUTE_NAMESPACE);
}
public function setId($id)
{
$this->setAttribute('id', $id, self::ATTRIBUTE_NAMESPACE);
}
public function getId()
{
return $this->getAttribute('id', null, self::ATTRIBUTE_NAMESPACE);
}
public function setImage($image)
{
$this->setAttribute('image', $image, self::ATTRIBUTE_NAMESPACE);
}
public function getImage()
{
return $this->getAttribute('image', null, self::ATTRIBUTE_NAMESPACE);
}
public function getImageBase64()
{
return base64_encode($this->getImage());
}
}
lib/model/doctrine/UserTable.class.phpにプロバイダとUIDでユーザー名を検索するメソッドを実装します。
public function getUserByProviderAndUid($providerName, $uid)
{
return $this->createQuery('u')
->where('u.hybridauth_provider_name = ?', $providerName)
->andWhere('u.hybridauth_provider_uid = ?', $uid)
->fetchOne();
}
apps/frontend/modules/auth/actions/actions.class.phpを編集します。
<?php
class authActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$config = sfContext::getInstance()->getConfigCache()->checkConfig(sfConfig::get('sf_config_dir').'/hybrid_auth.yml');
try{
$hybridauth = new Hybrid_Auth($config);
$adapter = $hybridauth->authenticate($request->getParameter('provider'));
$user_profile = $adapter->getUserProfile();
$user = Doctrine::getTable('User')->getUserByProviderAndUid($adapter->id, $user_profile->identifier);
if (!$user) {
// 新規作成
$user = new User();
$user->set('name', $user_profile->displayName);
$user->set('image', file_get_contents($user_profile->photoURL));
$user->set('hybridauth_provider_name', $adapter->id);
$user->set('hybridauth_provider_uid', $user_profile->identifier);
$user->save();
}
$this->getUser()->setAuthenticated(true);
$this->getUser()->setId($user->get('id'));
$this->getUser()->setName($user->get('name'));
$this->getUser()->setImage($user->get('image'));
$this->redirect('@homepage');
}
catch( Exception $e ){
switch( $e->getCode() ){
case 0 : echo "Unspecified error."; break;
case 1 : echo "Hybriauth configuration error."; break;
case 2 : echo "Provider not properly configured."; break;
case 3 : echo "Unknown or disabled provider."; break;
case 4 : echo "Missing provider application credentials."; break;
case 5 : echo "Authentification failed. "
. "The user has canceled the authentication or the provider refused the connection.";
break;
case 6 : echo "User profile request failed. Most likely the user is not connected "
. "to the provider and he should authenticate again.";
$twitter->logout();
break;
case 7 : echo "User not connected to the provider.";
$twitter->logout();
break;
case 8 : echo "Provider does not support this feature."; break;
}
// well, basically your should not display this to the end user, just give him a hint and move on..
echo "<br /><br /><b>Original error message:</b> " . $e->getMessage();
}
return sfView::NONE;
}
public function executeEndPoint(sfWebRequest $request)
{
Hybrid_Endpoint::process();
return sfView::NONE;
}
}
これでhttp://localhost/login/Twitterにアクセスすれば、Twitterの認証ページへジャンプしてログインができるようになります。
ログイン後のユーザー表示とかは、テンプレートでこんな感じに。
<img src="<?php echo 'data:;base64,' . $sf_user->getImageBase64(); ?>" alt="<?php echo $sf_user->getName(); ?>">
<?php echo $sf_user->getName(); ?>
後でどうせキャッシュしてしまうのでbase64_encodeのオーバーヘッドは無視です。