メインコンテンツまでスキップ

Laravel Passport

はじめに

Laravel Passport は、数分で Laravel アプリケーションに完全な OAuth2 サーバーの実装を提供します。Passport は、Andy Millington と Simon Hamp によってメンテナンスされている League OAuth2 server の上に構築されています。

警告

このドキュメントでは、OAuth2 について既に理解していることを前提としています。OAuth2 について何も知らない場合は、続行する前に一般的な 用語 と機能について理解することを検討してください。

Passport または Sanctum?

始める前に、アプリケーションが Laravel Passport または Laravel Sanctum のどちらが適しているかを判断することができます。アプリケーションが絶対に OAuth2 をサポートする必要がある場合は、Laravel Passport を使用する必要があります。

ただし、単一ページアプリケーション、モバイルアプリケーション、または API トークンを発行する場合は、Laravel Sanctum を使用する必要があります。Laravel Sanctum は OAuth2 をサポートしていませんが、よりシンプルな API 認証開発体験を提供します。

インストール

install:api Artisan コマンドを使用して Laravel Passport をインストールできます:

php artisan install:api --passport

このコマンドは、OAuth2 クライアントとアクセストークンを保存するためにアプリケーションが必要とするテーブルを作成するために必要なデータベースマイグレーションを公開および実行します。また、セキュアなアクセストークンを生成するために必要な暗号化キーも作成します。

さらに、このコマンドは、Passport Client モデルの主キー値として自動増分整数の代わりに UUID を使用するかどうかを尋ねます。

install:api コマンドを実行した後、App\Models\User モデルに Laravel\Passport\HasApiTokens トレイトを追加してください。このトレイトにより、モデルにいくつかのヘルパーメソッドが提供され、認証されたユーザーのトークンとスコープを調べることができます:

    <?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}

最後に、アプリケーションの config/auth.php 構成ファイルで、api 認証ガードを定義し、driver オプションを passport に設定する必要があります。これにより、着信 API リクエストの認証に Passport の TokenGuard を使用するようにアプリケーションに指示されます。

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

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

Passportの展開

Passportをアプリケーションのサーバーに初めて展開する際には、おそらくpassport:keysコマンドを実行する必要があるでしょう。このコマンドは、アクセストークンを生成するためにPassportが必要とする暗号化キーを生成します。生成されたキーは通常、ソースコントロールに保存されません:

php artisan passport:keys

必要に応じて、Passportのキーを読み込むパスを定義することができます。これを実現するためには、Passport::loadKeysFromメソッドを使用することができます。通常、このメソッドは、アプリケーションのApp\Providers\AppServiceProviderクラスのbootメソッドから呼び出すべきです:

    /**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}

環境からキーを読み込む

または、vendor:publish Artisanコマンドを使用してPassportの設定ファイルを公開することができます:

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

設定ファイルを公開した後、アプリケーションの暗号化キーを環境変数として定義することができます:

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のアップグレード

Passportの新しいメジャーバージョンにアップグレードする際には、アップグレードガイドを注意深く確認することが重要です。

設定

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

データベースに保存されるクライアントのシークレットをハッシュ化したい場合は、App\Providers\AppServiceProviderクラスのbootメソッドでPassport::hashClientSecretsメソッドを呼び出す必要があります:

    use Laravel\Passport\Passport;

Passport::hashClientSecrets();

有効にした場合、すべてのクライアントシークレットは作成直後にユーザーにのみ表示可能となります。プレーンテキストのクライアントシークレット値はデータベースに保存されないため、失われた場合にその値を回復することはできません。

トークンの有効期限

デフォルトでは、Passportは1年後に期限切れとなる長寿命のアクセストークンを発行します。より長い/短いトークンの有効期間を設定したい場合は、tokensExpireInrefreshTokensExpireInpersonalAccessTokensExpireIn メソッドを使用できます。これらのメソッドは、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッドから呼び出す必要があります:

    /**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}

警告

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

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

Passportによって内部的に使用されるモデルを拡張することができます。対応するPassportモデルを拡張して独自のモデルを定義することで、自由に使用できます:

    use Laravel\Passport\Client as PassportClient;

class Client extends PassportClient
{
// ...
}

モデルを定義した後、Laravel\Passport\Passport クラスを介してカスタムモデルを使用するようにPassportに指示できます。通常、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッドでPassportにカスタムモデルについて通知する必要があります:

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

/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::useTokenModel(Token::class);
Passport::useRefreshTokenModel(RefreshToken::class);
Passport::useAuthCodeModel(AuthCode::class);
Passport::useClientModel(Client::class);
Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}

ルートのオーバーライド

Passportによって定義されたルートをカスタマイズしたい場合があります。これを実現するには、まずPassportによって登録されたルートを無視する必要があります。これには、アプリケーションの AppServiceProviderregister メソッドに Passport::ignoreRoutes を追加する必要があります:

    use Laravel\Passport\Passport;

/**
* Register any application services.
*/
public function register(): void
{
Passport::ignoreRoutes();
}

その後、Passportによって定義されたルートをアプリケーションの routes/web.php ファイルにコピーし、必要に応じて変更できます:

    Route::group([
'as' => 'passport.',
'prefix' => config('passport.path', 'oauth'),
'namespace' => '\Laravel\Passport\Http\Controllers',
], function () {
// Passport routes...
});

アクセストークンの発行

OAuth2を使用して認可コード経由でアクセスする方法は、ほとんどの開発者がOAuth2に慣れている方法です。認可コードを使用する場合、クライアントアプリケーションはユーザーをサーバーにリダイレクトし、ユーザーはクライアントにアクセストークンを発行するリクエストを承認または拒否します。

クライアントの管理

まず、あなたのアプリケーションのAPIとやり取りする必要があるアプリケーションを開発する開発者は、あなたのアプリケーションに「クライアント」を作成することで登録する必要があります。通常、これは、アプリケーションの名前と、ユーザーが承認した後にあなたのアプリケーションがリダイレクトできるURLを提供することで構成されます。

passport:client コマンド

クライアントを作成する最も簡単な方法は、passport:client Artisanコマンドを使用することです。このコマンドは、OAuth2の機能をテストするために独自のクライアントを作成するために使用できます。client コマンドを実行すると、Passportがクライアントに関する詳細情報を求め、クライアントIDとシークレットを提供します:

php artisan passport:client

リダイレクトURL

クライアントに複数のリダイレクトURLを許可したい場合は、passport:client コマンドでURLを求められる際に、カンマ区切りのリストを指定することができます。カンマを含むURLはURLエンコードする必要があります:

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

JSON API

あなたのアプリケーションのユーザーはclient コマンドを利用できないため、Passportはクライアントを作成するために使用できるJSON APIを提供しています。これにより、クライアントの作成、更新、削除のためのコントローラを手動でコーディングする手間が省けます。

ただし、PassportのJSON APIを自分のフロントエンドとペアにする必要があり、ユーザーがクライアントを管理するためのダッシュボードを提供する必要があります。以下では、クライアントの管理のためのすべてのAPIエンドポイントを見ていきます。便宜上、Axios を使用してエンドポイントにHTTPリクエストを行う方法を示します。

JSON APIはweb および auth ミドルウェアで保護されているため、自分のアプリケーションからのみ呼び出すことができます。外部ソースから呼び出すことはできません。

GET /oauth/clients

このルートは、認証されたユーザーのすべてのクライアントを返します。これは、ユーザーのすべてのクライアントを一覧表示して編集や削除を行うために主に役立ちます。

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

POST /oauth/clients

このルートは新しいクライアントを作成するために使用されます。クライアントの nameredirect URL の2つのデータが必要です。 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 => {
// List errors on response...
});

PUT /oauth/clients/{client-id}

このルートはクライアントを更新するために使用されます。クライアントの nameredirect URL の2つのデータが必要です。 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 => {
// List errors on response...
});

DELETE /oauth/clients/{client-id}

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

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

トークンのリクエスト

承認のリダイレクト

クライアントが作成されると、開発者はクライアントIDとシークレットを使用してアプリケーションから認可コードとアクセストークンをリクエストできます。最初に、消費アプリケーションは次のようにアプリケーションの /oauth/authorize ルートにリダイレクトリクエストを行う必要があります:

    use Illuminate\Http\Request;
use Illuminate\Support\Str;

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://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
// 'prompt' => '', // "none", "consent", or "login"
]);

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

prompt パラメータを使用して、Passportアプリケーションの認証動作を指定できます。

prompt の値が none の場合、PassportはユーザーがPassportアプリケーションで既に認証されていない場合、常に認証エラーをスローします。値が consent の場合、Passportは、消費アプリケーションにすべてのスコープが以前に付与されていても、常に承認画面を表示します。値が login の場合、Passportアプリケーションは、既存のセッションがある場合でも、ユーザーに常にアプリケーションに再ログインするよう促します。

If no prompt value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes.

注記

/oauth/authorize ルートはすでに Passport によって定義されています。このルートを手動で定義する必要はありません。

リクエストの承認

認可リクエストを受け取ると、Passport は自動的に prompt パラメーターの値に基づいて応答し、ユーザーにテンプレートを表示して承認または拒否を許可することがあります。リクエストを承認すると、消費アプリケーションで指定された redirect_uri にリダイレクトされます。redirect_uri は、クライアントが作成されたときに指定された redirect URL と一致する必要があります。

認可承認画面をカスタマイズしたい場合は、vendor:publish Artisan コマンドを使用して Passport のビューを公開することができます。公開されたビューは resources/views/vendor/passport ディレクトリに配置されます:

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

ファーストパーティクライアントを認可する場合など、認可プロンプトをスキップしたい場合があります。これは、デフォルトモデルをオーバーライド して Client モデルを拡張し、skipsAuthorization メソッドを定義することで実現できます。skipsAuthorizationtrue を返すと、クライアントは承認され、ユーザーは redirect_uri に直ちにリダイレクトされます。ただし、消費アプリケーションが認可のためにリダイレクトする際に prompt パラメーターを明示的に設定している場合を除きます:

    <?php

namespace App\Models\Passport;

use Laravel\Passport\Client as BaseClient;

class Client extends BaseClient
{
/**
* Determine if the client should skip the authorization prompt.
*/
public function skipsAuthorization(): bool
{
return $this->firstParty();
}
}

認可コードをアクセストークンに変換する

ユーザーが認可リクエストを承認すると、消費アプリケーションにリダイレクトされます。消費者はまず、リダイレクト前に保存された state パラメーターを検証する必要があります。state パラメーターが一致する場合、消費者はアクセストークンをリクエストするためにアプリケーションに POST リクエストを発行する必要があります。リクエストには、ユーザーが認可リクエストを承認したときにアプリケーションによって発行された認可コードを含める必要があります:

    use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

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

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

$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'redirect_uri' => 'http://third-party-app.com/callback',
'code' => $request->code,
]);

return $response->json();
});

この /oauth/token ルートは、access_tokenrefresh_token、および expires_in 属性を含む JSON レスポンスを返します。expires_in 属性には、アクセス トークンの有効期限までの秒数が含まれています。

注記

/oauth/authorize ルートと同様に、/oauth/token ルートは Passport によってあなたのために定義されています。このルートを手動で定義する必要はありません。

JSON API

Passport には、認可されたアクセス トークンを管理するための JSON API も含まれています。これを自分のフロントエンドとペアにして、ユーザーがアクセス トークンを管理するダッシュボードを提供することができます。便宜上、Axios を使用して、エンドポイントに対する HTTP リクエストの作成をデモンストレーションします。JSON API は web および auth ミドルウェアによって保護されているため、自分のアプリケーションからのみ呼び出すことができます。

GET /oauth/tokens

このルートは、認証されたユーザーが作成したすべての認可されたアクセス トークンを返します。これは、ユーザーのトークンを一覧表示して取り消すことができるようにするために主に役立ちます:

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

DELETE /oauth/tokens/{token-id}

このルートは、認可されたアクセス トークンとそれに関連するリフレッシュ トークンを取り消すために使用できます:

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

トークンのリフレッシュ

アプリケーションが短命なアクセス トークンを発行する場合、ユーザーは、アクセス トークンが発行されたときに提供されたリフレッシュ トークンを使用してアクセス トークンをリフレッシュする必要があります:

    use Illuminate\Support\Facades\Http;

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

return $response->json();

この /oauth/token ルートは、access_tokenrefresh_token、および expires_in 属性を含む JSON レスポンスを返します。expires_in 属性には、アクセス トークンの有効期限までの秒数が含まれています。

トークンの取り消し

Laravel\Passport\TokenRepositoryrevokeAccessToken メソッドを使用してトークンを取り消すことができます。Laravel\Passport\RefreshTokenRepositoryrevokeRefreshTokensByAccessTokenId メソッドを使用して、トークンのリフレッシュ トークンを取り消すことができます。これらのクラスは、Laravel の サービス コンテナ を使用して解決することができます。

    use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;

$tokenRepository = app(TokenRepository::class);
$refreshTokenRepository = app(RefreshTokenRepository::class);

// Revoke an access token...
$tokenRepository->revokeAccessToken($tokenId);

// Revoke all of the token's refresh tokens...
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);

トークンの削除

トークンが取り消されたり期限切れになった場合、それらをデータベースから削除したいと思うかもしれません。Passportに含まれる passport:purge Artisanコマンドを使用すると、これを行うことができます:

# Purge revoked and expired tokens and auth codes...
php artisan passport:purge

# Only purge tokens expired for more than 6 hours...
php artisan passport:purge --hours=6

# Only purge revoked tokens and auth codes...
php artisan passport:purge --revoked

# Only purge expired tokens and auth codes...
php artisan passport:purge --expired

また、アプリケーションの routes/console.php ファイルに スケジュールされたジョブ を設定して、定期的にトークンを削除することもできます:

    use Laravel\Support\Facades\Schedule;

Schedule::command('passport:purge')->hourly();

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

"Proof Key for Code Exchange" (PKCE) を使用した認可コードグラントは、シングルページアプリケーションやネイティブアプリケーションがAPIにアクセスするための安全な方法です。このグラントは、クライアントシークレットが機密に保存されることを保証できない場合や、認可コードが攻撃者によって傍受される脅威を緩和するために使用すべきです。アクセストークンを取得する際に、"コードバリファイア" と "コードチャレンジ" の組み合わせがクライアントシークレットの代わりとなります。

クライアントの作成

PKCEを使用した認可コードグラントでトークンを発行する前に、PKCEを有効にしたクライアントを作成する必要があります。--public オプションを使用して passport:client Artisanコマンドを使用してこれを行うことができます:

php artisan passport:client --public

トークンのリクエスト

コードバリファイアとコードチャレンジ

この認可グラントはクライアントシークレットを提供しないため、開発者はトークンをリクエストするためにコードバリファイアとコードチャレンジの組み合わせを生成する必要があります。

コードバリファイアは、RFC 7636 で定義されているように、文字、数字、"-"".""_""~" を含む43〜128文字のランダムな文字列である必要があります。

コードチャレンジは、Base64でエンコードされた文字列で、URLやファイル名に使用できる安全な文字を含む必要があります。末尾の '=' 文字は削除され、改行、空白、その他の追加文字は含まれていてはいけません。

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

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

認可のリダイレクト

クライアントが作成されたら、クライアントIDと生成されたコード検証子およびコードチャレンジを使用して、アプリケーションから認可コードとアクセストークンをリクエストできます。最初に、消費アプリケーションは、アプリケーションの /oauth/authorize ルートにリダイレクトリクエストを行う必要があります:

    use Illuminate\Http\Request;
use Illuminate\Support\Str;

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://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
'code_challenge' => $codeChallenge,
'code_challenge_method' => 'S256',
// 'prompt' => '', // "none", "consent", or "login"
]);

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

認可コードをアクセストークンに変換する

ユーザーが認可リクエストを承認すると、消費アプリケーションにリダイレクトされます。消費者は、state パラメータをリダイレクト前に保存された値と照合する必要があります。これは、標準の認可コードグラントと同様です。

stateパラメータが一致する場合、消費者はアクセストークンをリクエストするためにアプリケーションに POST リクエストを発行する必要があります。リクエストには、ユーザーが認可リクエストを承認したときにアプリケーションによって発行された認可コードと元々生成されたコード検証子を含める必要があります:

    use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

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 = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'code_verifier' => $codeVerifier,
'code' => $request->code,
]);

return $response->json();
});

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

警告

パスワードグラントトークンの使用はもはや推奨されていません。代わりに、OAuth2 Server で現在推奨されているグラントタイプを選択するべきです。

OAuth2パスワードグラントにより、モバイルアプリケーションなどの他の第一当事者クライアントが、メールアドレス/ユーザー名とパスワードを使用してアクセストークンを取得できます。これにより、ユーザーにOAuth2認可コードリダイレクトフロー全体を経由する必要なく、最初の当事者クライアントに安全にアクセストークンを発行できます。

パスワードグラントを有効にするには、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッドで enablePasswordGrant メソッドを呼び出します:

    /**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::enablePasswordGrant();
}

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

アプリケーションがパスワードグラントを介してトークンを発行する前に、パスワードグラントクライアントを作成する必要があります。--passwordオプションを使用してpassport:clientアーティザンコマンドを使用してこれを行うことができます。すでにpassport:installコマンドを実行している場合は、このコマンドを実行する必要はありません:

php artisan passport:client --password

トークンのリクエスト

パスワードグラントクライアントを作成したら、ユーザーのメールアドレスとパスワードを使用して/oauth/tokenルートにPOSTリクエストを発行することでアクセストークンをリクエストできます。このルートはすでにPassportによって登録されているため、手動で定義する必要はありません。リクエストが成功すると、サーバーからのJSONレスポンスでaccess_tokenrefresh_tokenを受け取ります:

    use Illuminate\Support\Facades\Http;

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

return $response->json();

注記

アクセストークンはデフォルトで長寿命です。ただし、必要に応じてアクセストークンの最大有効期間を構成することができます。

すべてのスコープのリクエスト

パスワードグラントまたはクライアント資格情報グラントを使用する場合、アプリケーションでサポートされているすべてのスコープにトークンを認可したい場合があります。これは*スコープをリクエストすることで行うことができます。*スコープをリクエストすると、トークンインスタンスのcanメソッドは常にtrueを返します。このスコープはpasswordまたはclient_credentialsグラントを使用して発行されたトークンにのみ割り当てることができます:

    use Illuminate\Support\Facades\Http;

$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'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構成ファイルで定義されている有効なプロバイダと一致する必要があります。その後、ミドルウェアを使用してルートを保護し、ガードで指定されたプロバイダのユーザーのみが認可されるようにすることができます。

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

パスワードグラントを使用して認証する場合、Passport は、認証可能なモデルの email 属性を "ユーザー名" として使用します。ただし、モデルで findForPassport メソッドを定義することで、この動作をカスタマイズすることができます:

    <?php

namespace App\Models;

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

class User extends Authenticatable
{
use HasApiTokens, Notifiable;

/**
* Find the user instance for the given username.
*/
public function findForPassport(string $username): User
{
return $this->where('username', $username)->first();
}
}

パスワード検証のカスタマイズ

パスワードグラントを使用して認証する場合、Passport は、モデルの password 属性を使用して与えられたパスワードを検証します。モデルに password 属性がない場合や、パスワード検証ロジックをカスタマイズしたい場合は、モデルで validateForPassportPasswordGrant メソッドを定義することができます:

    <?php

namespace App\Models;

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;

/**
* Validate the password of the user for the Passport password grant.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}

暗黙的グラントトークン

警告

暗黙的グラントトークンの使用はもはや推奨されていません。代わりに、OAuth2 Server で現在推奨されているグラントタイプ を選択する必要があります。

暗黙的グラントは認可コードグラントに類似していますが、トークンは認可コードを交換せずにクライアントに返されます。このグラントは、クライアントの資格情報を安全に保存できない JavaScript やモバイルアプリケーションで最も一般的に使用されます。このグラントを有効にするには、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッドで enableImplicitGrant メソッドを呼び出します:

    /**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::enableImplicitGrant();
}

グラントが有効になったら、開発者はクライアント ID を使用してアプリケーションからアクセストークンをリクエストできます。消費アプリケーションは、次のようにアプリケーションの /oauth/authorize ルートにリダイレクトリクエストを行う必要があります:

    use Illuminate\Http\Request;

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://third-party-app.com/callback',
'response_type' => 'token',
'scope' => '',
'state' => $state,
// 'prompt' => '', // "none", "consent", or "login"
]);

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

注記

/oauth/authorize ルートは既に Passport によって定義されています。このルートを手動で定義する必要はありません。

クライアント資格情報グラントトークン

クライアント資格情報グラントは、機械間認証に適しています。たとえば、API 上でメンテナンスタスクを実行しているスケジュールされたジョブでこのグラントを使用するかもしれません。

アプリケーションがクライアント資格情報グラントを使用してトークンを発行する前に、クライアント資格情報グラントクライアントを作成する必要があります。これは、passport:clientアーティザンコマンドの--clientオプションを使用して行うことができます:

php artisan passport:client --client

次に、このグラントタイプを使用するために、CheckClientCredentialsミドルウェアのミドルウェアエイリアスを登録します。ミドルウェアエイリアスは、アプリケーションのbootstrap/app.phpファイルで定義することができます:

    use Laravel\Passport\Http\Middleware\CheckClientCredentials;

->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'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エンドポイントにリクエストを行います:

    use Illuminate\Support\Facades\Http;

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

return $response->json()['access_token'];

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

時には、ユーザーが典型的な認可コードリダイレクトフローを経ずに自分自身にアクセストークンを発行したい場合があります。ユーザーがアプリケーションのUIを介して自分自身にトークンを発行できるようにすることは、ユーザーがAPIを試してみたり、一般的にアクセストークンを発行するためのより簡単なアプローチとして役立つことがあります。

注記

アプリケーションが主にパーソナルアクセストークンを発行するためにPassportを使用している場合は、APIアクセストークンを発行するためのLaravelの軽量なファーストパーティライブラリであるLaravel Sanctumを使用することを検討してください。

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

アプリケーションがパーソナルアクセストークンを発行できるようにするには、パーソナルアクセスクライアントを作成する必要があります。これは、passport:clientアーティザンコマンドを--personalオプションとともに実行することで行うことができます。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"

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

パーソナルアクセス クライアントを作成したら、App\Models\User モデルインスタンスの createToken メソッドを使用して、指定されたユーザーのためにトークンを発行できます。createToken メソッドは、トークンの名前を最初の引数として、スコープ のオプション配列を第二引数として受け入れます:

    use App\Models\User;

$user = User::find(1);

// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;

// Creating a token with scopes...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;

JSON API

Passport には、パーソナルアクセス トークンを管理するための JSON API も含まれています。これを独自のフロントエンドとペアにして、ユーザーがパーソナルアクセス トークンを管理するダッシュボードを提供することができます。以下では、パーソナルアクセス トークンを管理するためのすべての API エンドポイントを説明します。便宜上、Axios を使用して、エンドポイントに対する HTTP リクエストの作成方法を示します。

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

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 とトークンに割り当てるべき scopes の2つのデータが必要です:

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

axios.post('/oauth/personal-access-tokens', data)
.then(response => {
console.log(response.data.accessToken);
})
.catch (response => {
// List errors on response...
});

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

このルートは、個人アクセストークンを取り消すために使用できます:

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

ルートの保護

ミドルウェアを介して

Passportには、着信リクエストのアクセストークンを検証する認証ガードが含まれています。apiガードをpassportドライバーを使用するように構成した後は、有効なアクセストークンが必要なルートにauth:apiミドルウェアを指定するだけです:

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

警告

クライアント資格情報付与を使用している場合は、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');

注記

Passportを使用して複数のユーザープロバイダーを使用する方法についての詳細は、パスワード付与ドキュメントを参照してください。

アクセストークンの渡し方

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

    use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
])->get('https://passport-app.test/api/user');

return $response->json();

トークンスコープ

スコープを使用すると、APIクライアントはアカウントにアクセスするための認可をリクエストする際に特定の権限セットを要求できます。たとえば、eコマースアプリケーションを構築している場合、すべてのAPI消費者が注文をする機能を必要とするわけではありません。代わりに、消費者には注文出荷ステータスにアクセスする認可をリクエストするだけの権限を与えることができます。つまり、スコープは、アプリケーションのユーザーが第三者アプリケーションが代わりに実行できるアクションを制限することができるようにします。

スコープの定義

APIのスコープを定義するには、アプリケーションの App\Providers\AppServiceProvider クラスの boot メソッド内で Passport::tokensCan メソッドを使用することができます。tokensCan メソッドはスコープ名とスコープの説明を含む配列を受け入れます。スコープの説明は任意のものであり、認可承認画面にユーザーに表示されます。

    /**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}

デフォルトスコープ

クライアントが特定のスコープをリクエストしない場合、Passportサーバーにデフォルトスコープをトークンにアタッチするように設定することができます。通常、このメソッドはアプリケーションの App\Providers\AppServiceProvider クラスの boot メソッドから呼び出すべきです。

    use Laravel\Passport\Passport;

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

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

注記

Passportのデフォルトスコープは、ユーザーによって生成されたパーソナルアクセストークンには適用されません。

トークンにスコープを割り当てる

認可コードをリクエストする場合

認可コードグラントを使用してアクセストークンをリクエストする際、消費者は 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://passport-app.test/oauth/authorize?'.$query);
});

パーソナルアクセストークンを発行する場合

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

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

スコープの確認

Passportには、特定のスコープが付与されたトークンで認証された着信リクエストを検証するために使用できる2つのミドルウェアが含まれています。開始するには、アプリケーションの bootstrap/app.php ファイルで次のミドルウェアエイリアスを定義してください。

    use Laravel\Passport\Http\Middleware\CheckForAnyScope;
use Laravel\Passport\Http\Middleware\CheckScopes;

->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'scopes' => CheckScopes::class,
'scope' => CheckForAnyScope::class,
]);
})

すべてのスコープをチェックする

scopes ミドルウェアは、指定されたスコープをすべて持っているかどうかを確認するために、ルートに割り当てることができます:

    Route::get('/orders', function () {
// Access token has both "check-status" and "place-orders" scopes...
})->middleware(['auth:api', 'scopes:check-status,place-orders']);

任意のスコープを確認

scope ミドルウェアは、ルートに割り当てることで、リクエストのアクセストークンが 少なくとも1つ の指定されたスコープを持っているかを確認できます:

    Route::get('/orders', function () {
// Access token has either "check-status" or "place-orders" scope...
})->middleware(['auth:api', 'scope:check-status,place-orders']);

トークンインスタンスでのスコープの確認

アクセストークンが認証されたリクエストがアプリケーションに入った後も、認証された App\Models\User インスタンスで tokenCan メソッドを使用して、トークンが特定のスコープを持っているかどうかを確認できます:

    use Illuminate\Http\Request;

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

追加のスコープメソッド

scopeIds メソッドは、すべての定義済みの ID / 名前の配列を返します:

    use Laravel\Passport\Passport;

Passport::scopeIds();

scopes メソッドは、Laravel\Passport\Scope のインスタンスとして定義されたすべてのスコープの配列を返します:

    Passport::scopes();

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

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

hasScope メソッドを使用して、特定のスコープが定義されているかどうかを判断できます:

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

JavaScript で API を消費する

API を構築する際、JavaScript アプリケーションから自分自身の API を消費できると非常に便利です。この API 開発のアプローチにより、自分のアプリケーションが世界と共有している同じ API を消費できます。同じ API は、Web アプリケーション、モバイルアプリケーション、サードパーティのアプリケーション、およびさまざまなパッケージマネージャーで公開する可能性のある SDK によって消費される可能性があります。

通常、JavaScript アプリケーションから自分の API を消費したい場合、アクセストークンを手動でアプリケーションに送信し、各リクエストにそれを渡す必要があります。ただし、Passport にはこれを処理できるミドルウェアが含まれています。必要なのは、アプリケーションの bootstrap/app.php ファイルで web ミドルウェアグループに CreateFreshApiToken ミドルウェアを追加するだけです:

    use Laravel\Passport\Http\Middleware\CreateFreshApiToken;

->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
CreateFreshApiToken::class,
]);
})

警告

CreateFreshApiTokenミドルウェアが、ミドルウェアスタック内で最後にリストされるようにする必要があります。

このミドルウェアは、送信されるレスポンスにlaravel_tokenクッキーを添付します。このクッキーには、PassportがJavaScriptアプリケーションからのAPIリクエストを認証するために使用する暗号化されたJWTが含まれています。JWTの有効期限は、session.lifetime構成値と同じです。ブラウザは自動的にクッキーをすべての後続のリクエストと共に送信するため、アクセストークンを明示的に渡さずにアプリケーションのAPIにリクエストを行うことができます。

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

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

必要に応じて、laravel_tokenクッキーの名前をPassport::cookieメソッドを使用してカスタマイズすることができます。通常、このメソッドは、アプリケーションのApp\Providers\AppServiceProviderクラスのbootメソッドから呼び出すべきです。

    /**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}

CSRF保護

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

注記

X-XSRF-TOKENの代わりにX-CSRF-TOKENヘッダーを送信する場合は、csrf_token()が提供する非暗号化トークンを使用する必要があります。

イベント

Passportは、アクセストークンとリフレッシュトークンの発行時にイベントを発生させます。これらのイベントをリッスンして、データベース内の他のアクセストークンを削除または取り消すことができます。

イベント名
Laravel\Passport\Events\AccessTokenCreated
Laravel\Passport\Events\RefreshTokenCreated

テスト

PassportのactingAsメソッドを使用して、現在認証されているユーザーとそのスコープを指定することができます。actingAsメソッドに与えられる最初の引数はユーザーインスタンスであり、2番目はユーザーのトークンに付与されるべきスコープの配列です。

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

test('servers can be created', function () {
Passport::actingAs(
User::factory()->create(),
['create-servers']
);

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

$response->assertStatus(201);
});
use App\Models\User;
use Laravel\Passport\Passport;

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

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

$response->assertStatus(201);
}

PassportのactingAsClientメソッドを使用して、現在認証されているクライアントとそのスコープを指定できます。actingAsClientメソッドに与えられる最初の引数はクライアントインスタンスであり、2番目はクライアントのトークンに付与されるべきスコープの配列です:

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

test('orders can be retrieved', function () {
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);

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

$response->assertStatus(200);
});
use Laravel\Passport\Client;
use Laravel\Passport\Passport;

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

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

$response->assertStatus(200);
}