localdisk

PHP とか Java とか Web とか好きなことを書きます。

Laravel Socialite の独自ドライバを実装する

この記事は Laravelリファレンス発売記念!販売促進!! Advent Calendar 2015 - Adventar の3日目の記事です。

ちょうど1年前にこんな記事を書きました。

qiita.com

ドライバの独自実装

今書いています。ごめんなさい。更新したら通知するようにしますのでストックしてくれていいのよ?(チラチラ http://qiita.com/localdisk/items/2e2724f31864fd49b675

気がついたら一年経ってたよ…。というわけでドライバの実装をしてみたいと思います。現在 Socialite で実装されているドライバは

となります。一年前と比較すると LinkedIn と BItbucket が増えましたね。今回は折角なので はてなのOAuth ドライバを実装したいと思います。自分はてなーなので。

はてなの OAuth のバージョン

調べてみるとはてなの OAuth のバージョンは1のようです。Socialite だと一緒なのは Twitter と Bitbucket ですね。ソースコードを読んでみると2つクラスを作る必要があるようです。

ですね。まずは、app/Socialite ディレクトリを作成しましょう。

HatenaProvider

空でOK。

<?php

namespace App\Socialite;

use Laravel\Socialite\One\AbstractProvider;

class HatenaProvider extends AbstractProvider
{

}

親クラスの Laravel\Socialite\One\AbstractProvider ですが、abstract class なのに abstract method がない(困惑)のでこれでよいです。

HatenaServer

<?php

namespace App\Socialite;


use Laravel\Socialite\One\User;
use League\OAuth1\Client\Server\Server;
use League\OAuth1\Client\Credentials\TokenCredentials;

class HatenaServer extends Server
{
    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        $scopes = implode(',', config('services.hatena.scope'));

        return "https://www.hatena.com/oauth/initiate?scope={$scopes}";
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return 'https://www.hatena.ne.jp/oauth/authorize';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return 'https://www.hatena.com/oauth/token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return 'http://n.hatena.com/applications/my.json';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        $user = new User();

        $user->uid      = $data['url_name'];
        $user->nickname = $data['display_name'];
        $user->name     = $data['url_name'];
        $user->imageUrl = $data['profile_image_url'];
        $user->email    = '';

        $used = ['url_name', 'display_name', 'profile_image_url'];

        foreach ($data as $key => $value) {
            if (strpos($key, 'url') !== false) {
                if (!in_array($key, $used)) {
                    $used[] = $key;
                }

                $user->urls[$key] = $value;
            }
        }

        // Save all extra data
        $user->extra = array_diff_key($data, array_flip($used));

        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return $data['url_name'];
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return;
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return $data['name'];
    }

}

メソッドの説明

urlTemporaryCredentials

Request token の取得。はてなの場合、ここが特殊で scope というパラメータ名で "承認を求める操作名" を渡す必要があります(複数の場合はカンマ区切りで渡す)。

  • read_public
  • write_public
  • read_private
  • write_private

この情報を渡すために今回は config/services.php に"承認を求める操作名"を定義しています。

<?php

return [
    'hatena' => [
        'client_id'     => 'XXX',
        'client_secret' => 'XXX',
        'redirect'      => 'http://localhost:8000/hatena/login',
        'scope'         => ['read_public', 'write_public']
    ],
];

urlAuthorization

リダイレクトする認証用URLを返します。はてなの場合は PC / スマホ / 携帯電話 用にそれぞれ違うURLが用意されているので真面目に作る場合はデバイス判定が必要です。

urlTokenCredentials

Access token を取得するURLを返します。

urlUserDetails

ユーザー情報を取得するURLを返します。

userDetails

ユーザー取得APIを叩いた結果をオブジェクトに詰めています。

userUid / userEmail / userScreenName

継承元のメソッドはこれらのメソッドをコールした時に通信してるので、そうしないようにオーバーライドしています。Socialite の実装を参考に書いただけはあんまり深い意味はないです。

AuthServiceProvider に登録

独自ドライバを実装したら使えるように登録します。AuthServiceProviderboot メソッドにこんな感じで。

<?php
class AuthServiceProvider extends ServiceProvider
    public function boot(GateContract $gate)
    {
        $this->registerPolicies($gate);

        \Socialite::extend('hatena', function($app) {
            $setting = $app['config']['services.hatena'];
            $config  = array_merge([
                'identifier'   => $setting['client_id'],
                'secret'       => $setting['client_secret'],
                'callback_uri' => $setting['redirect'],
            ], $setting);
            return new HatenaProvider($app['request'], new HatenaServer($config));
        });
    }
}

使ってみる。

<?php
// routes.php
// view にはこんな感じで書く

// <p><a href="{{ url('github/ahthorize') }}">github login</a></p>
// <p><a href="{{ url('hatena/ahthorize') }}">hatena login</a></p>


Route::get('{provider}/ahthorize', function ($provider) {
    return Socialite::with($provider)->redirect();
});

Route::get('{provider}/login', function ($provider) {
    /** @var \Laravel\Socialite\Contracts\User $data */
    $data           = Socialite::with($provider)->user();
    dd($data);
});

シンプル!

参考

Consumer key を取得して OAuth 開発をはじめよう - Hatena Developer Center

宣伝

よろしくおねがいします!

www.amazon.co.jp