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をダウンロードします。
ちなみに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のオーバーヘッドは無視です。