Symfony 1.4でHybridAuthを使ってtwitter/facebookログインを実装する

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(); ?>">&nbsp;
<?php echo $sf_user->getName(); ?>

後でどうせキャッシュしてしまうのでbase64_encodeのオーバーヘッドは無視です。


タグ: , , , ,

新しく会社作りました!

コメント / トラックバック 2 件

  1. […] « Symfony 1.4でHybridAuthを使ってtwitter/facebookログインを実装する […]

  2. […] Symfony 1.4でHybridAuthを使ってtwitter/facebookログインを実装する – digital matter […]

コメントをどうぞ