Laravel 7.x Laravel Passport

イントロダクション

Laravelでは古典的なログインフォームによる認証は、簡単に実行できるようになっています。では、APIに関してはどうでしょうか?通常APIでは、ユーザーの認証にトークンを使用し、リクエスト間のセッション状態は保持されません。Laravelアプリケーションのために、完全なOAuth2サーバの実装を提供するLaravel Passportを使えば、短時間で簡単にAPI認証ができます。Passportは、Andy MillingtonとSimon Hampによりメンテナンスされている、League OAuth2サーバ上に構築しています。

Note: このドキュメントは、皆さんがOAuth2に慣れていることを前提にしています。OAuth2について知らなければ、この先を続けて読む前に、一般的な用語とOAuth2の機能について予習してください。

Passportのアップグレード

Passportの新しいメジャーバーションへアップグレードする場合は、注意深くアップグレードガイドをレビューしてください。

インストール

Composerパッケージマネージャにより、Passportをインストールすることからはじめましょう。

composer require laravel/passport

Passportサービスプロバイダはフレームワークに対し、自身のマイグレーションディレクトリを登録します。そのためにパッケージインストール後、データベースのマイグレーションを実行する必要があります。Passportのマイグレーションは、アプリケーションで必要となる、クライアントとアクセストークンを保存しておくテーブルを作成します。

php artisan migrate

次に、passport:installコマンドを実行します。このコマンドは安全なアクセストークンを生成するのに必要な暗号キーを作成します。さらにアクセストークンを生成するために使用する、「パーソナルアクセス」クライアントと「パスワードグラント」クライアントも作成します。

php artisan passport:install

Tip!! 自動増分整数の代わりに、PassportのClientモデルの主キー値としてUUIDを使用したい場合は、uuidsオプションを使いPassportをインストールしてください。

passport:installコマンドを実行し終えたら、Laravel\Passport\HasApiTokensトレイトをApp\Userモデルへ追加してください。このトレイトは認証済みユーザーのトークンとスコープを調べられるように、モデルへ数個のヘルパメソッドを提供します。

<?php

namespace App;

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

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

次に、AuthServiceProviderbootメソッドから、Passport::routesメソッドを呼び出す必要があります。このメソッドはアクセストークンの発行、アクセストークンの失効、クライアントとパーソナルアクセストークンの管理のルートを登録します。

<?php

namespace App\Providers;

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

class AuthServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションのポリシーのマップ
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * 全認証/認可サービスの登録
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

最後に、config/auth.php設定ファイル中で、ガードのapi認証のdriverオプションをpassportへ変更します。これにより、認証のAPIリクエストが送信された時に、パスポートのTokenGuardを使用するように、アプリケーションへ指示します。

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

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

クライアントUUID

passport:installコマンドは--uuidsオプションを指定して実行できます。このフラグはPassportへClientモデルの主キー値として自動増分整数の代わりにUUIDを使用することを指示します。--uuidsオプションを付けてpassport:installコマンドを実行したら、Passportのデフォルトマイグレーションの無効化に関して追加の指示が与えられます。

php artisan passport:install --uuids

フロントエンド・クイックスタート

Note: パスポートVueコンポーネントを使用するには、Vue JavaScriptフレームワークを使用する必要があります。コンポーネントはBootstrap CSSフレームワークを使用しています。皆さんがこれらのツールを使用しない場合でも、フロントエンド実装の参考として、これらのコンポーネントは役立つでしょう。

パスポートは皆さんのユーザーへ、クライアントとパーソナルアクセストークンを作成するために使用するJSON APIを初めから提供しています。しかし、こうしたAPIに関連するフロントエンドをコーディングするには時間を要します。そこで、Passportは実装例、もしくは実装の開始地点として役立ててもらうため、Vueコンポーネントも用意しています。

Passport Vueコンポーネントをリソース公開するには、vendor:publish Artisanコマンドを使用します。

php artisan vendor:publish --tag=passport-components

リソース公開されたコンポーネントは、resources/js/componentsディレクトリへ設置されます。リソース公開したコンポーネントは、resources/js/app.jsファイルで登録してください。

Vue.component(
    'passport-clients',
    require('./components/passport/Clients.vue').default
);

Vue.component(
    'passport-authorized-clients',
    require('./components/passport/AuthorizedClients.vue').default
);

Vue.component(
    'passport-personal-access-tokens',
    require('./components/passport/PersonalAccessTokens.vue').default
);

Note: バージョン5.7.19以前のLaravelでは、コンポーネントを登録する時にコンソールエラーが出る時に、.defaultを追加します。この変更については、Laravel Mix v4.0.0リリースノートで説明がなされています。

コンポーネントを登録したら、アセットを再コンパイルするためnpm run devを確実に実行してください。アセットの再コンパイルが済んだら、クライアントとパーソナルアクセストークンを作成し始めるために、アプリケーションのテンプレートへコンポーネントを指定しましょう。

<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>

Passportのデプロイ

Passportを実働サーバへ最初にデプロイするとき、passport:keysコマンドを実行する必要があるでしょう。このコマンドは、Passportがアクセストークンを生成するために必要な、暗号化キーを生成するコマンドです。生成されたキーは、通常ソースコントロールには含めません。

php artisan passport:keys

必要があれば、Passportのキーを読み込むパスを定義できます。Passport::loadKeysFromメソッドを使用します。

/**
 * 全認証/認可の登録
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::loadKeysFrom('/secret-keys/oauth');
}

さらに、php artisan vendor:publish --tag=passport-configにより、Passportの設定ファイルをリソース公開することもできます。これにより、環境変数から暗号化キーをロードするためのオプションを提供しています。

PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"

PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"

マイグレーションのカスタマイズ

Passportのデフォルトマイグレーションを使用しない場合、AppServiceProviderregisterメソッドでPassport::ignoreMigrationsメソッドを呼び出す必用があります。php artisan vendor:publish --tag=passport-migrationsにより、デフォルトマイグレーションをエクスポートできます。

設定

クライアントシークレットハッシュ

クライアントのシークレットをデータベース保存時にハッシュ化したい場合は、AppServiceProviderbootメソッドでPassport::hashClientSecretsメソッドを呼び出す必用があります。

Passport::hashClientSecrets();

一度有効にすると、すべてのクライアントのシークレットはクライアント作成時に一度だけ表示されます。データベースには平文のクライアントシークレット値はまったく保存されないため、失われたら回復は不可能です。

トークン持続時間

Passportはデフォルトで、一年間有効な長期間持続するアクセストークンを発行します。トークンの持続時間をもっと短くしたい場合は、tokensExpireInrefreshTokensExpireInpersonalAccessTokensExpireInメソッドを使ってください。これらのメソッドは、AuthServiceProviderbootメソッドから呼び出してください。

/**
 * 全認証/認可の登録
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::tokensExpireIn(now()->addDays(15));

    Passport::refreshTokensExpireIn(now()->addDays(30));

    Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}

Note: Passportデータベーステーブルの expires_atカラムは読み取り専用で、表示目的のみです。トークン発行時にPassportは、署名暗号化されたトークン内へ有効期限情報を格納します。トークンを無効にする必要がある場合は、トークンを取り消す必要があります。

デフォルトモデルのオーバーライド

Passportが内部で使用するモデルは自由に拡張できます。

use Laravel\Passport\Client as PassportClient;

class Client extends PassportClient
{
    // ...
}

それから、Passportクラスを通して、カスタムモデルを指示します。

use App\Models\Passport\AuthCode;
use App\Models\Passport\Client;
use App\Models\Passport\PersonalAccessClient;
use App\Models\Passport\Token;

/**
 * 全認証/認可の登録
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::useTokenModel(Token::class);
    Passport::useClientModel(Client::class);
    Passport::useAuthCodeModel(AuthCode::class);
    Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}

アクセストークンの発行

OAuth2で認可コードを使いこなせるかは、どの程度開発者がOAuth2に慣れているかによります。認可コードを使用する時、クライアントアプリケーションはそのクライアントに対するアクセストークン発行のリクエストが許可されようと、拒絶されようと、あなたのサーバにそのユーザーをリダイレクトします。

クライアント管理

あなたのアプリケーションのAPIと連携する必要のある、アプリケーションを構築しようとしている開発者たちは、最初に「クライアント」を作成することにより、彼らのアプリケーションを登録しなくてはなりません。通常、アプリケーションの名前と、許可のリクエストをユーザーが承認した後に、アプリケーションがリダイレクトされるURLにより、登録情報は構成されます。

passport:clientコマンド

クライアントを作成する一番簡単な方法は、passport:client Artisanコマンドを使うことです。このコマンドは、OAuth2の機能をテストするため、皆さん自身のクライアントを作成する場合に使用できます。clientコマンドを実行すると、Passportはクライアントに関する情報の入力を促し、クライアントIDとシークレットを表示します。

php artisan passport:client

リダイレクトURL

クライアントに対するリダイレクトURLを複数許可したければ、passport:clientコマンドで入力を促された時にURLをカンマで区切り指定します。

http://example.com/callback,http://examplefoo.com/callback

Note: カンマを含んでいるURLは、エンコードしてください。

JSON API

皆さんのアプリのユーザーはclientコマンドを使用できないわけですから、Passportはクライアント作成のJSON APIを提供しています。これにより、クライアントを作成、更新、削除するコントローラをわざわざコードする手間を省略できます。

しかし、ユーザーにクライアントを管理してもらうダッシュボードを提供するために、PassportのJSON APIと皆さんのフロントエンドを結合する必要があります。以降から、クライアントを管理するためのAPIエンドポイントをすべて説明します。エンドポイントへのHTTPリクエスト作成をデモンストレートするため利便性を考慮し、Axiosを使用していきましょう。

JSON APIはwebauthミドルウェアにより保護されています。そのため、みなさん自身のアプリケーションからのみ呼び出せます。外部ソースから呼び出すことはできません。

Tip!! クライアント管理のフロントエンドを自分で実装したくなければ、フロントエンド・クイックスタートを使い、短時間で完全に機能するフロントエンドを用意できます。

GET /oauth/clients

このルートは認証されたユーザーの全クライアントを返します。ユーザーのクライアントの全リストは、主にクライアントを編集、削除する場合に役立ちます。

axios.get('/oauth/clients')
    .then(response => {
        console.log(response.data);
    });

POST /oauth/clients

このルートは新クライアントを作成するために使用します。これには2つのデータが必要です。クライアントの名前(name)と、リダイレクト(redirect)のURLです。redirectのURLは許可のリクエストが承認されるか、拒否された後のユーザーのリダイレクト先です。

クライアントを作成すると、クライアントIDとクライアントシークレットが発行されます。これらの値はあなたのアプリケーションへリクエストし、アクセストークンを取得する時に使用されます。クライアント作成ルートは、新しいクライアントインスタンスを返します。

const data = {
    name: 'Client Name',
    redirect: 'http://example.com/callback'
};

axios.post('/oauth/clients', data)
    .then(response => {
        console.log(response.data);
    })
    .catch (response => {
        // レスポンス上のエラーのリスト
    });

PUT /oauth/clients/{client-id}

このルートはクライアントを更新するために使用します。それには2つのデータが必要です。クライアントのnameredirectのURLです。redirectのURLは許可のリクエストが承認されるか、拒否され後のユーザーのリダイレクト先です。このルートは更新されたクライアントインスタンスを返します。

const data = {
    name: 'New Client Name',
    redirect: 'http://example.com/callback'
};

axios.put('/oauth/clients/' + clientId, data)
    .then(response => {
        console.log(response.data);
    })
    .catch (response => {
        // レスポンス上のエラーのリスト
    });

DELETE /oauth/clients/{client-id}

このルートはクライアントを削除するために使用します。

axios.delete('/oauth/clients/' + clientId)
    .then(response => {
        //
    });

トークンのリクエスト

許可のリダイレクト

クライアントが作成されると、開発者はクライアントIDとシークレットを使用し、あなたのアプリケーションへ許可コードとアクセストークンをリクエストするでしょう。まず、API利用側アプリケーションは以下のように、あなたのアプリケーションの/oauth/authorizeルートへのリダイレクトリクエストを作成する必要があります。

Route::get('/redirect', function (Request $request) {
    $request->session()->put('state', $state = Str::random(40));

    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'code',
        'scope' => '',
        'state' => $state,
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

Tip!! /oauth/authorizeルートは、すでにPassport::routesメソッドが定義づけていることを覚えておいてください。このルートを自分で定義する必要はありません。

リクエストの承認

許可のリクエストを受け取ると、Passportはユーザーがその許可のリクエストを承認するか、拒絶するかのテンプレートを自動的に表示します。ユーザーが許可した場合、API利用側アプリケーションが指定したredirect_uriへリダイレクトします。redirect_uriは、クライアントを作成した時に指定したredirectのURLと一致する必要があります。

許可の承認ページをカスタマイズしたい場合は、vendor:publish Artisanコマンドを使い、Passportのビューを公開することでリソースを用意する必要があります。リソース公開されたビューは、resources/views/vendor/passportへ設置されます。

php artisan vendor:publish --tag=passport-views

ファーストパーティ製クライアントにより認可中のような場合、認可プロンプトをとばしたい場合もあり得ます。Clientモデルを拡張しskipsAuthorizationメソッドを定義することで実現できます。skipsAuthorizationがクライアントは認証済みとしてtrueを返すと、そのユーザーをすぐにredirect_uriへリダイレクトで戻します。

<?php

namespace App\Models\Passport;

use Laravel\Passport\Client as BaseClient;

class Client extends BaseClient
{
    /**
     * クライアントが認可プロンプトを飛ばすべきか決める
     *
     * @return bool
     */
    public function skipsAuthorization()
    {
        return $this->firstParty();
    }
}

許可コードからアクセストークンへの変換

ユーザーが許可リクエストを承認したら、API使用側アプリケーションへリダイレクトされます。使用側は最初に、リダイレクトする前に保存していた値と、stateパラメータを確認する必要があります。stateパラメータが一致したら、使用側はあなたのアプリケーションへアクセストークンをリクエストするためのPOSTリクエストを送信する必要があります。そのリクエストには、ユーザーが許可リクエストを承認した時にあなたのアプリケーションが発行した、許可コードを含める必要があります。この例として、Guzzle HTTPライブラリでPOSTリクエストを作成してみましょう。

Route::get('/callback', function (Request $request) {
    $state = $request->session()->pull('state');

    throw_unless(
        strlen($state) > 0 && $state === $request->state,
        InvalidArgumentException::class
    );

    $http = new GuzzleHttp\Client;

    $response = $http->post('http://your-app.com/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => 'client-id',
            'client_secret' => 'client-secret',
            'redirect_uri' => 'http://example.com/callback',
            'code' => $request->code,
        ],
    ]);

    return json_decode((string) $response->getBody(), true);
});

この/oauth/tokenルートは、access_tokenrefresh_tokenexpires_in属性を含むJSONレスポンスを返します。expires_in属性は、アクセストークンが無効になるまでの秒数を含んでいます。

Tip!! /oauth/authorizeルートと同様に、/oauth/tokenルートはPassport::routesメソッドが定義しています。このルートを自分で定義する必要はありません。デフォルトでこのルートは、ThrottleRequestsミドルウェアの設定を利用し、アクセス回数制限されています。

JSON API

Passportには、承認済みアクセストークンを管理するためのJSON APIも含んでいます。これを独自のフロントエンドと組み合わせ、アクセストークンを管理するダッシュボードをユーザーへ提供できます。便宜上、AxiosをエンドポイントへのHTTPリクエストを生成するデモンストレーションのため使用しています。JSON APIはwebauthミドルウェアにより保護されているため、自身のアプリケーションからのみ呼び出しできます。

GET /oauth/tokens

このルートは、認証されたユーザーが作成した、承認済みアクセストークンをすべて返します。これは主に取り消すトークンを選んでもらうため、ユーザーの全トークンを一覧リスト表示するのに便利です。

axios.get('/oauth/tokens')
    .then(response => {
        console.log(response.data);
    });

DELETE /oauth/tokens/{token-id}

このルートは、認証済みアクセストークンと関連するリフレッシュトークンを取り消すために使います。

axios.delete('/oauth/tokens/' + tokenId);

トークンのリフレッシュ

アプリケーションが短い有効期限のアクセストークンを発行している場合に、ユーザーはアクセストークンを発行する時に提供しているリフレッシュトークンを使用し、アクセストークンをリフレッシュする必要が起きます。以下はGuzzle HTTPライブラリを使用し、トークンをリフレッシュする例です。

$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'refresh_token',
        'refresh_token' => 'the-refresh-token',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'scope' => '',
    ],
]);

return json_decode((string) $response->getBody(), true);

この/oauth/tokenルートは、access_tokenrefresh_tokenexpires_in属性を含むJSONレスポンスを返します。expires_in属性は、アクセストークンが無効になるまでの秒数を含んでいます。

トークンの取り消し

TokenRepositoryrevokeAccessTokenメソッドを利用し、トークンを取り消せます。トークンのリフレッシュトークンの取り消しはRefreshTokenRepositoryrevokeRefreshTokensByAccessTokenIdメソッドを使います。

$tokenRepository = app('Laravel\Passport\TokenRepository');
$refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');

// アクセストークンの取り消し
$tokenRepository->revokeAccessToken($tokenId);

// そのトークンのリフレッシュトークンを全て取り消し
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);

トークンの破棄

トークンが無効、もしくは期限切れになったら、データベースから一掃する必要があります。Passportはこのためのコマンドを用意しています。

# 無効・期限切れのトークンと認可コードを破棄する
php artisan passport:purge

# 無効なトークンと認可コードのみ破棄する
php artisan passport:purge --revoked

# 期限切れのトークンと認可コードのみ破棄する
php artisan passport:purge --expired

スケジュールに従い自動的にトークンを整理するため、コンソールのKernelクラスでスケジュール済みジョブとして設定することも可能です。

/**
 * アプリケーションのコマンドスケジュール定義
 *
 * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
 * @return void
 */
protected function schedule(Schedule $schedule)
{
    $schedule->command('passport:purge')->hourly();
}

PKCEを使った認可コードグラント

"Proof Key for Code Exchange" (PKCE)を使用する認可コードグラントは、シングルページアプリケーションやネイティブアプリケーションが、APIへアクセスするための安全な認証方法です。このグラントはクライアントの秘密コードを十分な機密を保ち保存できないか、もしくは認可コード横取り攻撃の危険を軽減する必要がある場合に、必ず使用すべきです。アクセストークンのために認可コードを交換するときに、クライアントの秘密コードを「コードベリファイヤ(code verifier)」と「コードチャレンジ(code challenge)」のコンピネーションに置き換えます。

クライアント生成

PKCEを使用した認可コードグラントを通じトークンを発行する前に、PKCE可能なクライアントを生成する必要があります。passport:clientコマンドを--publicオプション付きで実行してください。

php artisan passport:client --public

トークンのリクエスト

コードベリファイヤとコードチャレンジ

この認可グラントではクライアント秘密コードが提供されないため、開発者はトークンを要求するためにコードベリファイヤとコードチャレンジのコンビネーションを生成する必要があります。

コードベリファイヤはRFC 7636仕様で定義されている通り、43から128文字の文字、数字、"-"".""_""~"を含んだランダムな文字列の必要があります。

コードチャレンジはURL/ファイルネームセーフな文字をBase64エンコードしたものである必要があります。文字列終端の'='文字を削除し、ラインブレイクやホワイトスペースを含まず、その他はそのままにします。

$encoded = base64_encode(hash('sha256', $code_verifier, true));

$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');

許可のリダイレクト

クライアントが生成できたら、アプリケーションから認可コードとアクセストークンをリクエストするために、クライアントIDと生成したコードベリファイヤ、コードチャレンジを使用します。最初に、認可要求側のアプリケーションは、あなたのアプリケーションの/oauth/authorizeルートへのリダイレクトリクエストを生成する必要があります。

Route::get('/redirect', function (Request $request) {
    $request->session()->put('state', $state = Str::random(40));

    $request->session()->put('code_verifier', $code_verifier = Str::random(128));

    $codeChallenge = strtr(rtrim(
        base64_encode(hash('sha256', $code_verifier, true))
    , '='), '+/', '-_');

    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'code',
        'scope' => '',
        'state' => $state,
        'code_challenge' => $codeChallenge,
        'code_challenge_method' => 'S256',
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

許可コードからアクセストークンへの変換

ユーザーが認可リクエストを承認すると、認可要求側のアプリケーションへリダイレクで戻されます。認可要求側では認可コードグラントの規約に従い、リダイレクトの前に保存しておいた値と、stateパラメータを検証する必要があります。

stateパラメータが一致したら、要求側はアクセストークンをリクエストするために、あなたのアプリケーションへPOSTリクエストを発行する必要があります。そのリクエストは最初に生成したコードベリファイヤと同時に、ユーザーが認可リクエストを承認したときにあなたのアプリケーションが発行した認可コードを持っている必要があります。

Route::get('/callback', function (Request $request) {
    $state = $request->session()->pull('state');

    $codeVerifier = $request->session()->pull('code_verifier');

    throw_unless(
        strlen($state) > 0 && $state === $request->state,
        InvalidArgumentException::class
    );

    $response = (new GuzzleHttp\Client)->post('http://your-app.com/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => 'client-id',
            'redirect_uri' => 'http://example.com/callback',
            'code_verifier' => $codeVerifier,
            'code' => $request->code,
        ],
    ]);

    return json_decode((string) $response->getBody(), true);
});

パスワードグラントのトークン

OAuth2のパスワードグラントはモバイルアプリケーションのような、その他のファーストパーティクライアントへ、メールアドレス/ユーザー名とパスワードを使ったアクセストークンの取得を提供します。これによりOAuth2のAuthorization Codeリダイレクトフローに完全に従うことをユーザーへ要求せずとも、アクセストークンを安全に発行できます。

パスワードグラントクライアントの作成

パスワードグラントにより、あなたのアプリケーションがトークンを発行する前に、パスワードグラントクライアントを作成する必要があります。それには、passport:clientコマンドで--passwordを使用してください。すでにpassport:installコマンドを実行済みの場合、このコマンドを実行する必要はありません。

php artisan passport:client --password

トークンのリクエスト

パスワードグラントクライアントを作成したら、ユーザーのメールアドレスとパスワードを指定し、/oauth/tokenルートへPOSTリクエストを発行することで、アクセストークンをリクエストできます。このルートは、Passport::routesメソッドが登録しているため、自分で定義する必要がないことを覚えておきましょう。リクエストに成功すると、サーバからaccess_tokenrefresh_tokenのJSONレスポンスを受け取ります。

$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => 'taylor@laravel.com',
        'password' => 'my-password',
        'scope' => '',
    ],
]);

return json_decode((string) $response->getBody(), true);

Tip!! アクセストークンはデフォルトで、長期間有効であることを記憶しておきましょう。ただし、必要であれば自由に、アクセストークンの最長持続時間を設定できます。

全スコープの要求

パスワードグラント、またはクライアント認証情報グラントを使用時は、あなたのアプリケーションでサポートする全スコープを許可するトークンを発行したいと考えるかと思います。*スコープをリクエストすれば可能です。*スコープをリクエストすると、そのトークンインスタンスのcanメソッドは、いつもtrueを返します。このスコープはpasswordclient_credentialsグラントを使って発行されたトークのみに割り付けるのが良いでしょう。

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => 'taylor@laravel.com',
        'password' => 'my-password',
        'scope' => '*',
    ],
]);

ユーザープロバイダのカスタマイズ

アプリケーションが認証ユーザープロバイダを複数使用している場合、artisan passport:client --passwordコマンドによりクライアントを制作する時に、--providerオプションによりパスワードグランツクライアントで使用するユーザープロバイダを指定します。指定プロバイダ名はconfig/auth.php設定ファイルで定義した有効なプロバイダと一致している必用があります。次に、[ミドルウェアを使用してルートを保護](#via-middleware)して、ガードの指定したプロバイダのユーザーのみ認証するようにします。

ユーザー名フィールドのカスタマイズ

パスワードグラントを使用する認証を行う場合、Passportはモデルのemail属性をユーザー名("username")として利用します。しかし、モデルのfindForPassportメソッドを定義することで、この振る舞いをカスタマイズできます。

<?php

namespace App;

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

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    /**
     * 指定されたユーザー名のユーザーインスタンスを見つける
     *
     * @param  string  $username
     * @return \App\User
     */
    public function findForPassport($username)
    {
        return $this->where('username', $username)->first();
    }
}

パスワードバリデーションのカスタマイズ

パスワードガードを使用して認証している場合、Passportは指定されたパスワードを確認するためにモデルのpassword属性を使用します。もし、password属性を持っていないか、パスワードのバリデーションロジックをカスタマイズしたい場合は、モデルのvalidateForPassportPasswordGrantメソッドを定義してください。

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    /**
     * Passportパスワードグラントのために、ユーザーのパスワードをバリデート
     *
     * @param  string  $password
     * @return bool
     */
    public function validateForPassportPasswordGrant($password)
    {
        return Hash::check($password, $this->password);
    }
}

暗黙のグラントトークン

暗黙のグラントは認可コードのグラントと似ています。違いは認可コードの交換をせずにクライアントへトークンが返されることです。一般的にこのグラントは、JavaScriptやモバイルアプリケーションでクライアントの認証情報を安全に保存できない場合に使用します。このグラントを有効にするには、AuthServiceProviderenableImplicitGrantメソッドを呼び出します。

/**
 * 全認証/認可の登録
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::enableImplicitGrant();
}

グラントを有効にしたら、開発者はあなたのアプリケーションからのアクセストークンをリクエストするために、クライアントIDを使うことになるでしょう。使用側アプリケーションは、あなたのアプリケーションの/oauth/authorizeルートへのリダイレクトリクエストを生成する必要があります。例を確認してください。

Route::get('/redirect', function (Request $request) {
    $request->session()->put('state', $state = Str::random(40));

    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'token',
        'scope' => '',
        'state' => $state,
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

Tip!! /oauth/authorizeルートは、すでにPassport::routesメソッドが定義づけていることを覚えておいてください。このルートを自分で定義する必要はありません。

クライアント認証情報グラントトークン

クライアント認証情報グラントはマシンーマシン間の認証に最適です。たとえば、APIによりメンテナンスタスクを実行する、定期実行ジョブに使用できます。

クライアント認証情報グラントによりトークンを発行する前に、クライアント認証情報グラントクライアントを生成する必要があります。passport:clientコマンドで、--clientオプションを使用してください。

php artisan passport:client --client

次に、このグラントタイプを使用するために、app/Http/Kernel.phpファイルの$routeMiddlewareへ、CheckClientCredentialsミドルウェアを追加する必要があります。

use Laravel\Passport\Http\Middleware\CheckClientCredentials;

protected $routeMiddleware = [
    'client' => CheckClientCredentials::class,
];

それから、ルートへこのミドルウェアを指定します。

Route::get('/orders', function (Request $request) {
    ...
})->middleware('client');

スコープを指定しアクセスを制限するには、ルートへclientミドルウェアを指定する時に、カンマ区切りで指定します。

Route::get('/orders', function (Request $request) {
    ...
})->middleware('client:check-status,your-scope');

トークンの取得

このグラントタイプを使うトークンを取得するため、oauth/tokenエンドポイントへリクエストを送ります。

$guzzle = new GuzzleHttp\Client;

$response = $guzzle->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'client_credentials',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'scope' => 'your-scope',
    ],
]);

return json_decode((string) $response->getBody(), true)['access_token'];

パーソナルアクセストークン

ときどき、あなたのユーザーが典型的なコードリダイレクションフローに従うのではなく、自分たち自身でアクセストークンを発行したがることもあるでしょう。あなたのアプリケーションのUIを通じて、ユーザー自身のトークンを発行を許可することにより、あなたのAPIをユーザーに経験してもらう事ができますし、全般的なアクセストークン発行するシンプルなアプローチとしても役立つでしょう。

パーソナルアクセスクライアントの作成

あなたのアプリケーションでパーソナルアクセストークンを発行する前に、パーソナルアクセスクライアントを作成する必要があります。--personalオプションを付け、passport:clientコマンドを実行すれば、作成できます。passport:installコマンドを実行済みの場合、このコマンドを実行する必要はありません。

php artisan passport:client --personal

パーソナルアクセスクライアントを制作したら、クライアントIDと平文シークレット値をアプリケーションの.envファイルに設定してください。

PASSPORT_PERSONAL_ACCESS_CLIENT_ID=client-id-value
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=unhashed-client-secret-value

次に、AuthServiceProviderbootメソッドの中で、Passport::personalAccessClientIdPassport::personalAccessClientSecretを呼び出し、これらの値を登録します。

/**
 * 全認証/認可の登録
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::personalAccessClientId(
        config('passport.personal_access_client.id')
    );

    Passport::personalAccessClientSecret(
        config('passport.personal_access_client.secret')
    );
}

パーソナルアクセストークンの管理

パーソナルアクセスクライアントを作成したら、UserモデルインスタンスのcreateTokenメソッドを使用し、指定ユーザーに対しトークンを発行することができるようになります。createTokenメソッドは最初の引数として、トークンの名前を受け付けます。任意の第2引数として、スコープの配列を指定できます。

$user = App\User::find(1);

// スコープ無しのトークンを作成する
$token = $user->createToken('Token Name')->accessToken;

// スコープ付きのトークンを作成する
$token = $user->createToken('My Token', ['place-orders'])->accessToken;

JSON API

Passportにはパーソナルアクセストークンを管理するためのJSON APIも含まれています。ユーザーにパーソナルアクセストークンを管理してもらうダッシュボードを提供するため、APIと皆さんのフロントエンドを結びつける必要があるでしょう。以降から、パーソナルアクセストークンを管理するためのAPIエンドポイントをすべて説明します。利便性を考慮し、エンドポイントへのHTTPリクエスト作成をデモンストレートするために、Axiosを使用していきましょう。

JSON APIはwebauthミドルウェアにより保護されています。そのため、みなさん自身のアプリケーションからのみ呼び出せます。外部ソースから呼び出すことはできません。

Tip!! パーソナルアクセストークンのフロントエンドを自分自身で実装したくない場合は、フロントエンドクイックスタートを使用して、短時間に完全な機能を持つフロントエンドを用意できます。

GET /oauth/scopes

このルートはあなたのアプリケーションで定義した、全スコープを返します。このルートを使い、ユーザーがパーソナルアクセストークンに割り付けたスコープをリストできます。

axios.get('/oauth/scopes')
    .then(response => {
        console.log(response.data);
    });

GET /oauth/personal-access-tokens

このルートは認証中のユーザーが作成したパーソナルアクセストークンをすべて返します。ユーザーがトークンの編集や取り消しを行うため、全トークンをリストするために主に使われます。

axios.get('/oauth/personal-access-tokens')
    .then(response => {
        console.log(response.data);
    });

POST /oauth/personal-access-tokens

このルートは新しいパーソナルアクセストークンを作成します。トークンの名前(name)と、トークンに割り付けるスコープ(scope)の、2つのデータが必要です。

const data = {
    name: 'Token Name',
    scopes: []
};

axios.post('/oauth/personal-access-tokens', data)
    .then(response => {
        console.log(response.data.accessToken);
    })
    .catch (response => {
        // レスポンス上のエラーのリスト
    });

DELETE /oauth/personal-access-tokens/{token-id}

このルートはパーソナルアクセストークンを取り消すために使用します。

axios.delete('/oauth/personal-access-tokens/' + tokenId);

ルート保護

ミドルウェアによる保護

Passportは送信されてきたリクエスト上のアクセストークンをバリデートする、認証ガードを用意しています。passportドライバをapiガードで使うように設定すれば、あとはアクセストークンをバリデートしたいルートに、auth:apiミドルウェアを指定するだけです。

Route::get('/user', function () {
    //
})->middleware('auth:api');

複数認証ガード

アプリケーションの認証でたぶんまったく異なるEloquentモデルを使用する、別々のタイプのユーザーを認証する場合、それぞれのユーザープロバイダタイプごとにガード設定を定義する必用があるでしょう。これにより特定ユーザープロバイダ向けのリクエストを保護できます。例としてconfig/auth.php設定ファイルで以下のようなガード設定を行っているとしましょう。

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

'api-customers' => [
    'driver' => 'passport',
    'provider' => 'customers',
],

以下のルートは受信リクエストを認証するためcustomersユーザープロバイダを使用するapi-customersガードを使用します。

Route::get('/customer', function () {
    //
})->middleware('auth:api-customers');

Tip!! Passportを使用する複数ユーザープロバイダ利用の詳細は、パスワードグラントのドキュメントを調べてください。

アクセストークンの受け渡し

Passportにより保護されているルートを呼び出す場合、あなたのアプリケーションのAPI利用者は、リクエストのAuthorizationヘッダとして、アクセストークンをBearerトークンとして指定する必要があります。Guzzle HTTPライブラリを使う場合を例として示します。

$response = $client->request('GET', '/api/user', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer '.$accessToken,
    ],
]);

トークンのスコープ

スコープは、あるアカウントにアクセスする許可がリクエストされたとき、あなたのAPIクライアントに限定された一連の許可をリクエストできるようにします。たとえば、eコマースアプリケーションを構築している場合、全API利用者へ発注する許可を与える必要はないでしょう。代わりに、利用者へ注文の発送状況にアクセスできる許可を与えれば十分です。言い換えれば、スコープはアプリケーションユーザーに対し、彼らの代理としてのサードパーティアプリケーションが実行できるアクションを制限できるようにします。

スコープの定義

AuthServiceProviderbootメソッドの中で、Passport::tokensCanメソッドを用い、皆さんのAPIのスコープを定義できます。tokenCanメソッドはスコープ名とスコープの説明の配列を引数に取ります。スコープの説明はお望み通りに記述でき、許可の承認ページでユーザーに表示されます。

use Laravel\Passport\Passport;

Passport::tokensCan([
    'place-orders' => 'Place orders',
    'check-status' => 'Check order status',
]);

デフォルトスコープ

クライアントが特定のスコープを要求しない場合は、setDefaultScopeメソッドを使用しそのトークンにデフォルトスコープを付加するように、Passportサーバを設定できます。通常、このメソッドはAuthServiceProviderbootメソッドで呼び出す必要があります。

use Laravel\Passport\Passport;

Passport::setDefaultScope([
    'check-status',
    'place-orders',
]);

トークンへのスコープ割り付け

許可コードのリクエスト時

許可コードグラントを用い、アクセストークンをリクエストする際、利用者はscopeクエリ文字列パラメータとして、希望するスコープを指定する必要があります。scopeパラメータはスコープを空白で区切ったリストです。

Route::get('/redirect', function () {
    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'code',
        'scope' => 'place-orders check-status',
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

パーソナルアクセストークン発行時

UserモデルのcreateTokenメソッドを使用し、パーソナルアクセストークンを発行する場合、メソッドの第2引数として希望するスコープを配列で渡します。

$token = $user->createToken('My Token', ['place-orders'])->accessToken;

スコープのチェック

Passportには、指定されたスコープが許可されているトークンにより、送信されたリクエストが認証されているかを確認するために使用できる、2つのミドルウエアが用意されています。これを使用するには、app/Http/Kernel.phpファイルの$routeMiddlewareプロパティへ、以下のミドルウェアを追加してください。

'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,

全スコープの確認

scopesミドルウェアは、リストしたスコープがすべて、送信されてきたリクエストのアクセストークンに含まれていることを確認するため、ルートへ指定します。

Route::get('/orders', function () {
    // アクセストークンは"check-status"と"place-orders"、両スコープを持っている
})->middleware(['auth:api', 'scopes:check-status,place-orders']);

一部のスコープの確認

scopeミドルウエアは、リストしたスコープのうち、最低1つが送信されてきたリクエストのアクセストークンに含まれていることを確認するため、ルートへ指定します。

Route::get('/orders', function () {
    // アクセストークンは、"check-status"か"place-orders"、どちらかのスコープを持っている
})->middleware(['auth:api', 'scope:check-status,place-orders']);

トークンインスタンスでのスコープチェック

アクセストークンを確認されたリクエストがアプリケーションにやってきた後でも、認証済みのUserインスタンスへtokenCanメソッドを使用し、トークンが指定したスコープを持っているかを確認できます。

use Illuminate\Http\Request;

Route::get('/orders', function (Request $request) {
    if ($request->user()->tokenCan('place-orders')) {
        //
    }
});

その他のスコープメソッド

scopeIdsメソッドは定義済みの全ID/名前の配列を返します。

Laravel\Passport\Passport::scopeIds();

scopesメソッドは定義済みの全スコープをLaravel\Passport\Scopeのインスタンスの配列として返します。

Laravel\Passport\Passport::scopes();

scopesForメソッドは、指定したID/名前に一致するLaravel\Passport\Scopeインスタンスの配列を返します。

Laravel\Passport\Passport::scopesFor(['place-orders', 'check-status']);

指定したスコープが定義済みであるかを判定するには、hasScopeメソッドを使います。

Laravel\Passport\Passport::hasScope('place-orders');

APIをJavaScriptで利用

API構築時にJavaScriptアプリケーションから、自分のAPIを利用できたらとても便利です。このAPI開発のアプローチにより、世界中で共有されるのと同一のAPIを自身のアプリケーションで使用できるようになります。自分のWebアプリケーションやモバイルアプリケーション、サードパーティアプリケーション、そしてさまざまなパッケージマネージャ上で公開するSDKにより、同じAPIが使用されます。

通常、皆さんのAPIをJavaScriptアプリケーションから使用しようとするなら、アプリケーションに対しアクセストークンを自分で送り、それを毎回リクエストするたび、一緒にアプリケーションへ渡す必要があります。しかし、Passportにはこれを皆さんに変わって処理するミドルウェアが用意してあります。必要なのはapp/Http/Kernel.phpファイル中の、webミドルウェアグループに対し、CreateFreshApiTokenミドルウェアを追加することだけです。

'web' => [
    // 他のミドルウェア…
    \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],

Note: ミドルウェアの指定の中で、CreateFreshApiTokenミドルウェアを確実に最後へリストしてください。

このPassportミドルウェアはlaravel_tokenクッキーを送信するレスポンスへ付加します。このクッキーはPassportが、皆さんのJavaScriptアプリケーションからのAPIリクエストを認可するために使用する、暗号化されたJWTを含んでいます。JWTはsession.lifetime設定値と同じ有効期間です。これで、アクセストークンを明示的に渡さなくても、あなたのアプリケーションのAPIへリクエストを作成できるようになります。

axios.get('/api/user')
    .then(response => {
        console.log(response.data);
    });

クッキー名のカスタマイズ

必要であれば、Passport::cookieメソッドを使用し、laravel_tokenクッキーの名前をカスタマイズできます。通常、このメソッドはAuthServiceProviderbootメソッドから呼び出します。

/**
 * 全認証/認可の登録
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::cookie('custom_name');
}

CSRF保護

この認証方法を使用する場合、リクエストのヘッダに有効なCSRFトークンを確実に含める必要があります。デフォルトのLaravel JavaScriptスカフォールドはAxiosインスタンスを含み、同一オリジンリクエスト上にX-XSRF-TOKENヘッダを送るために、暗号化されたXSRF-TOKENクッキーを自動的に使用します。

Tip!! X-XSRF-TOKENの代わりにX-CSRF-TOKENヘッダを送る方法を取る場合は、csrf_token()により提供される復元したトークンを使用する必要があります。

イベント

Passportはアクセストークン発行時とトークンリフレッシュ時にイベントを発行します。これらのイベントをデータベース状の他のアクセストークンを破棄したり、無効にしたりするために使用できます。アプリケーションのEventServiceProviderで、これらのイベントをリッスンできます。

/**
 * アプリケーションのイベントリスナマッピング
 *
 * @var array
 */
protected $listen = [
    'Laravel\Passport\Events\AccessTokenCreated' => [
        'App\Listeners\RevokeOldTokens',
    ],

    'Laravel\Passport\Events\RefreshTokenCreated' => [
        'App\Listeners\PruneOldTokens',
    ],
];

テスト

PassportのactingAsメソッドは、現在認証中のユーザーを指定すると同時にスコープも指定します。actingAsメソッドの最初の引数はユーザーのインスタンスで、第2引数はユーザートークンに許可するスコープ配列を指定します。

use App\User;
use Laravel\Passport\Passport;

public function testServerCreation()
{
    Passport::actingAs(
        factory(User::class)->create(),
        ['create-servers']
    );

    $response = $this->post('/api/create-server');

    $response->assertStatus(201);
}

PassportのactingAsClientメソッドは、現在認証中のクライアントを指定すると同時にスコープも指定します。actingAsClientメソッドの最初の引数はクライアントインスタンスで、第2引数はクライアントのトークンへ許可するスコープの配列です。

use Laravel\Passport\Client;
use Laravel\Passport\Passport;

public function testGetOrders()
{
    Passport::actingAsClient(
        factory(Client::class)->create(),
        ['check-status']
    );

    $response = $this->get('/api/orders');

    $response->assertStatus(200);
}

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる