「OAuth wrapper for Laravel 4」を利用してOAuthを実装
情報流出のニュースが世間を騒がせている昨今、ログイン機構をOAuthに限定するWebサービスも増えてきました。
セキュリティ対策の難しさを考えれば、小さなWebサービスが取る戦略としては妥当なものかもしれません。
私の作成した「GOいけん(ゴーイケン)」もOAuthに限定しようかと考えたのですが「タブーに縛られない意見を投稿する」という目的から考えて、SNSアカウントを使いたくないという方もいるかと思い、通常のログイン機構も残しました。
目次
OAuth wrapper for Laravel 4の導入方法
Facebookアプリの設定
新規登録用のコントローラーを作成
ログイン用のコントローラーを作成
TwitterアカウントでもOAuthを利用できるようにする
GoogleアカウントでもOAuthを利用できるようにする
OAuth wrapper for Laravel 4の導入方法
githubの「OAuth wrapper for Laravel 4ページ」にありますが、導入は至って簡単です。
今回はWindowsのローカルにXAMPPを構築し、インストールしたLaravelにOAuth wrapper for Laravel 4を導入します。
1.まずはLaravelのルートディレクトにある「composer.json」追加
前後に項目がある場合は最後の「,(カンマ)」を忘れずに。
"require": { //(省略) "artdarek/oauth-4-laravel": "dev-master", }
2.composerでアップデート
コマンドプロンプトでコンポーサーの場所を指定してアップデートを行います。
>cd C:\xampp\htdocs\xampp\example\ >composer update (省略) - Installing artdarek/oauth-4-laravel (dev-master 12d27a1) Downloading: 100% Writing lock file Generating autoload files Generating optimized class loader
3.オートロードサービスプロバイダーとクラスエイリアスに追加
「app/config/app.php」を開きオートロードサービスプロバイダーの項目「‘providers’ => array(…」の最後に追加します。
'Artdarek\OAuth\OAuthServiceProvider',
クラスエイリアス「‘aliases’ => array(」の項目にも追加。
'OAuth' => 'Artdarek\OAuth\Facade\OAuth',
4.OAuth wrapper for Laravel 4の設定ファイルを作成
artisanコマンドで作成する方法が用意されているので、ありがたく利用させていただきます。
コマンドプロンプトにて。
>php artisan config:publish artdarek/oauth-4-laravel
これで「app/config/packages/artdarek/oauth-4-laravel/config.php」が作成されます。
デフォルトでは以下の様なFacebook用の雛形が用意されています。
'Facebook' => array( 'client_id' => '', 'client_secret' => '', 'scope' => array(), ),
Facebookアプリの設定
続いて各アプリケーションを作成して、クライアントIDなどを取得します。(Laravelの設定より、これを探すほうがずっと大変でしたw)
「FacebookのAll Appsページ」へアクセス。
1.新しくFacebookアプリを作成
「Add a New App」ボタンをクリック。
2.Facebookアプリの設定
「Settings > Basic > App Domains」で「example.com」「localhost.example.com」を追加。さらにアプリを動作させるにはメールアドレスも入力する必要があります。
ローカル環境でFacebookのOAuthを利用する方法は「過去の投稿」でまとめたので必要な方は参照してください。
これで設定は完了ですが、AdvancedタブのClient OAuth Loginの項目がONになっていることを確認してください。
再び「Settings > Basic」に戻り「App ID」と「App Secret」を「app/config/packages/artdarek/oauth-4-laravel/config.php」の「client_id」と「client_secret」に追加します。
(App SecretはShowボタンをクリックすれば表示されます)
'Facebook' => array( 'client_id' => '0000000000000000', 'client_secret' => '00000000000000000000000000000000', 'scope' => array(), ),
scopeに値を入力すると、Facebookから取得できるリソースを拡張することができます。
詳しくはFacebookの「Permissions with Facebook Login」を参照してください。
scopeに何も入力せずに取得できる値は「public_profile (Default)」の項目です。
今回はメールアドレスを取得したいので「email」を追加します。(複数指定する場合はカンマ区切りで入力してください。)
'scope' => array(email),
これで準備は完了です。
新規登録用のコントローラーを作成
コントローラーの仕様はサイトによって異なるので合わせて記述してください。
「GOいけん」ではレストフルコントローラーを採用しているので「app/controllers/RegisterController.php」にfacebookページを作成します。
雛形は「OAuth wrapper for Laravel 4」のUsage examplesにあります。
雛形を簡単に解説すると、$fb->getAuthorizationUri()でURLを作成してcodeを取得したら再びリダイレクトしてtokenを取得、その値を使ってユーザー情報を取得するという流れになっています。
そのままコピーして「dd($result);」の部分を「var_dump($result);」として書き出すと、以下の値が取れます。
array(11) { ["id"]=> string(15) "000000000000000" ["email"]=> string(19) "hoge@example.com" ["first_name"]=> string(7) "hoge" ["gender"]=> string(4) "male" ["last_name"]=> string(5) "huga" ["link"]=> string(60) "https://www.facebook.com/app_scoped_user_id/000000000000000/" ["locale"]=> string(5) "ja_JP" ["name"]=> string(14) "hoge huga" ["timezone"]=> int(9) ["updated_time"]=> string(24) "2013-07-25T06:54:03+0000" ["verified"]=> bool(true) }
ちゃんとscopeに指定したemailの情報も取得できてますね。
後はこれらの値を使って好きに調理します。今回はEloquent ORMでデータベースへ書き込んでみました。
public function getFacebook() { // get data from input $code = Input::get( 'code' ); // get fb service $fb = OAuth::consumer( 'Facebook' ); // check if code is valid // if code is provided get user data and sign in if ( !empty( $code ) ) { // This was a callback request from facebook, get the token $token = $fb->requestAccessToken( $code ); // Send a request with it $result = json_decode( $fb->request( '/me' ), true ); // バリデーションでチェックするには連想配列にする必要あり $oa_id = array('oa_id' => $result['id']); // バリデーションルールの指定 // usersテーブルのoa_flagsがfacebookの行でoa_idが一意かをチェック $rules = array( 'oa_id' => 'unique:users,oa_id,NULL,id,oa_flags,facebook', ); // バリデーションメッセージの指定 $messages = array( 'unique' => 'このユーザーは既に登録されています。', // カスタムメッセージ(オリジナルだと変数名が表示されるので) ); // バリデーションチェック $validator = Validator::make($oa_id, $rules, $messages); // バリデーションNGなら if($validator->fails()) { return Redirect::to('register')->withInput()->withErrors($validator); } // Eloquent ORMで$userインスタンスを作成してデータベースへ書き込む $user = new User; $user->oa_flags = "facebook"; // フラグのセット $user->oa_id = $result['id']; // サービスごとに一意の値をセット $user->oa_email = $result['email']; // サービスで利用しているメールアドレスをセット $user->save(); // ログインしてID取得してリダイレクトする Auth::login($user); $id = Auth::user()->id; return Redirect::to('user/'.$id); } // 一番最初にアクセスした時 else { // get fb authorization $url = $fb->getAuthorizationUri(); // return to facebook login url return Redirect::to( (string)$url ); } }
OAuthで複数のSNSアカウントを利用する場合、ユーザーの一意性を確保するには何のサービスを使ってログインしているかと、サービス内で一意のIDをセットにしておく必要があります。
今回はusersテーブルにoa_flagsとoa_idというカラムを追加して管理することにしました。
そして、その値を使って登録済みかどうかのバリデーションをしています。
ログインに成功したら「user/<id>」ページヘリダイレクトします。
これで「/register/facebook」にアクセスすればFacebookのユーザー情報を利用して、ユーザー登録することができるようになりました。
ログイン用のコントローラーを作成
登録ができたのでログインも全く同じように実装します。
登録とログインが同じボタンの例も多いですが、ログインページのボタンをクリックすると登録されるというのも、なんかモヤモヤするので、今回はログインページを「login/facebook」に分けました。
「app/controllers/LoginController.php」に追加します。
public function getFacebook() { // get data from input $code = Input::get( 'code' ); // get fb service $fb = OAuth::consumer( 'Facebook' ); // check if code is valid // if code is provided get user data and sign in if ( !empty( $code ) ) { // This was a callback request from facebook, get the token $token = $fb->requestAccessToken( $code ); // Send a request with it $result = json_decode( $fb->request( '/me' ), true ); $user = User::where('oa_flags', "facebook")->where('oa_id', $result['id'])->first(); // 登録されていない場合は別処理 if ( !empty( $user ) ) { // 登録されている場合 if (Auth::loginUsingId($user->id)) { // ログインしたらユーザーページヘリダイレクト $id = Auth::user()->id; return Redirect::to('user/'.$id); } } else { // 登録されていない場合はリダイレクトでエラー表示 $message = '<p class="vali_error">ご指定のユーザーは、まだ登録されていません。</p>'; return Redirect::to('login')->withErrors([$message]); } } // if not ask for permission first else { // get fb authorization $url = $fb->getAuthorizationUri(); // return to facebook login url return Redirect::to( (string)$url ); } }
先ほど作成した「oa_flags」と「oa_id」を使ってユーザー情報を取得。
$result[‘id’]が取得できている時点で正当なFacebookユーザーであることは確認済みなので、確認とログインが同時にできるAuth::attempt()ではなく、Auth::loginUsingId()でログインします。
ログインしたら登録時と同じようにユーザーページヘリダイレクトします。
以上でFacebookのユーザー情報を利用して新規登録とログインをする仕組みができました。
TwitterアカウントでもOAuthを利用できるようにする
tokenの取得方法などは多少異なるのでライブラリのサンプルを参照してください。
しかし基本的な考え方は同じです。
client_idやclient_secretの取得ページがわからず手間取ったのでメモがてら残しておきます。
1.アプリケーションを作成するアカウントで携帯電話を登録する
アプリケーションの作成するユーザーは携帯電話の登録が必須になったので「設定 > モバイル」で登録します。
携帯番号を入力するとコードが送られてくるので認証を行います。
ちなにみ認証用のコードが記載されたメールは海外から届くため、迷惑メール設定に注意してください。「迷惑メールの問題」でだいぶ時間を無駄にしました。
2.新しくTwitterアプリを作成
「Twitter Application Management」へアクセスして「Create New App」をクリック。
3.Twitterアプリの設定をする
「Name:」にアプリ名、
「Description:」に説明
「Website:」にURL
「Callback URL:」に同じURL
Yes, I agreeにチェックして「Create」ボタンクリック
4.client_idとclient_secretに対応する値を取得
登録が完了したらKeys and Access TokensタブにあるConsumer Key (API Key)がclient_id、Consumer Secret (API Secret)がclient_secretにそれぞれ対応します。
Facebookの時と同じように「app/config/packages/artdarek/oauth-4-laravel/config.php」にTwitter用の設定を追加します。
'Twitter' => array( 'client_id' => '0000000000000000000000000', 'client_secret' => '00000000000000000000000000000000000000000000000000', // No scope - oauth1 doesn't need scope ),
これでコントローラーを作成すれば、TwitterのOAuthも使えるようになります。
補足、var_dumpで取れる$resultの値
解説にありますがTwitterはoauth1なので規格的にscopeがありません。
下記の$resultの出力を見ればわかる通り、メールアドレスは提供されないため、どうしても必要なら追加でメールアドレスの入力を求めるページに飛ばす必要があります。
認証を行うという本来の目的には使えますが、メールアドレスを渡さないというのは納得がいきませんね。(自分はアプリの登録に携帯の番号を強要するくせにw)
array(42) { ["id"]=> float(000000000000) ["id_str"]=> string(10) "000000000000" ["name"]=> string(11) "hoge" ["screen_name"]=> string(10) "example.com" ["location"]=> string(0) "" ["profile_location"]=> NULL ["description"]=> string(197) "000000000000000000000000" ["url"]=> string(22) "http://t.co/0000000000" ["entities"]=> array(2) { ["url"]=> array(1) { ["urls"]=> array(1) { [0]=> array(4) { ["url"]=> string(22) "http://t.co/00000000" ["expanded_url"]=> string(19) "http://example.com" ["display_url"]=> string(11) "example.com" ["indices"]=> array(2) { [0]=> int(0) [1]=> int(22) } } } } ["description"]=> array(1) { ["urls"]=> array(0) { } } } ["protected"]=> bool(false) ["followers_count"]=> int(324) ["friends_count"]=> int(371) ["listed_count"]=> int(0) ["created_at"]=> string(30) "Wed Dec 24 11:13:19 +0000 2014" ["favourites_count"]=> int(0) ["utc_offset"]=> NULL ["time_zone"]=> NULL ["geo_enabled"]=> bool(false) ["verified"]=> bool(false) ["statuses_count"]=> int(5) ["lang"]=> string(2) "ja" ["status"]=> array(22) { ["created_at"]=> string(30) "Thu Jan 01 05:46:03 +0000 2015" ["id"]=> float(0000000000000) ["id_str"]=> string(18) "0000000000000" ["text"]=> string(74) "0000000000000" ["source"]=> string(66) "
GoogleアカウントでもOAuthを利用できるようにする
こちらも同じくメモがてら残します。
1.Googleアプリを作成
ログインして「Applications Overview」へアクセス。
「Create Application」ボタンをクリックしてアプリケーションを作成。
1.Googleアプリの設定
「Application Identifier:」にアプリ名、
「Application Title:」に解説、
「Authentication Options (Advanced):」は「Open to all Google Accounts users (default)」を選択。
「Create Application」ボタンクリック。
「Google Developers Console」へアクセスして左のMENUから「APIと認証」にある「認証情報」をクリック。
続いて「クライアントIDを作成」をクリック。
アプリケーションの種類 | ウェブ アプリケーション |
---|---|
承認済みの JAVASCRIPT 生成元 | http://example.com/ http://localhost.example.com/ (あとで設定するのも面倒なので、ローカル用と本番用の2つを予め登録しておきます。) |
承認済みのリダイレクト URI | http://example.com/login/google http://example.com/register/google http://localhost.example.com/login/google http://localhost.example.com/register/google (Googleの場合は認証後にリダイレクトするURLをきっちり最後まで指定しないとダメみたいです。) |
続いて左のメニューから「認証画面」をクリックして表示されたウィンドウにそれぞれの項目を入力。
メールアドレス | hoge@example.com |
---|---|
サービス名 | hoge |
ホームページの URL (省略可) | http://example.com/ |
サービス ロゴ (省略可) | http://example.com/image/google-api-120×120.png (120×120で画像必要みたい) |
GOOGLE+ ページ (省略可) | 000000000000000000000 |
以上で「保存」ボタンをクリック。
3.client_idとclient_secretに対応する値を取得
クライアント IDがclient_id、クライアント シークレットがclient_secretに対応しています。
上2つと同じように「app/config/packages/artdarek/oauth-4-laravel/config.php」に追記
'Google' => array( 'client_id' => '0000000000000000000000000000000000000000000mh.apps.googleusercontent.com', 'client_secret' => '000000000000000000000', 'scope' => array('userinfo_email', 'userinfo_profile'), ),
scopeはuserinfo_emailでメールアドレス、userinfo_profileでID情報を取得しています。
「openid profile email」なんかがあるようです。
詳しくは「OpenID Connect (OAuth 2.0 for Login) – Google Accounts Authentication and Authorization — Google Developers」を参照してください。
これでGoogleのアカウントでもOAuthが利用できるようになりました。
補足、var_dumpで取れる$resultの値
array(10) { ["id"]=> string(21) "000000000000000000" ["email"]=> string(19) "hoge@example.com" ["verified_email"]=> bool(true) ["name"]=> string(12) "hogehuga" ["given_name"]=> string(6) "huga" ["family_name"]=> string(6) "hoge" ["link"]=> string(45) "https://plus.google.com/000000000000000000" ["picture"]=> string(92) "https://lh5.googleusercontent.com/-VHq8AnIX40U/AAAAAAAAAAI/AAAAAAAAAEM/9-Of1GZvNPg/photo.jpg" ["gender"]=> string(4) "male" ["locale"]=> string(2) "ja" }
以上で、Twitter・Facebook・Googleのアカウントから新規登録・ログインができるようになりました。
Facebookアプリの設定について質問させてください。
私自身、一般ユーザーがFacebookログインができるよう、自身のWEBサービスに実装したいと思っています。
Developersの「Status & Review」項目の「Items in Review」より、Facebookに申請しての通過認証は不要なのでしょうか。
説明不足で失礼しました。
Developersの「Status & Review」項目の「Status」タブの「Do you want to make this app and all its live features available to the general public?」と書かれた項目は「YES」にしておく必要があります。
「Items in Review」タブの項目は設定しなくても動作します。
しかし各サービスのアプリ作成画面は、作成するごとにコロコロ変わるので迷いやすいですね。
早速のご返答、ありがとうございます!
publicをYESにすれば、アクセスしたユーザはログイン可能になりますね。Items in Reviewは、ユーザの一般情報以上を入手したいときにFacebookになぜその情報が必要なのか、申請しなくてはいけないのですね。
なるほど、そんな登録が必要だったんですね。
毎回メールアドレスくらいしか取得してなかったので知りませんでした。
情報ありがとうございました。
・どのページにアクセスしても常にログイン認証済みかどうかの判定はどうしておりますでしょうか?セッションにFacebookのIDを保存させてそれがあるかないかだけの判定は危険でしょうか?
・ログアウトさせたいときはどうすればよいでしょうか??
ログイン済みかの判定はセッションではなく、ControllerでAuth::check()をフラグにifで分岐すればできます。
ログアウトはログアウト用のControllerでAuth::logout()としてください。
大変ありがたいことにLaravelは最新のドキュメントが日本語化されているので、ぜひ利用しましょう。
「認証 5.1 Laravel」