laravel
laravel5.7

Laravel Passportの使い方まとめ

Laravel Passportとは

平たく言えばAPIにOAuthに従った認証機能を追加できる機能。Laravelのエコシステムの1つ。
すこし大げさなので標準のapi_tokenを利用したり、外部ライブラリを利用するのもあり。

対応するToken発行方式

PassportはOAuthで定義されている4つの方法に加え、Personal Access (Grant) Tokenという主に内部利用向けのToken発行ができる(名前はOAuthの正式な呼び方とは微妙に違うみたい)。

  1. OAuth2 with authorization codes (Auth Code)→SNS認証でよく使うやつ(確認画面あり・Code発行あり)
  2. Password Grant Token(確認画面無し。Codeなし。ID, PWでTokenを発行)
  3. Implicit Grant Token(いきなりTokenを発行。安全性に問題?)
  4. Client Credentials Grant Token(マシン間通信向け。個人の認証無し)
  5. Personal Access Token(その名の通り、個人・内部利用向け)

私氏自身、OAuth勉強中の身なので間違いなどあればご指摘を。

個人的な利用頻度順に試してみる

一般的にAuth CodeはSNS認証でよく使いますが、自分がプロバイダ側になることはあまりない。
個人的には信頼関係のある人間(社内含む)にTokenを発行してAPIを使ってもらうというシーンを想定するので以下の順序で機能を試してみる。

  • Personal Access Token
  • Password Grant Token(jwt-authはこれに対応?)
  • Client Credentials Grant Token
  • Auth Code
  • Implicit Grant Token

環境準備

Laravelのインストールと.envの設定

Passportを利用したい人が詰まることはないと思うので割愛します。

Passportのインストール

composerでインストール。

composer require laravel/passport

認証機能のスキャフォールド

必須ではありませんが、ユーザーの登録に利用したり、不要なエラーを防止できるので追加しておきます。

php artisan make:auth

Migrate

migrateの実行。

php artisan migrate

oauth_*から始まるテーブルがいくつか作成されます。

app/User.phpの編集

利用するモデルをいじります。
コメントは削除しています。

app/User.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
+use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use Notifiable;
+   use HasApiTokens;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];
}

app/Providers/AuthServiceProvider.phpの編集

Passportで利用するルートを登録します。
コメントは削除しています。

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
+use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    public function boot()
    {
        $this->registerPolicies();
+       Passport::routes();
    }
}

config/auth.phpの編集

api guardのdriverをpassportに変更します。なお、先程User.phpをいじったのは、ここでproviderがusersになっているからです。
必要に応じて他のテーブルを利用することもできます。
コメントは削除しています。

<?php

return [

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
+           'driver' => 'passport',
            'provider' => 'users',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

];

Keyの発行

暗号化に利用するシード値などを生成します。
php artisan passport:installとすると、keysも実行されるのですが、ここでは個別に実行します。

php artisan passport:keys

テストユーザーの生成

テストで利用するユーザーを生成しておきます。ここでは2名のテストユーザーを作成しています。

ここではtinkerで作成しますが、make:authで生成された画面(register)から登録してもOKです。

php artisan tinker
Psy Shell v0.9.8 (PHP 7.2.8 — cli) by Justin Hileman
>>>
>>>
>>> $user1 = new App\User;
=> App\User {#2950}
>>> $user1->name = 'user1';
=> "user1"
>>> $user1->email = 'user1@test.com';
=> "user1@test.com"
>>> $user1->password = Hash::make('testtest');
=> "$2y$10$EfPa4c.4pNNbvsq1qiCbUea7ZhWKcuCWG483RQYYX2QEkef5iEduy"
>>> $user1->save();
=> true
>>>
>>> $user2 = new App\User;
=> App\User {#2958}
>>> $user2->name = 'user2';
=> "user2"
>>> $user2->email = 'user2@test.com';
=> "user2@test.com"
>>> $user2->password = Hash::make('testtest');
=> "$2y$10$XOpROv3mvrPYgS5erPjzW.tuhHtJk4BG1Tw6S.NxfkUgFo2u6PH76"
>>> $user2->save();
=> true

準備はとりあえず以上となります。

Personal Access Token

まず、Personal Access Tokenから試してみます。
商用環境での利用頻度は低いかもしれませんが、開発用等で利用することが想定されるため、Personal Access Tokenを試しておきましょう。

なお、Personal Access Tokenはexpire等を設定しても無視され、半永久的だそうです。ただ、頑張れば設定を変更することもできるようです。

クライアントの生成

OAuthでは、まずクライアント(ID)を生成(取得)する必要があるので実行します。

php artisan passport:client --personal

 What should we name the personal access client? [Laravel Personal Access Client]:
 > personal1

Personal access client created successfully.
Client ID: 1
Client Secret: jl46yLKUHxE5J3L4ypjhT60YWCG6pOkNl0BPdz5b

Tokenリクエスト

Tokenの取得は/oauth/tokenにアクセスすればよいのですが、Personal Access Tokenの場合、取得するユーザーでログインしている必要があります。ただ画面を用意するのが面倒なので、ここではLaravelのコード内で生成してみます。

LaravelのオフィシャルサイトではVue経由での取得が紹介されています。

とりあえずルート(api.php)に書きを追加します。

Route::get('/personal', function(){
    $user = App\User::find(1);
    $token = $user->createToken('token_for_user1')->accessToken;
    return response()->json(['token' => $token]);
});

特定のuserを取得して、userからcreateToken()を実行しています(トレイトの追加で拡張されている)。

Tokenレスポンス

http://localhost:8000/personal にアクセスしてみます。と、以下のコードが生成されます。
別にjson形式にする必要はないです。

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjM5ZmVhZThjMTUyOGI1MGM3YjI2ZjEzN2Q4MWU1ZGFjMWJlOWFkZjBkYjBjMDg3ZWY2Njk5YmY5MjRlNjE0ZjZlOWJhZGZiOGUyMzMyNmM4In0.eyJhdWQiOiIxIiwianRpIjoiMzlmZWFlOGMxNTI4YjUwYzdiMjZmMTM3ZDgxZTVkYWMxYmU5YWRmMGRiMGMwODdlZjY2OTliZjkyNGU2MTRmNmU5YmFkZmI4ZTIzMzI2YzgiLCJpYXQiOjE1Mzg4MjQ1MzEsIm5iZiI6MTUzODgyNDUzMSwiZXhwIjoxNTcwMzYwNTMxLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.5vK0YJWvM_LZjFsHaUWdc9V0Xms0YEr1lxUotaL_A_B5LWK9HI9pJWydgGk_OfWwxGz8qj57nQGUKiVlmzvj2TfcK-qeUyb04G0cLlLNK_ncyML6pKQRd7t_Y2KSnW-N0xQh_18zOnO9DXP0CPapaKVLiR4tsPlkzG3i3sTQzXarJranW-aSsR5z56obAvSaMQ85lkS1mlbxkdeGpK8CxCJ6ikikqNk2w-x96_AcDt2wo8xFNTOc8I8DLb_geeK6_2KcibQ7WKZOSZnIQj1oeQKginhJUQJ_mozxuPUT4jOW_VZWfzSRdan8TV5DrfoCWaTN00ES1Ebf3O96HigM5aJMo6_PiWinbXwheQXmeCdB949LZpmTEhkWsEytvuDoMAOUCInMtSkr-YQRaKdVKAZzqqiDxH90lLWBjwQjx1Du2KsAhIE3FFcPFC1Eo279jnC8x_MuLodYicmo7FRVvZmyni1T6_6Dg3L-PFFIWR1lhX261gwHEUPzDDJ1eBRh1IPVkTT7_50OveUD2YZwCo5QC7KnlxrahEdKcskB6j7FgjarT-EMY03Osn7LWZKPqLEuoR0PUknHXHG7IVs0j_cjgaKV8vgK05LLo8Lyz1VjWX65U2BUXKJnOCzI90smJXSk5SihJ2TMftRpZ3eKECN4gJXD9oUXi-1axE6bxEI"}

APIリクエスト

取得したTokenを利用して認証付きのAPIにリクエストしてみます。
標準で/api/user という認証済みユーザーの情報を取得するAPIがあるのでそれをコールしてみます。

認証を通すためにはAuthorization: Bearer {token}をヘッダに添付して送ります。

なお、LaravelにAPIからのリクエストであることを認識させるためAccept: application/jsonも添付しています。

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjM5ZmVhZThjMTUyOGI1MGM3YjI2ZjEzN2Q4MWU1ZGFjMWJlOWFkZjBkYjBjMDg3ZWY2Njk5YmY5MjRlNjE0ZjZlOWJhZGZiOGUyMzMyNmM4In0.eyJhdWQiOiIxIiwianRpIjoiMzlmZWFlOGMxNTI4YjUwYzdiMjZmMTM3ZDgxZTVkYWMxYmU5YWRmMGRiMGMwODdlZjY2OTliZjkyNGU2MTRmNmU5YmFkZmI4ZTIzMzI2YzgiLCJpYXQiOjE1Mzg4MjQ1MzEsIm5iZiI6MTUzODgyNDUzMSwiZXhwIjoxNTcwMzYwNTMxLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.5vK0YJWvM_LZjFsHaUWdc9V0Xms0YEr1lxUotaL_A_B5LWK9HI9pJWydgGk_OfWwxGz8qj57nQGUKiVlmzvj2TfcK-qeUyb04G0cLlLNK_ncyML6pKQRd7t_Y2KSnW-N0xQh_18zOnO9DXP0CPapaKVLiR4tsPlkzG3i3sTQzXarJranW-aSsR5z56obAvSaMQ85lkS1mlbxkdeGpK8CxCJ6ikikqNk2w-x96_AcDt2wo8xFNTOc8I8DLb_geeK6_2KcibQ7WKZOSZnIQj1oeQKginhJUQJ_mozxuPUT4jOW_VZWfzSRdan8TV5DrfoCWaTN00ES1Ebf3O96HigM5aJMo6_PiWinbXwheQXmeCdB949LZpmTEhkWsEytvuDoMAOUCInMtSkr-YQRaKdVKAZzqqiDxH90lLWBjwQjx1Du2KsAhIE3FFcPFC1Eo279jnC8x_MuLodYicmo7FRVvZmyni1T6_6Dg3L-PFFIWR1lhX261gwHEUPzDDJ1eBRh1IPVkTT7_50OveUD2YZwCo5QC7KnlxrahEdKcskB6j7FgjarT-EMY03Osn7LWZKPqLEuoR0PUknHXHG7IVs0j_cjgaKV8vgK05LLo8Lyz1VjWX65U2BUXKJnOCzI90smJXSk5SihJ2TMftRpZ3eKECN4gJXD9oUXi-1axE6bxEI' http://localhost:8000/api/user

リクエストにはcurlとかよりPostmanとかを使うほうが可読性、操作性が良いかもしません。

APIレスポンス

無事取得できたようです。

{"id":1,"name":"user1","email":"user1@test.com","email_verified_at":null,"created_at":"2018-10-06 11:10:52","updated_at":"2018-10-06 11:10:52"}

エラーの確認

少しTokenを編集してリクエストすると認証エラーになりました。

{"message":"Unauthenticated."}

このメッセージはapp/Exceptions/Handle.phpでunauthenticated()をオーバーライドすると変更できます。

Password Grant Token

登録済みのIDとPasswordを送信し、認証後Tokenが発行される形式です。
自社やパートナー企業との連携等が利用シナリオでしょうか。

クライアントの生成

--passwordオプションをつけてクライアントを生成します。

php artisan passport:client --password

 What should we name the password grant client? [Laravel Password Grant Client]:
 > password1

Password grant client created successfully.
Client ID: 2
Client Secret: GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53

Tokenリクエスト

Tokenをリクエストしてみます。リクエスト先は/oauth/tokenになります(token要求は常にこのURIが利用されます)。

json形式で送るにはContent-Type: application/jsonが必要です。

curl -X POST -H 'Content-Type: application/json' -d '{"grant_type":"password", "client_id":"2", "client_secret":"GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53", "username":"user1@test.com", "password":"testtest","scope":"*"}' http://localhost:8000/oauth/token

なお、以下のPOSTパラメーを送っています。

{
    "grant_type":"password",
    "client_id":"2",
    "client_secret":"GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53",
    "username":"user1@test.com",
    "password":"testtest",
    "scope":"*"
}

Tokenレスポンス

レスポンスです。tokenはもちろん、refresh_tokenも返ります。有効期限は標準では1年のようです。

{"token_type":"Bearer","expires_in":31536000,"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijc3OTFmN2JkMzM5MjEyZDBiYmZmMmE3M2RmN2YzNGNlYWU2YzY0ZThhOTBiZTdhYTViOWU4YmZkZTU3MmI3YzNlNzhjMzRhOGQ4YTAwZDA4In0.eyJhdWQiOiIyIiwianRpIjoiNzc5MWY3YmQzMzkyMTJkMGJiZmYyYTczZGY3ZjM0Y2VhZTZjNjRlOGE5MGJlN2FhNWI5ZThiZmRlNTcyYjdjM2U3OGMzNGE4ZDhhMDBkMDgiLCJpYXQiOjE1Mzg4MjYxODAsIm5iZiI6MTUzODgyNjE4MCwiZXhwIjoxNTcwMzYyMTgwLCJzdWIiOiIxIiwic2NvcGVzIjpbIioiXX0.4Pp-FB42ITy-GcNuhKJKY2GSBrbw7G2uniDssK0GwIXrY-C3gQexAhiw1eWzy5FOehR8Ygov2-dJMKxb7Ej0fSQMjUweX4SQAp3yYmuB7Vc9eAR4z7xoxoJoTSd854I-kTjqwg5W3-EC1iIM2wINI5iEgBWtobu2pGWQw7bIObYr583FWmj0Khx0l8knEXK-7n_oJY7eramrm9hX1KOigeVD1wisjTQKXCAEAMh54ChMh5AWc-4bA5qIiVc-MSgkjTKThLsklwyB8Y9Cj-69PoSMf2bE0YFgH7ODdjEogMWylIDvr79PACXKtevoSg63ANMKWxyIIq6XGnOqwHGqf-w-ModeRkFRb7LlHNF0CED2sEmDTEeTQjKodfzsuc2eN1zXEtDuhLXp0UGgBy2o5h93LNEnzxbsQ4vb8CaN_-tajBg8nkUh8XkAZyH6U9VvEFSoJ8NmqT8OzCZBvBxlxYhrzinh8H7X1UcBd0sM0n8jOPRHziqlQjYoLiVX2uf_zoPJF0UjU-4_lIiOzsAT19obObb8zZZElf2Yp1ySIf336MHalrzIrv3jEA6FHLb1T3db3Tx_hQGUVpUTnAsnbml-N0-m0ZrL9_Ncko62aWRB0ImbAAJr3JWWwHFG54JyG-1hevczRPA3c3_H6EMARyEDkWG2yH0hTNkvhbRgcS8","refresh_token":"def50200f03c31e644726560d410cf9e016a895b358b18101ed0e00f3d46b76148bb3bcd97d4f6dec1f3d6c79269dc70b09a02713509800222703dbd42375795710812fc9222fcb355450d67d399627a462ea5a52dc75b8cc962e455111ad3709f99f040d9425ad0e800ccafc98c672af8a4ae9717b4e4880e8b8ca68799605c0197a624861516c49b6e49aecfddfdca400f5e5d8500d193b6ee36f14a42c9c004c52d87a70160815927173ebb898b521d3309e8fc6bf8b38cb6e96ec0798a16c692f8e6be45924631cd51708ce1f0f8a6710001cb1cb5385d8f0b3ed02ca60f1cf1ea2031f89947571cd51ba4d4a60973169f86b8d6c2c9a8e814412d331d81d05424f45879ff926dda4ef9af60432ae21418e118aa6dd26aab7f3deabf52fe959cf9ee0df66a0fff8e73ec7f7f3089d1dbd9da5e675d15c576b56135e74c36f0492c3a52664371966ca4c1d01643dc115a67424054862f59ecf927bef5465c50f96fd3"}

APIリクエスト

受け取ったtokenを利用してAPIにリクエストしてみます。

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6Ijc3OTFmN2JkMzM5MjEyZDBiYmZmMmE3M2RmN2YzNGNlYWU2YzY0ZThhOTBiZTdhYTViOWU4YmZkZTU3MmI3YzNlNzhjMzRhOGQ4YTAwZDA4In0.eyJhdWQiOiIyIiwianRpIjoiNzc5MWY3YmQzMzkyMTJkMGJiZmYyYTczZGY3ZjM0Y2VhZTZjNjRlOGE5MGJlN2FhNWI5ZThiZmRlNTcyYjdjM2U3OGMzNGE4ZDhhMDBkMDgiLCJpYXQiOjE1Mzg4MjYxODAsIm5iZiI6MTUzODgyNjE4MCwiZXhwIjoxNTcwMzYyMTgwLCJzdWIiOiIxIiwic2NvcGVzIjpbIioiXX0.4Pp-FB42ITy-GcNuhKJKY2GSBrbw7G2uniDssK0GwIXrY-C3gQexAhiw1eWzy5FOehR8Ygov2-dJMKxb7Ej0fSQMjUweX4SQAp3yYmuB7Vc9eAR4z7xoxoJoTSd854I-kTjqwg5W3-EC1iIM2wINI5iEgBWtobu2pGWQw7bIObYr583FWmj0Khx0l8knEXK-7n_oJY7eramrm9hX1KOigeVD1wisjTQKXCAEAMh54ChMh5AWc-4bA5qIiVc-MSgkjTKThLsklwyB8Y9Cj-69PoSMf2bE0YFgH7ODdjEogMWylIDvr79PACXKtevoSg63ANMKWxyIIq6XGnOqwHGqf-w-ModeRkFRb7LlHNF0CED2sEmDTEeTQjKodfzsuc2eN1zXEtDuhLXp0UGgBy2o5h93LNEnzxbsQ4vb8CaN_-tajBg8nkUh8XkAZyH6U9VvEFSoJ8NmqT8OzCZBvBxlxYhrzinh8H7X1UcBd0sM0n8jOPRHziqlQjYoLiVX2uf_zoPJF0UjU-4_lIiOzsAT19obObb8zZZElf2Yp1ySIf336MHalrzIrv3jEA6FHLb1T3db3Tx_hQGUVpUTnAsnbml-N0-m0ZrL9_Ncko62aWRB0ImbAAJr3JWWwHFG54JyG-1hevczRPA3c3_H6EMARyEDkWG2yH0hTNkvhbRgcS8' http://localhost:8000/api/user

APIレスポンス

認証後、ユーザー(自分自身)のデータが取得できています。

{"id":1,"name":"user1","email":"user1@test.com","email_verified_at":null,"created_at":"2018-10-06 11:10:52","updated_at":"2018-10-06 11:10:52"}

Tokenリフレッシュリクエスト

せっかくなのでtokenのリフレッシュも行ってみます。

curl -X POST -H 'Content-Type: application/json' -d '{"grant_type":"refresh_token", "client_id":"2", "client_secret":"GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53", "scope":"*", "refresh_token":"def50200f03c31e644726560d410cf9e016a895b358b18101ed0e00f3d46b76148bb3bcd97d4f6dec1f3d6c79269dc70b09a02713509800222703dbd42375795710812fc9222fcb355450d67d399627a462ea5a52dc75b8cc962e455111ad3709f99f040d9425ad0e800ccafc98c672af8a4ae9717b4e4880e8b8ca68799605c0197a624861516c49b6e49aecfddfdca400f5e5d8500d193b6ee36f14a42c9c004c52d87a70160815927173ebb898b521d3309e8fc6bf8b38cb6e96ec0798a16c692f8e6be45924631cd51708ce1f0f8a6710001cb1cb5385d8f0b3ed02ca60f1cf1ea2031f89947571cd51ba4d4a60973169f86b8d6c2c9a8e814412d331d81d05424f45879ff926dda4ef9af60432ae21418e118aa6dd26aab7f3deabf52fe959cf9ee0df66a0fff8e73ec7f7f3089d1dbd9da5e675d15c576b56135e74c36f0492c3a52664371966ca4c1d01643dc115a67424054862f59ecf927bef5465c50f96fd3"}' http://localhost:8000/oauth/token

送信パラメータは以下の通り。

{
    "grant_type":"refresh_token",
    "client_id":"2",
    "client_secret":"GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53",
    "scope":"*",
    "refresh_token":"refresh_token..."
}

Tokenリフレッシュレスポンス

再度、tokenとrefresh_tokenが発行されます。
なお、refresh_tokenを発行すると、前のtokenは利用できなくなりますので注意が必要です。

{"token_type":"Bearer","expires_in":31536000,"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjVmM2JiZDJjNzU4MTkwNDcyOGI3YzMzMTg2NjQ1NTQ0MjQ2ZjZmYmY1ZWY0M2EwYjJhY2NjODBmZjkzY2Q5MDFjNjY1ZjhiNTJhMDVjZDUzIn0.eyJhdWQiOiIyIiwianRpIjoiNWYzYmJkMmM3NTgxOTA0NzI4YjdjMzMxODY2NDU1NDQyNDZmNmZiZjVlZjQzYTBiMmFjY2M4MGZmOTNjZDkwMWM2NjVmOGI1MmEwNWNkNTMiLCJpYXQiOjE1Mzg4MjY1NTksIm5iZiI6MTUzODgyNjU1OSwiZXhwIjoxNTcwMzYyNTU5LCJzdWIiOiIxIiwic2NvcGVzIjpbIioiXX0.atpqFPqfGMFE2wEcftkQTFdzycwRl0jXcaEyB-WIk2fXMM7r7-wML83YerZ4HnaXHS36Ee1V2ibNJcr3UpyCGQkRVWNahryHFWv0KWrGZX36uuCd1T43WlvrmEsEz5rQVPYMHE6uht1IsgRlpTcPyI7OtvECVOP6XKf5YRB4huH-2T-rI7BkcP0e8WFleNVsLPLRh16l_znAhVABAt_WgWdr6-5ldo3daGXP5wow_t2WskRBXqI5jfVtfqe_nqfR4HVK6MGYU0mPlwSufXBVAoM3ldr8S4Wyln7APRenTcTet4RcfdhjVoZHV_--tJCXr83JN5gcZ1dD7mLaFrgc5A7vRzyFjmCyE-yxDsV90WcYh5EIBK8kmKqvHPkvtbBdW_G0vQzhuyC-SLnph1xlwI2nDuvyb90ofOCQ47S4eyV8xvGm4FDNpscJy00wX5fzQEPQBLQqmq4jZ1bP6swXxLwpO-7BZRj_B5rHs2-klUubQAQvapKF6q0M3MMYQgHda1a8ebF83TSSXZV-ywLM3OWxiXjO2pcBNg6Y3-IwhZX5RUS8R6efvA-g-_Y2rVtbSISCkYb3dP6REX-rtBbfLn1K6h1h122rPKxbwkkVhWQbcRtpex_09TjzBjw1fHkjPIJVACOITN-dFUv2rrQPpQbDW7xLxGYYPVUAcvyE42Q","refresh_token":"def50200921c2a9639d11068423148f7663db2efe53d3e052098ec812b800033c516aecd67d702326019e4a944d83ab8f909d0ea3a034e063c247f99cc3d1321d657ae6c63c869e7227fd99f1c480c93e48e8e95277b95c7c4e0ea60e3ccadce5fad4e70422357e44840e1dc0998c2ec104223f78dfb6e79d3fe095bac2e703d93d87133a1a625ce4d2c5e336ef75b02587bbab5c2711dd31e2cb332c8dd9674b88b3c626cac7de54b9742acffb8402e586f494e56db3bd766c9b0c9147a15fea2c39be48aa88c80c5e6aab2d5db73f12ac9bcbc8d669d77303eeddfe28c355061dd32bd9e531836262a2abc1472fe13b87aca53c77cfe79da45795f8f0cf38f79a5dba635d6b14f82fb3a3ac8400f86c0815545cc34af5ad605928255fc1670a1e88701e81652556bee97408e465198903083c756db9ba9358dc1c26ffefd2039fcaab6f0bee20a0d1a6d1abea070491b1927d31e675cdc6a7580da578128ea6d803125"}

Client Credentials Grant Token

マシン間の通信に適していると書いてありますね。バッチ処理でAPI連携する場合などでしょうか。

クライアントの生成

専用のクライアント生成コマンドは無いようです。
ここでは上記のPassword Grant Tokenで生成したクライアント(client_id = 2)を利用してみます。

これでいいのかが正直謎。

ミドルウエアの追加

Client Credentials Grant Tokenを利用するにはミドルウエアの追加が必要なようです。

app/Http/Kernel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;
+use Laravel\Passport\Http\Middleware\CheckClientCredentials;

class Kernel extends HttpKernel
{
    protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+       'client' => CheckClientCredentials::class,
    ];
}

ルート(ミドルウエア付き)の設定

ミドルウエアが適用されたルートを用意してみます。
なお、Client Credentials Grant Tokenでは、個人の特定はできない(ユーザー認証はしない)のでログイン状態を前提としたデータの取得はできないようです。

それを検証するために2つのルートを用意しました。
1つは、今まで通り、/api/userと同じ機能(client1)。もう1つは個人情報は取得しないが、認証?は必要なページ(client2)。

なお、auth:apiと併用はできないようです(認証エラーとなる)。

routes/api.php
Route::middleware('client')->get('/client1', function (Request $request) {
    return $request->user();
});

Route::middleware('client')->get('/client2', function (Request $request) {
    return response('auth contents');
});

Tokenリクエスト

では、まずTokenをリクエストしてみます。

curl -X POST -H 'Content-Type: application/json' -d '{"grant_type":"client_credentials", "client_id":"2", "client_secret":"GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53", "scope":"*"}' http://localhost:8000/oauth/token

送信パラメータは以下の通り。

{
    "grant_type":"client_credentials",
    "client_id":"2",
    "client_secret":"GVzzSWmP5nsmbOMQGvHnvAoJqXHaNeDPC6SH3I53",
    "scope":"*"
}

Tokenレスポンス

access_tokenは返りますが、refresh_tokenはありません。

{"token_type":"Bearer","expires_in":31536000,"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImUxYTBmMDg4YzE5NTRlMjJkYjNkNDA0NzUzYjM1OGI4NWQwMTRmNjNhYTQ2OTBjZDNjNzlmYTQxNTVlMDYzYWRkNGFiZWVmNjAwNmEyNzdlIn0.eyJhdWQiOiIyIiwianRpIjoiZTFhMGYwODhjMTk1NGUyMmRiM2Q0MDQ3NTNiMzU4Yjg1ZDAxNGY2M2FhNDY5MGNkM2M3OWZhNDE1NWUwNjNhZGQ0YWJlZWY2MDA2YTI3N2UiLCJpYXQiOjE1Mzg4Mjc2MjcsIm5iZiI6MTUzODgyNzYyNywiZXhwIjoxNTcwMzYzNjI3LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.ennDYPoktYtZMpbvg80wyxt71vaX1ziBHOgPv3d0i8XoOVjeNGzWi9s2GcZ7Bhk99owBDLGzl1z-EOM5ihcUT1aRmjmOAJTOptQW5rGV000cetGKDGNig8xJwTEMvaVCzNWEZqi0bDxWG-PBDzR7MQjNtPvDxc6nP3pmUhJby2V7PLJVO6_ZYPRXPwOclOV9E6YNWpG9s77n44p2XPChGnG2fh7l5sjqLu66zshvmxRf9vF_5AkDoopWFNMQiPqGsWhJlc5KjU_Wuzdamr9uyaI4Hv-WpdG5aV8dc2pzaLK1Z5UvxXdD3LvQAvvqk4MY14zveUW_e-92VFMuSpFQ-bJs22LCo2-axDfLbW21e7seVIOqrZx5pquKcbja3iW8ca6_P0KTWATXZBsFRJo6DFe2gI4eNkYQyBAlf96KVxUgEDD5ADr1UzDqJ792eVrTkPyELkbPGRKxEeziTpJgXxBypRyDCySXLpOSQmMpyP430cvivcsFj5pwaF0xGlOZSHBWa9d1W5FriWzMBsmnAvwllDmD_EWvDh7wK_ccAthb7eNjGk67Ag7eWDctmNuI02Xh2OwhsRp9foUAbxrNOpnZQEjvouw-MmdpmpvYAGijg6keMZiwyMtvIDqqqG12y4_8n5NcslpdhKn1oC8D96h804ae-kRCGOieDEf4sP8"}

APIリクエスト1

まず、/api/client1にリクエストしてみます。

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImUxYTBmMDg4YzE5NTRlMjJkYjNkNDA0NzUzYjM1OGI4NWQwMTRmNjNhYTQ2OTBjZDNjNzlmYTQxNTVlMDYzYWRkNGFiZWVmNjAwNmEyNzdlIn0.eyJhdWQiOiIyIiwianRpIjoiZTFhMGYwODhjMTk1NGUyMmRiM2Q0MDQ3NTNiMzU4Yjg1ZDAxNGY2M2FhNDY5MGNkM2M3OWZhNDE1NWUwNjNhZGQ0YWJlZWY2MDA2YTI3N2UiLCJpYXQiOjE1Mzg4Mjc2MjcsIm5iZiI6MTUzODgyNzYyNywiZXhwIjoxNTcwMzYzNjI3LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.ennDYPoktYtZMpbvg80wyxt71vaX1ziBHOgPv3d0i8XoOVjeNGzWi9s2GcZ7Bhk99owBDLGzl1z-EOM5ihcUT1aRmjmOAJTOptQW5rGV000cetGKDGNig8xJwTEMvaVCzNWEZqi0bDxWG-PBDzR7MQjNtPvDxc6nP3pmUhJby2V7PLJVO6_ZYPRXPwOclOV9E6YNWpG9s77n44p2XPChGnG2fh7l5sjqLu66zshvmxRf9vF_5AkDoopWFNMQiPqGsWhJlc5KjU_Wuzdamr9uyaI4Hv-WpdG5aV8dc2pzaLK1Z5UvxXdD3LvQAvvqk4MY14zveUW_e-92VFMuSpFQ-bJs22LCo2-axDfLbW21e7seVIOqrZx5pquKcbja3iW8ca6_P0KTWATXZBsFRJo6DFe2gI4eNkYQyBAlf96KVxUgEDD5ADr1UzDqJ792eVrTkPyELkbPGRKxEeziTpJgXxBypRyDCySXLpOSQmMpyP430cvivcsFj5pwaF0xGlOZSHBWa9d1W5FriWzMBsmnAvwllDmD_EWvDh7wK_ccAthb7eNjGk67Ag7eWDctmNuI02Xh2OwhsRp9foUAbxrNOpnZQEjvouw-MmdpmpvYAGijg6keMZiwyMtvIDqqqG12y4_8n5NcslpdhKn1oC8D96h804ae-kRCGOieDEf4sP8' http://localhost:8000/api/client1

APIレスポンス1

該当するユーザーがいないため何も戻りません(が、認証エラーがでるわけでもありません)。

(何も返らない)

APIリクエスト2

次に、/api/client2にリクエストを投げてみます。

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImUxYTBmMDg4YzE5NTRlMjJkYjNkNDA0NzUzYjM1OGI4NWQwMTRmNjNhYTQ2OTBjZDNjNzlmYTQxNTVlMDYzYWRkNGFiZWVmNjAwNmEyNzdlIn0.eyJhdWQiOiIyIiwianRpIjoiZTFhMGYwODhjMTk1NGUyMmRiM2Q0MDQ3NTNiMzU4Yjg1ZDAxNGY2M2FhNDY5MGNkM2M3OWZhNDE1NWUwNjNhZGQ0YWJlZWY2MDA2YTI3N2UiLCJpYXQiOjE1Mzg4Mjc2MjcsIm5iZiI6MTUzODgyNzYyNywiZXhwIjoxNTcwMzYzNjI3LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.ennDYPoktYtZMpbvg80wyxt71vaX1ziBHOgPv3d0i8XoOVjeNGzWi9s2GcZ7Bhk99owBDLGzl1z-EOM5ihcUT1aRmjmOAJTOptQW5rGV000cetGKDGNig8xJwTEMvaVCzNWEZqi0bDxWG-PBDzR7MQjNtPvDxc6nP3pmUhJby2V7PLJVO6_ZYPRXPwOclOV9E6YNWpG9s77n44p2XPChGnG2fh7l5sjqLu66zshvmxRf9vF_5AkDoopWFNMQiPqGsWhJlc5KjU_Wuzdamr9uyaI4Hv-WpdG5aV8dc2pzaLK1Z5UvxXdD3LvQAvvqk4MY14zveUW_e-92VFMuSpFQ-bJs22LCo2-axDfLbW21e7seVIOqrZx5pquKcbja3iW8ca6_P0KTWATXZBsFRJo6DFe2gI4eNkYQyBAlf96KVxUgEDD5ADr1UzDqJ792eVrTkPyELkbPGRKxEeziTpJgXxBypRyDCySXLpOSQmMpyP430cvivcsFj5pwaF0xGlOZSHBWa9d1W5FriWzMBsmnAvwllDmD_EWvDh7wK_ccAthb7eNjGk67Ag7eWDctmNuI02Xh2OwhsRp9foUAbxrNOpnZQEjvouw-MmdpmpvYAGijg6keMZiwyMtvIDqqqG12y4_8n5NcslpdhKn1oC8D96h804ae-kRCGOieDEf4sP8' http://localhost:8000/api/client2

APIレスポンス2

ミドルウエアを通過して、コンテンツが表示されます。

auth contents

Auth Code

SNS認証では一番よく見るパターンです。

クライアントの生成

まずはクライアント生成。どのuser IDに割り当てるか聞いてきます。

php artisan passport:client

 Which user ID should the client be assigned to?:
 > 1

 What should we name the client?:
 > auth_code1

 Where should we redirect the request after authorization? [http://localhost/auth/callback]:
 > http://localhost/callback.php

New client created successfully.
Client ID: 3
Client secret: qhP34Alx3MGAgEteQ8VzXnk977wWvuOceNKZ6pS6

[xxxでログイン」に相当するコンテンツの作成(通常は外部サービス)

この機能は通常、(Laraveで作ったサービスと)連携する外部サービスが起点になります。
[xxxxでログイン」という感じのやつです。個々ではそのボタンがクリックされた際を想定した動きを検証します。

必要コンポーネント(Guzzle)のインストール

テストするのにGuzzle使うのでインストールします。

composer require guzzlehttp/guzzle

下記が[xxxxでログイン]ボタンを押したときの動作です。

[facebookでログイン」とかのイメージ。

簡易コード

いきなりリダイレクトするだけ。本来はJSとかでボタンクリックイベントとかで実行することになるでしょう。

oauth.php
<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client as Client;

$guzzle = new Client;

$query = http_build_query([
    'client_id' => '3',
    'redirect_uri' => 'http://localhost/callback.php',
    'response_type' => 'code',
    'scope' => '',
]);

header('Location: http://localhost:8000/oauth/authorize?'.$query);

確認画面(プロバイダ側:Laravel自動生成)

Laravelが用意する標準の画面が出ます。

この画面を出すためには先にcallbackを用意しておかないとエラーが出るかも。

auth.png

Callback先

Authorizeボタンをクリックすると、指定したcallback先に転送され、最終的にaccess_tokenが表示されます。

よく見ると、一度codeを受け取り、さらにそのcodeを利用してaccess_tokenをリクエストしているのがわかります。これがAuth Codeと呼ばれる所以です。

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client as Client;

$http = new Client;

if($_GET['code']){

    $response = $http->post('http://localhost:8000/oauth/token',[
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => '3',
            'client_secret' => 'qhP34Alx3MGAgEteQ8VzXnk977wWvuOceNKZ6pS6',
            'redirect_uri' => 'http://localhost/callback.php',
            'code' => $_GET['code'],
        ],
    ]);

    $res = json_decode((string)$response->getBody(), true);
    // var_dump($res);
    echo $res['access_token'];
}

下記のようなtoken表示されます。

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjJlNWYzOTExMTcyZmM5MTkxOTZhZTI1MGNmY2ZkOGNhNTExNzFmN2I1Y2E5M2I0OGE0ODFiNDQzOTlhNDAxNTBmNjM4OWJhNjFjMDBlYTY5In0.eyJhdWQiOiIzIiwianRpIjoiMmU1ZjM5MTExNzJmYzkxOTE5NmFlMjUwY2ZjZmQ4Y2E1MTE3MWY3YjVjYTkzYjQ4YTQ4MWI0NDM5OWE0MDE1MGY2Mzg5YmE2MWMwMGVhNjkiLCJpYXQiOjE1Mzg4Mjg4ODAsIm5iZiI6MTUzODgyODg4MCwiZXhwIjoxNTcwMzY0ODgwLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.ROGkKk_BRTCuW2pUrq7PegzNnrF1Bo3a6Q0-v3T_9s3AJ33ZtZWyuD6QSxVJpPFWxiRttkno-5kFiNk0Ne8okg3XUwA1WQqJwZMlXjn1A2LmYO2NZ-KihgfjXKkncYwxPi3VsE84chIPRfOFokpy5vcVMtV7ds7jkl3zThTks5AqTPDNwCvMK95cYuFmtUfY8f__FRlLvEU2qaTbHVt7UJbe-l_P1LZEhfkZDQcPDkddkGn0W_5Zwgk5VqEEJezv1W07Yrq7BiWGzqO227XWgbwwZT4S6BX2l9Yka8aQGdqUkW-zj3Eeq07gLyRTDdzRd2tICZGn6_sXoChXeT_uu6_f3sWSiS8nn1StMW_4zEfBW2lbb8H1VQzS4Y5SMUqwCnSvZ2OV1-nG_e0KPd9HJeCz9q5-Rm6kcm2nWaILPIReejfra65DyeLr5po8UPVRwOvB75x07DwJnhCg7Watu2r7QJJupzDyYrf6NmHpDLS7LPqvIgGgQ4W_uv1v2_k8bD1o03phj_yF5_p6M9GUnZWRDTKqGke4kGUh57HrF7kgMikqgm0MjrA2nIPlcdcZRnt_siSLs9_NbWMQF6eGtp6LUiiWlAn7ePkcHcP0mn9D7VOYFdINk-kEf9EaXZXNm8foCnnEfcpnkFiPdFRGap9h3x7suzzo8rAqe14uYcI

APIリクエスト

取得したaccess_tokenを利用してAPIをコールしてみます。

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjJlNWYzOTExMTcyZmM5MTkxOTZhZTI1MGNmY2ZkOGNhNTExNzFmN2I1Y2E5M2I0OGE0ODFiNDQzOTlhNDAxNTBmNjM4OWJhNjFjMDBlYTY5In0.eyJhdWQiOiIzIiwianRpIjoiMmU1ZjM5MTExNzJmYzkxOTE5NmFlMjUwY2ZjZmQ4Y2E1MTE3MWY3YjVjYTkzYjQ4YTQ4MWI0NDM5OWE0MDE1MGY2Mzg5YmE2MWMwMGVhNjkiLCJpYXQiOjE1Mzg4Mjg4ODAsIm5iZiI6MTUzODgyODg4MCwiZXhwIjoxNTcwMzY0ODgwLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.ROGkKk_BRTCuW2pUrq7PegzNnrF1Bo3a6Q0-v3T_9s3AJ33ZtZWyuD6QSxVJpPFWxiRttkno-5kFiNk0Ne8okg3XUwA1WQqJwZMlXjn1A2LmYO2NZ-KihgfjXKkncYwxPi3VsE84chIPRfOFokpy5vcVMtV7ds7jkl3zThTks5AqTPDNwCvMK95cYuFmtUfY8f__FRlLvEU2qaTbHVt7UJbe-l_P1LZEhfkZDQcPDkddkGn0W_5Zwgk5VqEEJezv1W07Yrq7BiWGzqO227XWgbwwZT4S6BX2l9Yka8aQGdqUkW-zj3Eeq07gLyRTDdzRd2tICZGn6_sXoChXeT_uu6_f3sWSiS8nn1StMW_4zEfBW2lbb8H1VQzS4Y5SMUqwCnSvZ2OV1-nG_e0KPd9HJeCz9q5-Rm6kcm2nWaILPIReejfra65DyeLr5po8UPVRwOvB75x07DwJnhCg7Watu2r7QJJupzDyYrf6NmHpDLS7LPqvIgGgQ4W_uv1v2_k8bD1o03phj_yF5_p6M9GUnZWRDTKqGke4kGUh57HrF7kgMikqgm0MjrA2nIPlcdcZRnt_siSLs9_NbWMQF6eGtp6LUiiWlAn7ePkcHcP0mn9D7VOYFdINk-kEf9EaXZXNm8foCnnEfcpnkFiPdFRGap9h3x7suzzo8rAqe14uYcI' http://localhost:8000/api/user

APIレスポンス

ユーザー情報が取得できています。

{"id":1,"name":"user1","email":"user1@test.com","email_verified_at":null,"created_at":"2018-10-06 11:10:52","updated_at":"2018-10-06 11:10:52"}

Implicit Grant Token

いよいよ最後のImplicit Grant Tokenです。

準備

Implicit Grant Tokenを利用するには、AuthServiceProvider.phpにその旨、加筆します。

AuthServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
+       Passport::enableImplicitGrant();
    }
}

クライアントの生成

redirect_uriを指定する必要があるため、Auth Code用のクライアントを流用します(する必要があります)。

Tokenリクエスト

implicit.php
<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client as Client;

$guzzle = new Client;

$query = http_build_query([
    'client_id' => '3',
    'redirect_uri' => 'http://localhost/callback.php',
    'response_type' => 'token',
    'scope' => '',
]);

header('Location: http://localhost:8000/oauth/authorize?'.$query);

Tokenレスポンス

上記のコードを実行すると、リダイレクトURL中にフラグメントとしてtokenが返されるようです。

http://localhost/callback.php#access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImQ0MzI3MWFmNjk3ZmJhMDIxM2U2NjUzZWU0MDgyNmU5NTlhYTRlMmYwM2QyNzFjZWNmODEwYzk0NjQxMjY2OGJmN2RmM2UwZTA4MGE3YzFiIn0.eyJhdWQiOiIzIiwianRpIjoiZDQzMjcxYWY2OTdmYmEwMjEzZTY2NTNlZTQwODI2ZTk1OWFhNGUyZjAzZDI3MWNlY2Y4MTBjOTQ2NDEyNjY4YmY3ZGYzZTBlMDgwYTdjMWIiLCJpYXQiOjE1Mzg4Mjk5MTgsIm5iZiI6MTUzODgyOTkxOCwiZXhwIjoxNTcwMzY1OTE4LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.32FUf_2iF75523QY1uDxG0sv4gWyDW5qbx9rJ1FdYDbbRSPPohyGf3SjSzXsAWNeFSDMuKoLNaeczOMQ_jY3dLd2D2KyU7RD75IM2ZFXFf1w9vzUBHoLv9AMpIuAMuAik7N3LJIyRrGKo1tKxuwNDitEdASQpOtm16io6pn1aUkzuyeJ8C_geugogOOp377WH4wKj4hRwM6GHQilUxcMKVu6XTsb8ZtqWXXv_LGQvxgcLH1koMAKisi5qn8lEeMI3AX6K19mWe2UruBjUFmyfsBKqoLNWNeOKD2O-IzCnPZkfp1eU88lQlDCln9RdM4du4C0lILwEKRATxwlxT-EEny7VP4pckbUX_hBzooD5S0imwHMQ89zE4t5T7Cxhi8LF2Vm1mvsNaOmbAzo4SUtgWa3J2-lcDfNC3m6hSYKEUkKNcIFZN_CWRQsPJmNgwU202OlmDhiJlP-Bk9j4X_6i6vq_l3x54KFJ8M6s6yPqLEUBXilouotS82r_BKsM1f5OMQFUAaMiFPHvp45R0nfsTnJsKHM6yhkprSWytP63SUGUtbDewUeMrjdXfrxCyhqGOO-bo8HcyPOVFF9hHqUzgoOELAKoJwpJvOmdueVTuQNqFXtak_H86K3ea02CJ9QXc_JHyxQRWPUK01RLY2yR-vV3677AeTCTNE2EJh5Aqk&token_type=Bearer&expires_in=31536000

なお、フラグメント値はサーバ側では取得できないため、JavaScriptで取得します。

callback.php
<!doctype html>
<html>
<body>
Implicit Callback.
<script>
(function(){
    var hash = window.location.hash;
    alert(hash);
})();
</script>
</body>
</html>

なお、Client Credentials Grant Token同様、Implicit Grant Tokenでも個人レベルでの認証は行われません。
そのため、/api/client1は値を返しません。

APIリクエスト1

/api/user相当。

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImQ0MzI3MWFmNjk3ZmJhMDIxM2U2NjUzZWU0MDgyNmU5NTlhYTRlMmYwM2QyNzFjZWNmODEwYzk0NjQxMjY2OGJmN2RmM2UwZTA4MGE3YzFiIn0.eyJhdWQiOiIzIiwianRpIjoiZDQzMjcxYWY2OTdmYmEwMjEzZTY2NTNlZTQwODI2ZTk1OWFhNGUyZjAzZDI3MWNlY2Y4MTBjOTQ2NDEyNjY4YmY3ZGYzZTBlMDgwYTdjMWIiLCJpYXQiOjE1Mzg4Mjk5MTgsIm5iZiI6MTUzODgyOTkxOCwiZXhwIjoxNTcwMzY1OTE4LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.32FUf_2iF75523QY1uDxG0sv4gWyDW5qbx9rJ1FdYDbbRSPPohyGf3SjSzXsAWNeFSDMuKoLNaeczOMQ_jY3dLd2D2KyU7RD75IM2ZFXFf1w9vzUBHoLv9AMpIuAMuAik7N3LJIyRrGKo1tKxuwNDitEdASQpOtm16io6pn1aUkzuyeJ8C_geugogOOp377WH4wKj4hRwM6GHQilUxcMKVu6XTsb8ZtqWXXv_LGQvxgcLH1koMAKisi5qn8lEeMI3AX6K19mWe2UruBjUFmyfsBKqoLNWNeOKD2O-IzCnPZkfp1eU88lQlDCln9RdM4du4C0lILwEKRATxwlxT-EEny7VP4pckbUX_hBzooD5S0imwHMQ89zE4t5T7Cxhi8LF2Vm1mvsNaOmbAzo4SUtgWa3J2-lcDfNC3m6hSYKEUkKNcIFZN_CWRQsPJmNgwU202OlmDhiJlP-Bk9j4X_6i6vq_l3x54KFJ8M6s6yPqLEUBXilouotS82r_BKsM1f5OMQFUAaMiFPHvp45R0nfsTnJsKHM6yhkprSWytP63SUGUtbDewUeMrjdXfrxCyhqGOO-bo8HcyPOVFF9hHqUzgoOELAKoJwpJvOmdueVTuQNqFXtak_H86K3ea02CJ9QXc_JHyxQRWPUK01RLY2yR-vV3677AeTCTNE2EJh5Aqk' http://localhost:8000/api/client1

APIレスポンス1

何も返ってきません。が、認証エラーにもなりません。

(何も返らない)

APIリクエスト2

curl -H 'Accept: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImQ0MzI3MWFmNjk3ZmJhMDIxM2U2NjUzZWU0MDgyNmU5NTlhYTRlMmYwM2QyNzFjZWNmODEwYzk0NjQxMjY2OGJmN2RmM2UwZTA4MGE3YzFiIn0.eyJhdWQiOiIzIiwianRpIjoiZDQzMjcxYWY2OTdmYmEwMjEzZTY2NTNlZTQwODI2ZTk1OWFhNGUyZjAzZDI3MWNlY2Y4MTBjOTQ2NDEyNjY4YmY3ZGYzZTBlMDgwYTdjMWIiLCJpYXQiOjE1Mzg4Mjk5MTgsIm5iZiI6MTUzODgyOTkxOCwiZXhwIjoxNTcwMzY1OTE4LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.32FUf_2iF75523QY1uDxG0sv4gWyDW5qbx9rJ1FdYDbbRSPPohyGf3SjSzXsAWNeFSDMuKoLNaeczOMQ_jY3dLd2D2KyU7RD75IM2ZFXFf1w9vzUBHoLv9AMpIuAMuAik7N3LJIyRrGKo1tKxuwNDitEdASQpOtm16io6pn1aUkzuyeJ8C_geugogOOp377WH4wKj4hRwM6GHQilUxcMKVu6XTsb8ZtqWXXv_LGQvxgcLH1koMAKisi5qn8lEeMI3AX6K19mWe2UruBjUFmyfsBKqoLNWNeOKD2O-IzCnPZkfp1eU88lQlDCln9RdM4du4C0lILwEKRATxwlxT-EEny7VP4pckbUX_hBzooD5S0imwHMQ89zE4t5T7Cxhi8LF2Vm1mvsNaOmbAzo4SUtgWa3J2-lcDfNC3m6hSYKEUkKNcIFZN_CWRQsPJmNgwU202OlmDhiJlP-Bk9j4X_6i6vq_l3x54KFJ8M6s6yPqLEUBXilouotS82r_BKsM1f5OMQFUAaMiFPHvp45R0nfsTnJsKHM6yhkprSWytP63SUGUtbDewUeMrjdXfrxCyhqGOO-bo8HcyPOVFF9hHqUzgoOELAKoJwpJvOmdueVTuQNqFXtak_H86K3ea02CJ9QXc_JHyxQRWPUK01RLY2yR-vV3677AeTCTNE2EJh5Aqk' http://localhost:8000/api/client2

APIレスポンス2

認証自体は通過し、コンテンツが表示されます。

auth contents

おまけ

公開するルートを限定する

実際にはPassword Grant Tokenしか利用しない・・・という場合もあります。
AuthServiceProvider.phpの記述を変更することで、必要なルートだけを有効にできるようです。

    public function boot()
    {
        $this->registerPolicies();

        // Passport::routes();
        Passport::routes(function($router){
            $router->forAccessTokens();
            // $router->forAuthorization();
            // $router->forTransientTokens();
        });
    }

継続調査事項

いろいろ謎な仕様があるので継続調査します。

  • Client(ID)とUser(ID)の関係
    • Clientの発行単位(ユーザーに1クライアントか、1つのクライアントで複数ユーザーに対応していいか?)
    • ユーザーひも付き型のクライアントでも他のユーザーからのリクエストでもTokenが変えるけど・・・(再確認)