Laravel Socialite用のYahoo JAPAN ID(YConnect)ドライバ実装 


まず、Socialiteの一般的な使い方は[こちら](http://qiita.com/zaburo/items/6f7c072795e99fd98a75)をどうぞ。


Laravel Socialiteはすごい便利ですね。但し、標準のドライバがあれば。。。
多くの場合、標準対応の、

* Facebook
* Twitter
* Google+

あたりで事足りるのですが、ある案件で「YahooのIDに対応しろ！」と言われそうなので、調べてみました。

##独自実装までの経緯

まず、「socialite yahoo」でググッてみると、直ぐに、[Yahoo用のドライバ](http://socialiteproviders.github.io/providers/yahoo/)に行き着いた。どやら、Yahoo以外にも[主要なサービス用のものが勢揃い](http://socialiteproviders.github.io/#about)しているよう。。。

とはいえ、海外サービスばかり。ご存知、Yahoo JAPANも、かなり独立性が高いので、認証の仕組みも違うかなと・・・と調べて見たところ、やはり本家とJAPANではずいぶん仕様が違うようで使えそうにありません。

独自に実装するしか無いか・・・と調べていたら、[ドンピシャ](http://localdisk.hatenablog.com/entry/2015/12/03/142656)な記事が。

これで一安心と、喜んだのもつかの間。OAuth1.0とOAuth2.0では、少し実装の仕方が異なる感じ（記事は1.0でYahooは2.0）。

仕方がないので、調べながら実装してみると何とかemailやIdを取得できるところまでは動いたのでメモしておきます。
あまりLaravelの仕組みを理解していないままいじってるので、おかしいところがあるかもしれません。

##前提

LaravelやらSocialiteは既にインストールされていることを前提として進めます。

##実装

先人の記事にならい、app以下にSocialiteというフォルダを作り、そこで作業することにしました。もちろん、他の場所でも問題ありません。

###YahooJpProvider

1.0では、classの実装が必要なようですが、2.0では基本、Providerだけ実装すればいいようです。
他の、Providerの記述や、[Yahooの仕様ページ](http://developer.yahoo.co.jp/yconnect/server_app/explicit/#yconnectStep3)を参考に、必要なパラメーターやメソッドをオーバーライドしていきます。

YahooJpProvider.php

```php
<?php

	namespace App\Socialite;

	use Laravel\Socialite\Two\AbstractProvider;
	use Laravel\Socialite\Two\ProviderInterface;
	use Laravel\Socialite\Two\User;

	class YahooJpProvider extends AbstractProvider implements ProviderInterface
	{

		//scopeの区切り文字設定
		protected $scopeSeparator = ' ';

		//スコープ設定
		protected $scopes = [
	        'openid',
	        'profile',
	        'email',
	    ];

		//認証用URL($stateはオプション)
		protected function getAuthUrl($state)
	    {
	        return $this->buildAuthUrlFromBase('https://auth.login.yahoo.co.jp/yconnect/v1/authorization', $state);
	    }

	    //token取得用URL
	    protected function getTokenUrl()
	    {
	        return 'https://auth.login.yahoo.co.jp/yconnect/v1/token';
	    }

	    //Token取得の際のオプション
	    //Basic認証と必要なPOSTパラメータを送付
	    public function getAccessToken($code)
	    {
	    	$basic_auth_key = base64_encode($this->clientId.":".$this->clientSecret);

	        $response = $this->getHttpClient()->post($this->getTokenUrl(), [
	        	//認証
	        	'headers' => [
	        		'Authorization' => 'Basic '.$basic_auth_key,
	        	],
	            // 'form_params' => $this->getTokenFields($code),
	            // 直接記述
	            'form_params' => [
	            	'grant_type' => 'authorization_code',
	            	'code' => $code,
	            	'redirect_uri' => $this->redirectUrl
	            ],
	        ]);

	        return $this->parseAccessToken($response->getBody());
	    }

	    //ユーザー情報取得
	    protected function getUserByToken($token)
	    {
	    	//url + schema=openidが必要だった
	        $response = $this->getHttpClient()->get('https://userinfo.yahooapis.jp/yconnect/v1/attribute?schema=openid', [
	            'headers' => [
	                'Authorization' => 'Bearer '.$token['access_token'],
	            ],
	        ]);
	        return json_decode($response->getBody(), true);
	    }

	    //Userにパラメーターをマップ（必要に応じてその他のパラメータ追加）
	    protected function mapUserToObject(array $user)
	    {
	        return (new User())->setRaw($user)->map([
	            'id' => $user['user_id'],
	            'name' => $user['name'],
	            'email' => $user['email'],
	        ]);
	    }

	    //token parse用の関数
	    protected function parseAccessToken($body)
	    {
	        return json_decode($body, true);
	    }

	}
```

本質的な機能の実装はとりあえずこんな感じ。
ここで、[Yahooデベロッパーネットワーク](http://developer.yahoo.co.jp/)にて取得・設定した情報を.envに追加しておきます。

```bash
#YahooJp
YahooJp_ID=アプリケーションID
YahooJp_SECRET=シークレット
YahooJp_CALLBACKURL=http://localhost:8000/yahoojp/callback（コールバックURL）
```

あとは、config/services.phpに.envパラメータとのmapを記述しておきます。

```php
    'yahoojp' => [
        'client_id'     => env('YahooJp_ID'),
        'client_secret' => env('YahooJp_SECRET'),
        'redirect'      => env('YahooJp_CALLBACKURL'),
    ],
```

ここまでで主要機能の実装は完了です。

##初期化およびプロバイダのコンテナへの登録

ちょっと悩んだのが、機能を使えるようにする方法です。providerのAuthServiceProviderのbootに書くこともできるようですが、ここでは別途プロバイダクラスを用意し、サービスプロバイダに登録することにしました。

###MySocialManager

まずは、登録するクラスを用意します。

Laravel\Socialite\SocialiteManagerを継承して、yahoojpドライバの生成を記述します。
config/services.phpから必要な情報を読み込み初期化します。

MySocialManager.php

```php
<?php

	namespace App\Socialite;

	use Laravel\Socialite\SocialiteManager;

	class MySocialManager extends SocialiteManager{

		protected function createYahooJpDriver()
		{
			//services.phpの設定情報を読む
			$config = $this->config['services.yahoojp'];
			//設定情報と共にプロバイダを生成
			return $this->buildProvider(
				'App\Socialite\YahooJpProvider',$config
			);
		}
	
	}
```

###MySocialServiceProvider

次にプロバイダクラスを用意します。
Laravel\Socialite\SocialiteServiceProviderを継承します。

MySocialServiceProvider.php

```php
<?php
	
	namespace App\Socialite;

	use Laravel\Socialite\SocialiteServiceProvider;

	class MySocialServiceProvider extends SocialiteServiceProvider
	{
		public function register()
		{
			//シングルトンとしてMySocialManagerを生成
			$this->app->singleton('Laravel\Socialite\Contracts\Factory',function($app){
				return new MySocialManager($app);
			});
		}
	}
```

###config/app.phpの編集

登録するクラスの準備ができたので、'providers' => []中にプロバイダを追加します（一部抜粋）。

```php
App\Socialite\MySocialServiceProvider::class,
```

これで利用できるようになりました。

##利用してみる

では、最低限の機能チェックをしてみます。

###Route設定

ソーシャルログインボタンのクリック先となるリダイレクト用ルートと認証後、戻ってくる先（Callback）用のルートを設定します。

```php
Route::group(['middleware' => 'web'], function () {

    //Yahooログイン（ボタンのリンク先）
    Route::get('yahoojp','YahooJpController@yahoojpLogin');
    //認証後の戻りURL
    Route::get('yahoojp/callback','YahooJpController@yahoojpCallback');

});
```

###Controller

コントローラーを実装します。
と入っても、ドライバさえきちんと動けば、Controller内での実装は難しくはありません。

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;

//追加
use Laravel\Socialite\Contracts\Factory as Socialite;

class YahooJpController extends Controller
{
    //
    protected $socialite;

    public function __construct(Socialite $socialite)
    {
    	$this->socialite = $socialite;
    }

    public function yahoojpLogin()
    {
    	//yahooへリダイレクト
    	return $this->socialite->driver('yahoojp')->redirect();
    }

    public function yahoojpCallback()
    {
    	//ユーザー情報を取得
    	$yuser = $this->socialite->driver('yahoojp')->user();

    	//各情報の取得
    	$yuser->getId();
    	$yuser->getName();
    	$yuser->getEmail();

    	//とりあえず表示
    	return $yuser->getEmail();
    }
}
```

以上です。emailが表示されればOKです。

出来てしまえば簡単ですが、試行錯誤が必要でした。