Laravel Socialiteは複雑なSNS認証を高機能で使いやすいインターフェイスとして手軽に組み込めるパッケージです。
SocialiteがOAuthプロバイダーとしてサポートしているのはGoogle、Facebook、Twitter、LinkedIn、GitHub、Bitbucketです。サポート対象が拡大される予定はないものの、コミュニティーが開発を進めているコレクション、Socialite Providersを使えば非公式ですが多くのSNSをSocialiteのOAuthプロバイダーとして使えます。詳しくは、後で説明します。
この記事では、Laravelアプリケーションのインスタンスが稼働していて、コードを実際に試せる環境が整っていることを前提にしています。もし開発環境が必要なら、無料のHomestead改良版を使えます。
フォーム認証
OAuth認証に入る前に、Laravel標準のフォーム認証を設定します。Artisanコマンドmake:authを実行して、必要なビューと認証エンドポイントをインストールしてください。
php artisan make:auth
usersテーブルを作成するためにphp artisan migrateも実行します。
これでBootstrapスタイルのログインページが/loginに表示されます。
SNS認証の追加
Composerを使ってSocialiteをインストールします。
composer require laravel/socialite
インストールが終われば通常のLaravelパッケージと同様に、Socialiteのサービスプロバイダーとファサードがconfig/app.phpに登録されます。
こちらがファサードのエイリアスです。
<?php
// ...
'aliases' => [
// ...
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
],
// ...
Socialiteは、サービスコンテナの内部にレイジーロードするシングルトンサービスとして登録されます。
設定
使いたいプロバイダーのプラットホームにOAuthアプリケーションを登録します。プロバイダーのAPIと通信するためのclient IDとclient secret keyを入手します。
プロバイダーごとにclient IDとsecret keyをconfig/services.phpに追加します。
// ...
'facebook' => [
'client_id' => env('FB_CLIENT_ID'),
'client_secret' => env('FB_CLIENT_SECRET'),
'redirect' => env('FB_URL'),
],
'twitter' => [
'client_id' => env('TWITTER_CLIENT_ID'),
'client_secret' => env('TWITTER_CLIENT_SECRET'),
'redirect' => env('TWITTER_URL'),
],
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_URL'),
],
// ...
実際のkeyはプロジェクトルートディレクトリにある.envファイルに記入します。
データベースの検討
usersテーブルはSNS認証を組み込むことを想定してデザインされていないので、少し工夫が必要です。
SNS認証を選んだユーザーにはパスワード設定を通常は求めません(OAuth認証後にパスワードを要求するのは避けてください)。さらに選択したOAuthプロバイダーには登録メールアドレスがないかもしれません。したがって、usersテーブルのemailとpasswordフィールドをnullableにします。
Laravelのスキーマビルダーでスキーマを修正します。既存テーブルのフィールドを変更する前にdoctrine/dbalパッケージをインストールします。
composer require doctrine/dbal
最初はusersです。
php artisan make:migration prepare_users_table_for_social_authentication --table users
続いてemailとpasswordフィールドをnullableにします。
ユーザーが選択したSNSアカウントへのリンクを保存するモデルとマイグレーションファイルを作成します。
php artisan make:model LinkedSocialAccount --migration
provider_nameはプロバイダー名、provider_idはそのプロバイダーに登録されているユーザーのIDです。
migrateを実行して変更を適用します。
php artisan migrate
モデル
1人のユーザーが複数のSNSアカウントと接続することもあるので、UserとLinkedSocialAccountsは1対多のリレーションにします。1対多のリレーションを実装するためにUser モデルに次のメソッドを追加します。
逆のリレーションをLinkedSocialAccount modelに追加します。
続いてprovider_nameとprovider_idをLinkedSocialAccountsの$fillable配列に追加して、複数の値を保存できるようにします。
create()メソッドでユーザーとSNSアカウントを関連付けられるようになりました。
コントローラー
Authネームスペースにコントローラーを作成します。コントローラークラスにはOAuthプロバイダーへユーザーをリダイレクトするアクションとプロバイダーからコールバックを受け取るアクションが必要です。
php artisan make:controller 'Auth\SocialAccountController'
コントローラークラスを次のように編集します。
上のコードではredirectToProvider()がプロバイダーのredirect()メソッドを呼び出して、ユーザーをSNS認証エンドポイントへリダイレクトしています。
<?php
// ...
return Socialite::driver($provider)->redirect();
// ...
またredirect()を呼び出す前に、デフォルトのスコープをscopes()で変更できます。
<?php
// ...
return Socialite::driver($provider)->scopes(['users:email'])->redirect();
// ...
OAuthプロバイダーは予期しない動作をすることもあるので、try/catchブロックを使います。例外が発生することなく進めばuserオブジェクト(Laravel\Socialite\Contracts\Userのインスタンス)をプロバイダーから受け取ります。userオブジェクトにはユーザー情報を取得するgetterメソッドがあり、ユーザーの名前、メールアドレス、アクセストークンなどを取得できます。使用可能なメソッドはドキュメントを参照してください。
次にローカルのuserオブジェクト(アプリのusersテーブルに格納されている)を取得するか、存在しなければ作成します。具体的にはヘルパークラスSocialAccountsService(このクラスは変数としてhandleProviderCallback()メソッドに渡します)からfindOrCreate()を呼び出します。
userオブジェクトを取得後にユーザーをログインさせ、ダッシュボードページへリダイレクトします。
ヘルパークラスSocialAccountService.phpを作成しましょう。
Appネームスペースにファイルを作成し次のコードを記入します。
このクラスの役割はローカルuserと関連するSNSアカウントを作成または取得することだけで、メソッドは1つだけです。
findOrCreateメソッドは現在のプロバイダーIDに関連したSNSアカウントが登録されているか確認するクエリをlinked_social_accountsテーブルに発行し、登録されていて、SNSアカウントを含むローカルuserオブジェクトを返します。
<?php
// ...
if ($account) {
return $account->user;
}
// ...
userが存在しないか接続前なら、SNSアカウントは見つかりません。ユーザーが登録フォームから登録している可能性があるので、メールでusersテーブルを検索します。それでもユーザーが見つからなければ、新たにuserのエントリーを作成し現在のSNSを関連付けます。
ルート
SNS認証のためにルートを2本設定します。
この2本のルートを任意のプロバイダーで使えるように、ルートパラメーターproviderを使っています。
事例:Github経由の認証
これまでのコードをテストするために、SNS認証(ログイン)の選択肢としてGitHubを追加します。
最初はGitHubに新しいOAuthアプリケーションを登録します。
アプリ作成ページに必要な項目を入力します。
- Application Name:作成するアプリケーションを説明する名前を入力する。入力した名前がアプリケーションにログインしようとしてGithubにリダイレクトされたユーザーに表示される
- Homepage URL:作成するWebサイトのURLでhttp://localhost:8000、または有効なドメインを指定する
- Authorization Callback URL:作成するWebサイトのエンドポイントで、認証完了後にユーザーがリダイレクトされる
アプリケーションを作成するとエディットページにリダイレクトされるので、そこでClient IDとSecret keyを取得します。
設定
次にGitHubのClient IDとSecret keyをconfig/services.phpに追加します。
Client ID、Secret key、コールバックURLを直接config/services.phpに書き込むのではなく、アプリケーションの.envファイルに保存して、getenv()でservices.phpファイルに自動的に読み込みます。これによりコードを触らずに本番環境で値を変更できます。
ログインページにGitHubへのリンクを追加
最後にGitHubへのリンクをログインページに追加します。resources/views/auth/login.blade.phpを開いて、次のコードを適切な位置に追加します。
次のような画面になります。
「Login with Github」をクリックすると、Githubの認証ページが表示されます。
Socialite Providersプロジェクト
Socialite Providersはたくさんの非公式プロバイダーをSocialiteで使えるようにするプロジェクトで、コミュニティーで開発が進められています。プロバイダーは独立したパッケージとしてComposer経由でインストールされます。
プロバイダーはSocialite Providersプロジェクトで開発されたManager packageを使ってSocialite providersに登録します。このpackageはプロバイダーと同時に依存オブジェクトとしてインストールされます。
Manager packageはLaravelのサービスプロバイダーに含まれており、Socialiteデフォルトのサービスプロバイダーを継承しています。Socialite Providersコレクションのプロバイダーを使うときには、Socialiteのサービスプロバイダーを置き換えます。
注意:サービスプロバイダー(Service Providers)とSocialite Providersは、名前は似ているものの異なるものなので混同しないでください。サービスプロバイダーはLaravelのサービスコンテナにサービスを登録するクラスであり、Socialite Providers(または単にプロバイダー:Providers)はOAuthプロバイダーとやり取りするクラスです。
コレクションに含まれるプロバイダーにはそれぞれイベントリスナーがあり、app/Provider/EventServiceProviderクラスに追加して、SocialiteWasCalledイベントを検知できるようにします。
SocialiteにアクセスするとSocialiteWasCalledイベントが発動し、このイベントを待ち受けているプロバイダーはSocialiteに登録されます(オブザーバーパターンの実装)。
上の例では、プロバイダーをDeezer経由で認証できるように登録しています。
注意:Socialiteの標準プロバイダーは同名のプロバイダーで、オーバーライドしない限り引き続き使用できます。
事例:Spotify経由の認証
Socialite Providersの例としてSpotifyをログインの選択肢に追加します。
最初にSocialite Providersにアクセスして、Spotifyのプロバイダーを左サイドバーの中から見つけます。
インストールと使い方を説明したマニュアルがプロバイダーごとに用意されています。Composerを使ってSpotifyのプロバイダーをインストールします。
composer install socialproviders/spotify
設定
先ほどと同様にアプリをSpotifyの開発者プラットホームに登録し、Client IDとSecret keyを取得して、アプリに設定します。
Manager packageを使えば、新しいプロバイダーの設定は簡単です。標準のプロバイダーとは違って、config/services.phpにプロバイダーごとに行を追加する必要はありません。代わりにアプリケーションの.envファイルに設定を追加するだけです。Manager packageのConfig Retrieverヘルパークラスのおかげでできることです。
設定はCLIENT_ID、CLIENT_SECRET、REDIRECT_URLの文頭にプロバイダー名を付けたものです。
ビュー
次にログインページに「Login with Spotify」のリンクを追加します。
次のようなログインページが表示されます。
ルートは先ほどの事例で定義したものを再利用(Github経由の認証)するか、新しいコントローラーとロジックで作成します。
Login with SpotifyをクリックするとSpotifyの認証ページにリダイレクトされます。
この画面が表示されれば成功です!
カスタムプロバイダーの作成
Socialite Providersコレクションにプロバイダーがない場合には、自分で簡単に作成できます。
プロバイダーには次の2要素が必要です。
- providerクラス
- イベントリスナー
Providerクラス
providerクラスにはOAuthに関連するオペレーションのロジックすべてを実装します。
注:もしOAuth 1.0も使いたいなら、providerクラスを別に用意する必要があります。
最初にSocialite Providersコレクションに含まれているDeezerのproviderクラスを示します。
providerクラスは抽象クラスLaravel\Socialite\Two\AbstractProviderを継承しています。この抽象クラスにはOAuth 2.0のオペレーション全般を扱うメソッドがあり、スコープのフォーマットやアクセストークンの取得と使用などができます。この抽象クラスを継承して抽象メソッドを実装します。
加えてProviderInterfaceも実装します。このインターフェイスによりredirect()とuser()メソッドの実装が必要になります。
すでに述べたとおり、redirect()はユーザーをOAuthプロバイダーの認証ページへリダイレクトし、user()はLaravel\Socialite\Contracts\Userのインスタンスを返します。このインスタンスにはプロバイダーが保有するユーザーの情報が含まれます。
プロバイダーのイベントリスナー
プロバイダーのイベントリスナーは、SocialiteWasCalledイベント発動時にプロバイダーをSocialite providerとして登録するクラスです。
Deezerのイベントリスナーを示します。
SocialiteWasCalledイベントにはextendSocialite()メソッドがあり、プロバイダーのクラスを引数として受け取りSocialiteに登録します。
最後に
SNS認証はLaravelを使えば簡単に実装できます。記事ではさまざまなOAuthプロバイダーを使ってユーザーを認証する方法やカスタムプロバイダーを作る方法を説明しました。
プロバイダーの名前とIDに加えて、アバター、アクセストークン、リフレッシュトークン(該当時)などのSNSの情報をusersテーブルに保存できます。プロバイダーのAPIで通信もでき、なんらかの操作をユーザーの代理で実行もできます。もちろんユーザーの許可を得ている場合だけですが。
この記事のコード全文はGitHubで入手できます。実際に自分で試してみてください。
※本記事はWern Anchetaが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:Easily Add Social Logins to Your App with Socialite)
[翻訳:内藤夏樹/編集:Livit]
[Image:mirtmirt / Shutterstock.com]