はじめに
FuelPHP でいわゆるソーシャルログインを実装した時のメモです。
FuelPHP では、Opauth というパッケージを使うことにより容易にソーシャルログインを実装することが可能です。
ここでは Facebook を使ったソーシャルログインを実装する例をメモとして残しておきます。
Facebookアプリを作成
これは FuelPHP に限った話ではないですが、Facebook の使ったソーシャルログインを実装するためには、まず Facebook 側でソーシャルログイン用の Facebook アプリを作成しておく必要があります。
Facebook アプリを作成するには、Facebook の開発者登録が必要です。
この辺については、Facebookの開発者登録をしてアプリIDを取得する を参照ください。
アプリを作成したら、アプリIDとsecret が作成されるので記録しておきます。
FuelPHP 側の設定
以降、FuelPHP側での対応です。
FuelPHP-1.8.2 の例になります。
FuelPHP インストール
まずは、FuelPHP をインストールします。
詳細はFuelPHPのインストール を参照ください。
以降、FuelPHP を /project/test1
以下にインストールした例となります。
パッケージインストール
次に、Facebook ログインの実装に必要なパッケージをインストールします。
パッケージは Composer を使ってインストール可能なので、まずは下記のように composer.json
に追記します。
"opauth/opauth": "0.4.*",
"opauth/facebook": "dev-master",
下記のように Composer を一度 self-update しつつ、パッケージをインストールします。
% php composer.phar self-update
% php composer.phar update
設定
まず、config.php
を下記のように設定して、auth パッケージを有効化します。
'always_load' => array(
'packages' => array(
'orm',
'auth',
),
),
次に、packages/auth/config/opauth.php
を app/config/
以下にコピーし、下記のように記録しておいた Facebook のアプリIDとsecret を記載します。
また、email 情報も取得したい場合には、デフォルトでは email 情報は得られないので、下記のように scope と fields も設定します。
'Strategy' => array(
'Facebook' => array(
'app_id' => '1111111111111111',
'app_secret' => 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
'scope' => 'email',
'fields' => 'email,first_name,last_name,name',
),
),
DB作成
通常の FuelPHP 使用時と同様に、MySQL などの DB 環境を整備し、FuelPHP 側の設定を行います。
FuelPHPからDB接続ができる状態となったら、Opauth に必要なDBテーブルを作成するために、下記を実行します。
% php /project/test1/oil refine migrate --packages=auth
ここまでで設定関係は完了となります。
実装
ここまでで設定した内容を使って、Facebook ログインを実装する例です。
ここでは非常に簡単な例として、下記のようなサイトを作成します。
URL path | 概要 |
---|---|
/sample/index | ログイン済みの時のみ表示可能とする(未ログインなら /user/login にリダイレクト) |
/user/login | Facebook ログインするためのリンクを表示 |
/user/auth/facebook | Facebook ログイン |
/sample/index
まずは、ログイン済みの時だけ表示可能な /sample/index を実装します。
手っ取り早く下記で generate します。
% php /project/test1/oil g controller sample index
Creating view: /project/test1/fuel/app/views/template.php
Creating view: /project/test1/fuel/app/views/sample/index.php
Creating controller: /project/test1/fuel/app/classes/controller/sample.php
app/views/sample/index.php はログイン済みの時に表示される内容なので、適当に表示したい内容に修正します。
app/classes/contoller/sample.php は、生成時の状態に before() を追加して、未ログイン時にログインページにリダイレクトするようにします。
<?php
class Controller_Sample extends Controller_Template
{
public function before()
{
parent::before();
if ( ! Auth::check())
{
Session::set_flash('error', 'no login');
Response::redirect('user/login');
}
}
public function action_index()
{
$data["subnav"] = array('index'=> 'active' );
$this->template->title = 'Sample » Index';
$this->template->content = View::forge('sample/index', $data);
}
}
/user/login
次に、ログインするためのリンクを表示する /user/login を実装します。
未ログインの場合には、このページにリダイレクトされてきます。
こちらも、手っ取り早く下記のように generate します。
% php /project/test1/oil g controller user login
Creating view: /project/test1/fuel/app/views/user/login.php
Creating controller: /project/test1/fuel/app/classes/controller/user.php
app/views/user/login.php は、Facebook ログインする際には /user/auth/facebook に遷移させるようにします。
下記は、単にリンクだけを置く例です。
<ul class="nav nav-pills">
<li class='<?php echo Arr::get($subnav, "login" ); ?>'><?php echo Html::anchor('user/auth/facebook','Facebookでログインする');?></li>
</ul>
app/classes/controller/user.php は、とりあえず /user/login については特に何も手を入れなくてもOKです。
/user/auth/facebook
今回の本題となる Facebook ログインを実装します。
基本的には、下記の公式ドキュメントの通りに実装すれば問題ありません。
今回は、/user/auth/facebook という path なので、app/classes/controller/user.php に Facebook ログインのコードを実装していきます。
action_auth()
まず、/usr/auth/facebook の path に直接関係する action_auth() メソッドを実装する必要がありますが、このメソッドはドキュメントの通り下記の内容でOKです。
public function action_oauth($provider = null)
{
// 呼び出すための OAuth プロバイダを持っていない場合は出て行く
if ($provider === null)
{
\Messages::error(__('login-no-provider-specified'));
\Response::redirect_back();
}
// Opauth の読み込み、プロバイダのストラテジーを読み込みプロバイダにリダイレクトするでしょう
\Auth_Opauth::forge();
}
コメントに書かれている通り、有効な provider (今回であれば facebook)が指定された場合には、\Auth_Opauth::forge()
により、指定された provider (Facebook) のページにリダイレクトされます。
action_callback()
action_callback() は、Facebook のページでログイン後にコールバックされるメソッドです。
ここで、Facebook での認証結果を取得します。
public function action_callback()
{
try {
$opauth = Auth_Opauth::forge(false);
$status = $opauth->login_or_register();
$url = 'sample/index';
Log::warning("login_or_register:" . $status);
switch ($status) {
case 'linked':
// ログイン済みでプロバイダはこのユーザーと関連付けられている
break;
case 'logged_in':
// 既知のプロバイダへ関連付けられ、そのアカウントでログイン
break;
case 'register':
// このプロバイダでのログインは関連付けられていないので、ローカルアカウントを作成する必要あり
$url = "user/register";
break;
case 'registered':
// このプロバイダでのログインは関連付けられていないが十分な情報が返されたので、ローカルアカウントを自動的に登録した
break;
default:
// 状態を判定できず
}
Response::redirect($url);
}catch(OpauthException $e) {
// 認証失敗
}catch(OpauthCancelException $e) {
// 認証失敗(キャンセル)
}
}
login_or_register()
で認証結果を取得することができるので、これを使って判定します。
ここでは、linked / logged_in / registered の時にはログインが完了したことを示しているので、/sample/index にリダイレクトするようにしています。
register の時には、Facebook ログインは成功しているが、まだローカルアカウントと関連付けられていない(ローカルアカウントが作成されていない)状態なので、ローカルアカウントと関連付けるために /user/register にリダイレクトするようにしています。
action_register()
action_register() では、Facebook ログインの情報を関連付けつつローカルアカウントを作成する処理を行います。
public function action_register()
{
if ($authentication = Session::get('auth-strategy.authentication', array()))
{
try
{
$user = Session::get('auth-strategy.user');
$user_id = Auth::create_user(
$user['email'],
'dummy',
$user['email'],
Config::get('application.user.default_group', 1),
array(
'fullname' => $user['name'],
)
);
if ($user_id)
{
$opauth = Auth_Opauth::forge(false);
// call Opauth to link the provider login with the local user
$insert_id = $opauth->link_provider(array(
'parent_id' => $user_id,
'provider' => $authentication['provider'],
'uid' => $authentication['uid'],
'access_token' => $authentication['access_token'],
'secret' => $authentication['secret'],
'refresh_token' => $authentication['refresh_token'],
'expires' => $authentication['expires'],
'created_at' => time(),
));
Auth::instance()->force_login((int) $user_id);
Session::set_flash('success', __('login.new-account-created'));
Response::redirect_back('calendar');
}
else
{
\Session::set_flash('error', __('login.account-creation-failed'));
}
}
// catch exceptions from the create_user() call
catch (SimpleUserUpdateException $e)
{
// メールアドレスが重複
if ($e->getCode() == 2)
{
Session::set_flash('error', __('login.email-already-exists'));
}
// ユーザー名が重複
elseif ($e->getCode() == 3)
{
Session::set_flash('error', __('login.username-already-exists'));
}
// これは起こり得ないが、ずっとそうとは限らない...
else
{
Session::set_flash('error', $e->getMessage());
}
}
}
else
{
Session::set_flash('error', 'Failed to retrieve a user information from the provider.');
}
Response::redirect_back('user/login');
}
Session::get('auth-strategy.authentication')
で認証情報が、Session::get('auth-strategy.user')
でそれに紐づくユーザー情報が取得できます。
これらを使ってローカルユーザーを作成し、provider login と紐付けることをしています。
ここまでの実装で、一通り Facebook ログインが機能することが確認できます。
一度試してみてしまえば簡単なロジックなので、わりといろいろとアレンジできると思います。
Facebook 以外に Twitter や Google あたりには少なくとも対応しているようです。
コメント