Laravel Passport¶
- はじめに
- インストール
- 設定
- アクセストークンの発行
- PKCE を使用した認証コードグラント
- パスワードグラントトークン
- 暗黙的グラントトークン
- クライアントクレデンシャルグラントトークン
- パーソナルアクセストークン
- ルートの保護
- トークンスコープ
- JavaScript での API の利用
- イベント
- テスト
はじめに¶
Laravel Passport は、Laravel アプリケーションに対して数分で完全な OAuth2 サーバー実装を提供します。Passport は、Andy Millington と Simon Hamp によってメンテナンスされている League OAuth2 サーバー の上に構築されています。
Warning
このドキュメントは、あなたがすでに OAuth2 に精通していることを前提としています。OAuth2 について何も知らない場合は、続行する前に OAuth2 の一般的な 用語 と機能について理解を深めることを検討してください。
Passport か Sanctum か?¶
始める前に、アプリケーションが Laravel Passport と Laravel Sanctum のどちらでよりよくサービスを受けられるかを判断することをお勧めします。アプリケーションが絶対に OAuth2 をサポートする必要がある場合は、Laravel Passport を使用する必要があります。
ただし、シングルページアプリケーション、モバイルアプリケーションを認証したり、API トークンを発行したりする場合は、Laravel Sanctum を使用する必要があります。Laravel Sanctum は OAuth2 をサポートしていませんが、はるかにシンプルな API 認証開発体験を提供します。
インストール¶
Laravel Passport は、install:api
Artisan コマンドを介してインストールできます:
このコマンドは、アプリケーションが 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 がアクセストークンを生成するために必要な暗号化キーを生成します。生成されたキーは通常、ソース管理には含まれません:
必要に応じて、Passport のキーをロードするパスを定義できます。これを行うには、Passport::loadKeysFrom
メソッドを使用します。通常、このメソッドはアプリケーションの App\Providers\AppServiceProvider
クラスの boot
メソッドから呼び出す必要があります:
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}
環境からのキーのロード¶
あるいは、vendor:publish
Artisan コマンドを使用して 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 のアップグレード¶
新しいメジャーバージョンの Passport にアップグレードする際には、アップグレードガイド を注意深く確認することが重要です。
設定¶
クライアントシークレットのハッシュ化¶
データベースに保存する際にクライアントのシークレットをハッシュ化したい場合は、App\Providers\AppServiceProvider
クラスの boot
メソッドで Passport::hashClientSecrets
メソッドを呼び出す必要があります:
use Laravel\Passport\Passport;
Passport::hashClientSecrets();
有効にすると、すべてのクライアントシークレットは、作成直後にのみユーザーに表示されるようになります。プレーンテキストのクライアントシークレット値はデータベースに保存されないため、シークレットの値が失われた場合、その値を回復することはできません。
トークンの有効期限¶
デフォルトでは、Passport は1年間有効な長期間有効なアクセストークンを発行します。トークンの有効期限を長くまたは短く設定したい場合は、tokensExpireIn
、refreshTokensExpireIn
、personalAccessTokensExpireIn
メソッドを使用できます。これらのメソッドは、アプリケーションの 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));
}
Warning
Passport のデータベーステーブルの expires_at
カラムは読み取り専用で、表示目的のみで使用されます。トークンを発行する際、Passport は署名され暗号化されたトークン内に有効期限情報を保存します。トークンを無効にする必要がある場合は、取り消し を行う必要があります。
デフォルトモデルのオーバーライド¶
Passport が内部で使用するモデルを自由に拡張できます。独自のモデルを定義し、対応する Passport モデルを拡張することで行えます:
use Laravel\Passport\Client as PassportClient;
class Client extends PassportClient
{
// ...
}
モデルを定義した後、Laravel\Passport\Passport
クラスを介して Passport にカスタムモデルを使用するように指示できます。通常、これはアプリケーションの App\Providers\AppServiceProvider
クラスの boot
メソッドで行います:
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 によって定義されたルートをカスタマイズしたい場合は、まずアプリケーションの AppServiceProvider
の register
メソッドに Passport::ignoreRoutes
を追加して、Passport によって登録されたルートを無視する必要があります:
use Laravel\Passport\Passport;
/**
* アプリケーションサービスの登録
*/
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のルート...
});
アクセストークンの発行¶
OAuth2を介した認証コードの使用は、ほとんどの開発者がOAuth2に精通している方法です。認証コードを使用する場合、クライアントアプリケーションはユーザーをサーバーにリダイレクトし、ユーザーはクライアントにアクセストークンを発行するリクエストを承認または拒否します。
クライアントの管理¶
まず、アプリケーションのAPIと対話する必要があるアプリケーションを構築する開発者は、アプリケーションに登録する必要があります。これには通常、アプリケーションの名前と、ユーザーが認証リクエストを承認した後にリダイレクトされるURLを提供することが含まれます。
passport:client
コマンド¶
クライアントを作成する最も簡単な方法は、passport:client
Artisanコマンドを使用することです。このコマンドは、OAuth2機能をテストするために独自のクライアントを作成するために使用できます。client
コマンドを実行すると、Passportはクライアントに関する詳細情報を求め、クライアントIDとシークレットを提供します。
リダイレクトURL
クライアントに複数のリダイレクトURLを許可したい場合は、passport:client
コマンドでURLを求められたときにカンマ区切りのリストを指定できます。カンマを含むURLはURLエンコードする必要があります。
JSON API¶
アプリケーションのユーザーはclient
コマンドを利用できないため、Passportはクライアントを作成するためのJSON APIを提供します。これにより、クライアントの作成、更新、削除のためのコントローラを手動でコーディングする手間が省けます。
ただし、PassportのJSON APIを独自のフロントエンドと組み合わせて、ユーザーがクライアントを管理できるダッシュボードを提供する必要があります。以下では、クライアントを管理するためのすべてのAPIエンドポイントを確認します。便宜上、エンドポイントへのHTTPリクエストを作成するためにAxiosを使用します。
JSON APIはweb
とauth
ミドルウェアによって保護されています。したがって、独自のアプリケーションからのみ呼び出すことができます。外部ソースから呼び出すことはできません。
GET /oauth/clients
¶
このルートは、認証されたユーザーのすべてのクライアントを返します。これは主に、ユーザーがクライアントを編集または削除できるように、ユーザーのすべてのクライアントをリストアップするために役立ちます。
POST /oauth/clients
¶
このルートは新しいクライアントを作成するために使用されます。これには、クライアントのname
とredirect
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 => {
// レスポンスのエラーをリストアップ...
});
PUT /oauth/clients/{client-id}
¶
このルートはクライアントを更新するために使用されます。これには、クライアントのname
とredirect
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 => {
// レスポンスのエラーをリストアップ...
});
DELETE /oauth/clients/{client-id}
¶
このルートはクライアントを削除するために使用されます。
トークンのリクエスト¶
認証のためのリダイレクト¶
クライアントが作成されると、開発者はクライアント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
の値が提供されていない場合、ユーザーは、以前に消費アプリケーションへのアクセスを要求されたスコープに対して承認されていない場合にのみ、承認を求められます。
Note
/oauth/authorize
ルートはPassportによって既に定義されていることに注意してください。このルートを手動で定義する必要はありません。
リクエストの承認¶
認証リクエストを受け取ると、Passportはprompt
パラメータの値(存在する場合)に基づいて自動的に応答し、ユーザーが認証リクエストを承認または拒否できるテンプレートを表示する場合があります。リクエストが承認された場合、ユーザーは消費アプリケーションによって指定されたredirect_uri
にリダイレクトされます。redirect_uri
は、クライアントが作成されたときに指定されたredirect
URLと一致する必要があります。
認証承認画面をカスタマイズしたい場合は、vendor:publish
Artisanコマンドを使用してPassportのビューを公開できます。公開されたビューはresources/views/vendor/passport
ディレクトリに配置されます。
認証プロンプトをスキップしたい場合があります。たとえば、ファーストパーティクライアントを承認する場合です。これは、Client
モデルを拡張し、skipsAuthorization
メソッドを定義することで実現できます。skipsAuthorization
がtrue
を返す場合、クライアントは承認され、ユーザーはredirect_uri
に即座にリダイレクトされます。ただし、消費アプリケーションが認証リダイレクト時にprompt
パラメータを明示的に設定していない場合に限ります。
<?php
namespace App\Models\Passport;
use Laravel\Passport\Client as BaseClient;
class Client extends BaseClient
{
/**
* クライアントが認証プロンプトをスキップするかどうかを判断します。
*/
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_token
、refresh_token
、および expires_in
属性を含む JSON レスポンスを返します。expires_in
属性には、アクセストークンの有効期限が切れるまでの秒数が含まれています。
Note
/oauth/authorize
ルートと同様に、/oauth/token
ルートは Passport によって定義されています。このルートを手動で定義する必要はありません。
JSON API¶
Passport には、認可されたアクセストークンを管理するための JSON API も含まれています。これを独自のフロントエンドと組み合わせて、ユーザーにアクセストークンを管理するためのダッシュボードを提供することができます。便宜上、エンドポイントへの HTTP リクエストを行うために Axios を使用します。JSON API は web
および auth
ミドルウェアによって保護されています。したがって、自分のアプリケーションからのみ呼び出すことができます。
GET /oauth/tokens
¶
このルートは、認証されたユーザーが作成したすべての認可されたアクセストークンを返します。これは主に、ユーザーのすべてのトークンをリストアップして、それらを取り消すことができるようにするために便利です:
DELETE /oauth/tokens/{token-id}
¶
このルートは、認可されたアクセストークンとそれに関連するリフレッシュトークンを取り消すために使用できます:
トークンのリフレッシュ¶
アプリケーションが短期間のアクセストークンを発行する場合、ユーザーはアクセストークンが発行されたときに提供されたリフレッシュトークンを介してアクセストークンをリフレッシュする必要があります:
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_token
、refresh_token
、および expires_in
属性を含む JSON レスポンスを返します。expires_in
属性には、アクセストークンの有効期限が切れるまでの秒数が含まれています。
トークンの取り消し¶
Laravel\Passport\TokenRepository
の revokeAccessToken
メソッドを使用してトークンを取り消すことができます。トークンのリフレッシュトークンは、Laravel\Passport\RefreshTokenRepository
の revokeRefreshTokensByAccessTokenId
メソッドを使用して取り消すことができます。これらのクラスは、Laravel の サービスコンテナ を使用して解決できます:
use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;
$tokenRepository = app(TokenRepository::class);
$refreshTokenRepository = app(RefreshTokenRepository::class);
// アクセストークンを取り消す...
$tokenRepository->revokeAccessToken($tokenId);
// トークンのすべてのリフレッシュトークンを取り消す...
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
トークンのパージ¶
トークンが取り消されたり期限切れになったりした場合、データベースからそれらをパージしたい場合があります。Passport に含まれる passport:purge
Artisan コマンドは、これを行うことができます:
# 取り消されたトークンと期限切れのトークン、および認証コードをパージする...
php artisan passport:purge
# 6時間以上経過した期限切れのトークンのみをパージする...
php artisan passport:purge --hours=6
# 取り消されたトークンと認証コードのみをパージする...
php artisan passport:purge --revoked
# 期限切れのトークンと認証コードのみをパージする...
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 にアクセスするために認証するための安全な方法です。この付与は、クライアントシークレットが機密に保存されることを保証できない場合や、認証コードが攻撃者によって傍受される脅威を軽減するために使用する必要があります。"code verifier" と "code challenge" の組み合わせが、アクセストークンと交換する際のクライアントシークレットを置き換えます。
クライアントの作成¶
アプリケーションが PKCE を使用した認証コード付与を介してトークンを発行する前に、PKCE 対応のクライアントを作成する必要があります。これは、passport:client
Artisan コマンドに --public
オプションを付けて行うことができます:
トークンのリクエスト¶
コード検証子とコードチャレンジ¶
この認証付与はクライアントシークレットを提供しないため、開発者はトークンをリクエストするためにコード検証子とコードチャレンジの組み合わせを生成する必要があります。
コード検証子は、43 文字から 128 文字の間のランダムな文字列で、文字、数字、および "-"
, "."
, "_"
, "~"
文字を含む必要があります。これは RFC 7636 仕様 で定義されています。
コードチャレンジは、URL とファイル名に安全な文字を含む Base64 エンコードされた文字列である必要があります。末尾の '='
文字は削除され、改行、空白、またはその他の追加文字は含まれてはなりません。
$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();
});
パスワード付与トークン¶
Warning
パスワード付与トークンの使用は推奨されなくなりました。代わりに、OAuth2 サーバーが現在推奨している 付与タイプ を選択する必要があります。
OAuth2 パスワード付与により、モバイルアプリケーションなどの他のファーストパーティクライアントは、メールアドレス / ユーザー名とパスワードを使用してアクセストークンを取得できます。これにより、ユーザーが OAuth2 認証コードリダイレクトフロー全体を経ることなく、ファーストパーティクライアントにアクセストークンを安全に発行できます。
パスワード付与を有効にするには、アプリケーションの App\Providers\AppServiceProvider
クラスの boot
メソッドで enablePasswordGrant
メソッドを呼び出します:
/**
* 任意のアプリケーションサービスのブートストラップ
*/
public function boot(): void
{
Passport::enablePasswordGrant();
}
パスワード付与クライアントの作成¶
アプリケーションがパスワード付与を介してトークンを発行する前に、パスワード付与クライアントを作成する必要があります。これは、passport:client
Artisan コマンドに --password
オプションを付けて行うことができます。すでに passport:install
コマンドを実行している場合は、このコマンドを実行する必要はありません:
トークンのリクエスト¶
パスワードグラントクライアントを作成したら、ユーザーのメールアドレスとパスワードを指定して、/oauth/token
ルートに POST
リクエストを発行することでアクセストークンをリクエストできます。このルートは 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' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '',
]);
return $response->json();
Note
アクセストークンはデフォルトで長期間有効です。ただし、必要に応じてアクセストークンの最大有効期間を設定することができます。
すべてのスコープのリクエスト¶
パスワードグラントまたはクライアントクレデンシャルグラントを使用する場合、アプリケーションがサポートするすべてのスコープに対してトークンを承認したい場合があります。これを行うには、*
スコープをリクエストします。*
スコープをリクエストすると、トークンインスタンスの 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' => '*',
]);
ユーザープロバイダのカスタマイズ¶
アプリケーションが複数の認証ユーザープロバイダを使用している場合、パスワードグラントクライアントが使用するユーザープロバイダを --provider
オプションを指定して artisan passport:client --password
コマンドで作成することができます。指定されたプロバイダ名は、アプリケーションの 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;
/**
* 指定されたユーザー名に対するユーザーインスタンスを検索します。
*/
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;
/**
* Passport パスワードグラントのユーザーのパスワードを検証します。
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}
暗黙的グラントトークン¶
Warning
暗黙的グラントトークンの使用は推奨されなくなりました。代わりに、OAuth2 Server が現在推奨しているグラントタイプを選択してください。
暗黙的グラントは認証コードグラントに似ていますが、トークンは認証コードを交換することなくクライアントに返されます。このグラントは、クライアントクレデンシャルを安全に保存できない JavaScript またはモバイルアプリケーションで最も一般的に使用されます。グラントを有効にするには、アプリケーションの App\Providers\AppServiceProvider
クラスの boot
メソッドで enableImplicitGrant
メソッドを呼び出します。
/**
* 任意のアプリケーションサービスをブートストラップします。
*/
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);
});
Note
/oauth/authorize
ルートは Passport によってすでに定義されているため、手動でこのルートを定義する必要はありません。
クライアントクレデンシャルグラントトークン¶
クライアントクレデンシャルグラントは、マシン間認証に適しています。たとえば、API を介してメンテナンスタスクを実行するスケジュールされたジョブでこのグラントを使用する場合があります。
アプリケーションがクライアントクレデンシャルグラントを介してトークンを発行する前に、クライアントクレデンシャルグラントクライアントを作成する必要があります。これは、passport:client
Artisan コマンドの --client
オプションを使用して行うことができます。
次に、このグラントタイプを使用するために、CheckClientCredentials
ミドルウェアのミドルウェアエイリアスを登録します。ミドルウェアエイリアスは、アプリケーションの bootstrap/app.php
ファイルで定義できます。
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'client' => CheckClientCredentials::class
]);
})
その後、ミドルウェアをルートにアタッチします。
ルートへのアクセスを特定のスコープに制限するには、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 を試すために便利であったり、一般的なアクセストークン発行のより簡単なアプローチとなる場合があります。
Note
アプリケーションが主に Passport を使用してパーソナルアクセストークンを発行する場合、Laravel Sanctum の使用を検討してください。Laravel の軽量な API アクセストークン発行ライブラリです。
パーソナルアクセスクライアントの作成¶
アプリケーションがパーソナルアクセストークンを発行する前に、パーソナルアクセスクライアントを作成する必要があります。これは、passport:client
Artisan コマンドに --personal
オプションを指定して実行することで行うことができます。passport:install
コマンドを既に実行している場合は、このコマンドを実行する必要はありません。
パーソナルアクセスクライアントを作成した後、クライアントの ID とプレーンテキストのシークレット値をアプリケーションの .env
ファイルに配置します。
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
パーソナルアクセストークンの管理¶
パーソナルアクセスクライアントを作成したら、App\Models\User
モデルインスタンスの createToken
メソッドを使用して、指定されたユーザーのトークンを発行できます。createToken
メソッドは、トークンの名前を最初の引数として受け取り、オプションのスコープの配列を2番目の引数として受け取ります。
use App\Models\User;
$user = User::find(1);
// スコープなしでトークンを作成する...
$token = $user->createToken('Token Name')->accessToken;
// スコープ付きでトークンを作成する...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
JSON API¶
Passport には、パーソナルアクセストークンを管理するための JSON API も含まれています。これにより、パーソナルアクセストークンを管理するためのフロントエンドを構築するための API を使用して、ユーザーと対話することができます。簡単にするために、Passport の JSON API を呼び出すための Vue コンポーネントを提供します。
この Vue コンポーネントを使用するには、resources/js/app.js
ファイルに Passport コンポーネントを登録する必要があります。
GET /oauth/scopes
¶
このルートは、アプリケーションに定義されたすべてのスコープを返します。このルートを使用して、ユーザーがパーソナルアクセストークンに割り当てることができるスコープをリストアップできます。
GET /oauth/personal-access-tokens
¶
このルートは、認証されたユーザーが作成したすべてのパーソナルアクセストークンを返します。これは主に、ユーザーのすべてのトークンをリストアップして、編集または取り消しができるようにするために便利です。
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 => {
// エラーをリストアップ...
});
DELETE /oauth/personal-access-tokens/{token-id}
¶
このルートは、パーソナルアクセストークンを取り消すために使用できます。
ルートの保護¶
ミドルウェア経由¶
Passportには、受信リクエストのアクセストークンを検証する認証ガードが含まれています。api
ガードをpassport
ドライバーを使用するように設定したら、有効なアクセストークンを必要とするルートにauth:api
ミドルウェアを指定するだけです。
Route::get('/user', function () {
// ...
})->middleware('auth:api');
Warning
クライアント認証情報付与を使用している場合は、auth:api
ミドルウェアの代わりにclient
ミドルウェアを使用してルートを保護する必要があります。
複数の認証ガード¶
アプリケーションが完全に異なる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');
Note
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クライアントが注文を行う権限を必要とするわけではありません。代わりに、クライアントが注文の配送状況にアクセスする権限のみをリクエストできるようにすることができます。言い換えれば、スコープにより、アプリケーションのユーザーは、第三者アプリケーションが自分に代わって実行できるアクションを制限できます。
スコープの定義¶
アプリケーションのApp\Providers\AppServiceProvider
クラスのboot
メソッドでPassport::tokensCan
メソッドを使用して、APIのスコープを定義できます。tokensCan
メソッドは、スコープ名とスコープ説明の配列を受け取ります。スコープ説明は、承認承認画面にユーザーに表示されるもので、好きなものを指定できます。
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}
デフォルトスコープ¶
クライアントが特定のスコープをリクエストしない場合、Passportサーバーを設定して、setDefaultScope
メソッドを使用してトークンにデフォルトスコープを添付するようにできます。通常、アプリケーションの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',
]);
Note
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
メソッドを使用してパーソナルアクセストークンを発行する場合、希望するスコープの配列をメソッドの第2引数として渡すことができます。
$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 () {
// アクセストークンに "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']);
トークンインスタンスでのスコープの確認¶
アクセストークンで認証されたリクエストがアプリケーションに入った後、認証された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,
]);
})
Warning
CreateFreshApiToken
ミドルウェアがミドルウェアスタックの最後にリストされていることを確認する必要があります。
このミドルウェアは、送信するレスポンスにlaravel_token
クッキーを添付します。このクッキーには、PassportがJavaScriptアプリケーションからのAPIリクエストを認証するために使用する暗号化されたJWTが含まれています。JWTの有効期間は、session.lifetime
設定値と同じです。現在、ブラウザは自動的にすべての後続リクエストとともにクッキーを送信するため、明示的にアクセストークンを渡すことなく、アプリケーションのAPIにリクエストを行うことができます:
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
クッキー名のカスタマイズ¶
必要に応じて、Passport::cookie
メソッドを使用してlaravel_token
クッキーの名前をカスタマイズできます。通常、このメソッドはアプリケーションのApp\Providers\AppServiceProvider
クラスのboot
メソッドから呼び出す必要があります:
/**
* 任意のアプリケーションサービスのブートストラップ。
*/
public function boot(): void
{
Passport::cookie('custom_name');
}
CSRF保護¶
この認証方法を使用する場合、有効なCSRFトークンヘッダーがリクエストに含まれていることを確認する必要があります。デフォルトのLaravel JavaScriptスキャフォールディングにはAxiosインスタンスが含まれており、暗号化されたXSRF-TOKEN
クッキー値を使用して同一生成元リクエストにX-XSRF-TOKEN
ヘッダーを自動的に送信します。
Note
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('サーバーを作成できる', 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_サーバーを作成できる(): 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('注文を取得できる', 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_注文を取得できる(): void
{
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
}