メインコンテンツまでスキップ
バージョン: 10.x

Laravel Passport

Introduction

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

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

Passport or Sanctum?

始める前に、アプリケーションが Laravel Passport と Laravel Sanctum のどちらの方が適切に提供されるかを判断したい場合があります。アプリケーションが OAuth2 をサポートする必要がある場合は、Laravel Passport を使用する必要があります。

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

Installation

まず、Composer パッケージ マネージャーを介して Passport をインストールします。

composer require laravel/passport

Passport の service provider は独自のデータベース移行ディレクトリを登録するため、パッケージのインストール後にデータベースを移行する必要があります。Passportの移行により、アプリケーションが OAuth2 クライアントとアクセス トークンを保存するために必要なテーブルが作成されます。

php artisan migrate

次に、passport:install Artisan コマンドを実行する必要があります。このコマンドは、安全なアクセス トークンを生成するために必要な暗号化キーを作成します。さらに、このコマンドは、アクセス トークンの生成に使用される「個人アクセス」クライアントと「パスワード許可」クライアントを作成します。

php artisan passport:install

自動インクリメント整数の代わりに、Passport Client モデルの主キー値として UUID を使用したい場合は、the uuids option を使用して Passport をインストールしてください。

passport:install コマンドを実行した後、Laravel\Passport\HasApiTokens 特性を App\Models\User モデルに追加します。この特性は、認証されたユーザーのトークンとスコープを検査できるようにするいくつかのヘルパ メソッドをモデルに提供します。モデルがすでに Laravel\Sanctum\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',
],
],

Client UUIDs

--uuids オプションを指定して passport:install コマンドを実行することもできます。このオプションは、Passport Client モデルの主キー値として自動インクリメント整数の代わりに UUID を使用するように Passport に指示します。 --uuids オプションを指定して passport:install コマンドを実行すると、Passportのデフォルト移行の無効化に関する追加の指示が表示されます。

php artisan passport:install --uuids

Deploying Passport

Passport をアプリケーションのサーバーに初めてデプロイするときは、おそらく passport:keys コマンドを実行する必要があります。このコマンドは、Passportがアクセス トークンを生成するために必要な暗号化キーを生成します。生成されたキーは通常、ソース管理に保持されません。

php artisan passport:keys

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

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

Loading Keys From the Environment

あるいは、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-----"

Migration Customization

Passport のデフォルトの移行を使用しない場合は、App\Providers\AppServiceProvider クラスの register メソッドで Passport::ignoreMigrations メソッドを呼び出す必要があります。 vendor:publish Artisan コマンドを使用して、デフォルトの移行をエクスポートできます。

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

Upgrading Passport

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

Configuration

Client Secret Hashing

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

use Laravel\Passport\Passport;

Passport::hashClientSecrets();

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

Token Lifetimes

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

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

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

Overriding Default Models

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

use Laravel\Passport\Client as PassportClient;

class Client extends PassportClient
{
// ...
}

モデルを定義した後、Laravel\Passport\Passport クラスを介してカスタム モデルを使用するように Passport に指示できます。通常、アプリケーションの App\Providers\AuthServiceProvider クラスの 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;

/**
* Register any authentication / authorization 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);
}

Overriding Routes

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

use Laravel\Passport\Passport;

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

次に、its routes file の Passport で定義されたルートをアプリケーションの routes/web.php ファイルにコピーし、好みに合わせて変更します。

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

Issuing Access Tokens

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

Managing Clients

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

The passport:client Command

クライアントを作成する最も簡単な方法は、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 => {
// ...
});

Requesting Tokens

Redirecting for Authorization

クライアントが作成されると、開発者はクライアント 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 アプリケーションは、ユーザーがすでに既存のセッションを持っている場合でも、常にユーザーにアプリケーションへの再ログインを要求します。

prompt 値が指定されていない場合、ユーザーは、要求されたスコープの使用アプリケーションへのアクセスを以前に承認していない場合にのみ、承認を求められます。

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

Approving the Request

認可リクエストを受信すると、Passport は prompt パラメータ (存在する場合) の値に基づいて自動的に応答し、ユーザーに認可リクエストを承認または拒否できるテンプレートを表示する場合があります。リクエストを承認すると、使用側アプリケーションによって指定された redirect_uri にリダイレクトされます。 redirect_uri は、クライアントの作成時に指定された redirect URL と一致する必要があります。

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

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

ファーストパーティクライアントを認可する場合など、認可プロンプトをスキップしたい場合があります。これは、extending the Client modelskipsAuthorization メソッドを定義することで実現できます。 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();
}
}

Converting Authorization Codes to Access Tokens

ユーザーが認可リクエストを承認すると、ユーザーは使用側アプリケーションにリダイレクトされます。コンシューマは、まず 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);

Refreshing Tokens

アプリケーションが有効期間の短いアクセス トークンを発行する場合、ユーザーは、アクセス トークンの発行時に提供された更新トークンを使用してアクセス トークンを更新する必要があります。

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 属性には、アクセス トークンの有効期限が切れるまでの秒数が含まれます。

Revoking Tokens

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

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);

Purging Tokens

トークンが取り消されたり期限切れになったりした場合、データベースからトークンを削除することができます。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

アプリケーションの App\Console\Kernel クラスで scheduled job を構成して、スケジュールに従ってトークンを自動的にプルーニングすることもできます。

/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('passport:purge')->hourly();
}

Authorization Code Grant With PKCE

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

Creating the Client

アプリケーションが PKCE を使用して認可コードグラント経由でトークンを発行できるようにするには、PKCE 対応クライアントを作成する必要があります。これを行うには、passport:client Artisan コマンドに --public オプションを指定します。

php artisan passport:client --public

Requesting Tokens

Code Verifier and Code Challenge

この認可付与ではクライアント シークレットが提供されないため、開発者はトークンを要求するためにコード検証ツールとコード チャレンジの組み合わせを生成する必要があります。

コード検証子は、RFC 7636 specification で定義されているように、文字、数字、および "-"".""_""~" 文字を含む 43 ~ 128 文字のランダムな文字列である必要があります。

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

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

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

Redirecting for Authorization

クライアントが作成されたら、クライアント 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);
});

Converting Authorization Codes to Access Tokens

ユーザーが認可リクエストを承認すると、ユーザーは使用側アプリケーションにリダイレクトされます。コンシューマは、標準の認可コード付与と同様に、リダイレクト前に保存された値に対して 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();
});

Password Grant Tokens

パスワード付与トークンの使用は推奨されなくなりました。代わりに、a grant type that is currently recommended by OAuth2 Server を選択する必要があります。

OAuth2 パスワード付与により、モバイル アプリケーションなどの他のファーストパーティ クライアントが、電子メール アドレス/ユーザー名とパスワードを使用してアクセス トークンを取得できるようになります。これにより、ユーザーが OAuth2 認可コード リダイレクト フロー全体を実行する必要がなく、ファーストパーティ クライアントにアクセス トークンを安全に発行できます。

Creating a Password Grant Client

アプリケーションがパスワード付与を通じてトークンを発行できるようにするには、パスワード付与クライアントを作成する必要があります。これを行うには、passport:client Artisan コマンドに --password オプションを指定します。 すでに passport:install コマンドを実行している場合は、このコマンドを実行する必要はありません。

php artisan passport:client --password

Requesting Tokens

パスワード付与クライアントを作成したら、ユーザーの電子メール アドレスとパスワードを使用して POST リクエストを /oauth/token ルートに発行することで、アクセス トークンをリクエストできます。このルートは Passport によってすでに登録されているため、手動で定義する必要がないことに注意してください。リクエストが成功すると、サーバーからの JSON レスポンスで access_token および refresh_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' => '[email protected]',
'password' => 'my-password',
'scope' => '',
]);

return $response->json();

アクセス トークンはデフォルトで長期間有効であることに注意してください。ただし、必要に応じて自由に configure your maximum access token lifetime を実行できます。

Requesting All Scopes

パスワード付与またはクライアント資格情報付与を使用する場合、アプリケーションでサポートされているすべてのスコープに対してトークンを承認したい場合があります。これを行うには、* スコープをリクエストします。 * スコープをリクエストした場合、トークン インスタンスの 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' => '[email protected]',
'password' => 'my-password',
'scope' => '*',
]);

Customizing the User Provider

アプリケーションが複数の authentication user provider を使用する場合、artisan passport:client --password コマンドでクライアントを作成するときに --provider オプションを指定することで、パスワード付与クライアントが使用するユーザー プロバイダを指定できます。指定されたプロバイダ名は、アプリケーションの config/auth.php 構成ファイルで定義されている有効なプロバイダと一致する必要があります。その後、protect your route using middleware を実行して、ガードの指定されたプロバイダのユーザーのみが承認されるようにすることができます。

Customizing the Username Field

パスワード付与を使用して認証する場合、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();
}
}

Customizing the Password Validation

パスワード付与を使用して認証する場合、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);
}
}

Implicit Grant Tokens

暗黙的な付与トークンの使用は推奨されなくなりました。代わりに、a grant type that is currently recommended by OAuth2 Server を選択する必要があります。

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

/**
* Register any authentication / authorization 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 によってすでに定義されていることに注意してください。このルートを手動で定義する必要はありません。

Client Credentials Grant Tokens

クライアント資格情報の付与は、マシン間の認証に適しています。たとえば、API を介してメンテナンス タスクを実行するスケジュールされたジョブでこの許可を使用できます。

アプリケーションがクライアント資格情報付与を通じてトークンを発行できるようにするには、クライアント資格情報付与クライアントを作成する必要があります。これは、passport:client Artisan コマンドの --client オプションを使用して実行できます。

php artisan passport:client --client

次に、この許可タイプを使用するには、アプリケーションの app/Http/Kernel.php ファイルの $middlewareAliases プロパティに CheckClientCredentials ミドルウェアを追加します。

use Laravel\Passport\Http\Middleware\CheckClientCredentials;

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

次に、ミドルウェアをルートにアタッチします。

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

ルートへのアクセスを特定のスコープに制限するには、client ミドルウェアをルートにアタッチするときに、必要なスコープのカンマ区切りのリストを指定できます。

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

Retrieving Tokens

この付与タイプを使用してトークンを取得するには、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'];

Personal Access Tokens

場合によっては、ユーザーが通常の認可コード リダイレクト フローを経由せずに自分自身にアクセス トークンを発行したい場合があります。ユーザーがアプリケーションの UI を介して自分自身にトークンを発行できるようにすると、ユーザーが API を実験できるようにする場合や、一般にアクセス トークンを発行するためのより簡単なアプローチとして機能する場合があります。

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

Creating a Personal Access Client

アプリケーションが個人アクセス トークンを発行できるようにするには、個人アクセス クライアントを作成する必要があります。これを行うには、--personal オプションを指定して passport:client Artisan コマンドを実行します。すでに 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"

Managing Personal Access Tokens

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

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

このルートは、アプリケーションに定義されたすべての 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);

Protecting Routes

Via Middleware

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

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

client credentials grant を使用している場合は、auth:api ミドルウェアの代わりに the client middleware を使用してルートを保護する必要があります。

Multiple Authentication Guards

アプリケーションが、おそらくまったく異なる 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 で複数のユーザー プロバイダを使用する方法の詳細については、password grant documentation を参照してください。

Passing the Access Token

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();

Token Scopes

スコープを使用すると、API クライアントがアカウントにアクセスするための承認をリクエストするときに、特定の権限のセットをリクエストできるようになります。たとえば、電子商取引アプリケーションを構築している場合、すべての API コンシューマーが注文する機能を必要とするわけではありません。代わりに、消費者が注文の出荷ステータスにアクセスするための承認のみを要求できるようにすることもできます。つまり、スコープを使用すると、アプリケーションのユーザーは、サードパーティのアプリケーションがユーザーに代わって実行できるアクションを制限できます。

Defining Scopes

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

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

Default Scope

クライアントが特定のスコープを要求しない場合は、setDefaultScope メソッドを使用してデフォルトのスコープをトークンに付加するように Passport サーバーを構成できます。通常、このメソッドは、アプリケーションの App\Providers\AuthServiceProvider クラスの boot メソッドから呼び出す必要があります。

use Laravel\Passport\Passport;

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

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

Passportのデフォルトのスコープは、ユーザーが生成した個人用アクセス トークンには適用されません。

Assigning Scopes to Tokens

When Requesting Authorization Codes

認可コードグラントを使用してアクセストークンをリクエストする場合、コンシューマは希望するスコープを 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);
});

When Issuing Personal Access Tokens

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

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

Checking Scopes

Passport には、受信リクエストが特定のスコープが付与されたトークンで認証されていることを検証するために使用できる 2 つのミドルウェアが含まれています。まず、次のミドルウェアを app/Http/Kernel.php ファイルの $middlewareAliases プロパティに追加します。

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

Check For All Scopes

scopes ミドルウェアをルートに割り当てて、受信リクエストのアクセス トークンにリストされているスコープがすべて含まれていることを確認できます。

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

Check for Any Scopes

scope ミドルウェアをルートに割り当てて、受信リクエストのアクセス トークンにリストされているスコープの * 少なくとも 1 つ* があることを確認できます。

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

Checking Scopes on a Token Instance

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

use Illuminate\Http\Request;

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

Additional Scope Methods

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');

Consuming Your API With JavaScript

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

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

'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],

CreateFreshApiToken ミドルウェアがミドルウェア スタックにリストされている最後のミドルウェアであることを確認する必要があります。

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

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

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

/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}

CSRF Protection

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

X-XSRF-TOKEN の代わりに X-CSRF-TOKEN ヘッダーを送信することを選択した場合は、csrf_token() によって提供される暗号化されていないトークンを使用する必要があります。

Events

Passportは、アクセス トークンとリフレッシュ トークンを発行するときにイベントを発生させます。これらのイベントを使用して、データベース内の他のアクセス トークンをプルーニングまたは取り消すことができます。必要に応じて、アプリケーションの App\Providers\EventServiceProvider クラスでこれらのイベントにリスナをアタッチできます。

/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Laravel\Passport\Events\AccessTokenCreated' => [
'App\Listeners\RevokeOldTokens',
],

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

Testing

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

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;

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

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

$response->assertStatus(200);
}