Laravel Passport

업데이트됨

번역일: 2026년 7월 28일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 7월 28일
번역 갱신
2026년 7월 28일

Laravel Passport

Passport

소개

Laravel Passport는 Laravel 애플리케이션에 완전한 OAuth2 서버 기능을 빠르게 추가할 수 있도록 도와주는 공식 패키지입니다. Passport는 Andy Millington과 Simon Hamp가 관리하는 League OAuth2 server를 기반으로 구축되어 있습니다.

NOTE

이 문서는 독자가 OAuth2의 기본 개념을 이미 알고 있다고 가정합니다. OAuth2가 처음이라면, 계속 읽기 전에 OAuth2 용어 및 개념을 먼저 살펴보시기 바랍니다.

Passport와 Sanctum 중 무엇을 선택할까?

본격적으로 시작하기 전에, 여러분의 애플리케이션에 Laravel Passport와 Laravel Sanctum 중 어느 쪽이 더 적합한지 먼저 판단해 보세요.

반드시 OAuth2 프로토콜을 지원해야 한다면 Laravel Passport를 사용하세요.

반면, SPA(싱글 페이지 애플리케이션), 모바일 앱 인증, 또는 단순한 API 토큰 발급이 목적이라면 **Laravel Sanctum**을 사용하는 것이 좋습니다. Sanctum은 OAuth2를 지원하지 않지만, 훨씬 간단하고 빠르게 API 인증을 구현할 수 있습니다.

NOTE

대부분의 국내 서비스에서는 자체 API 토큰 인증이나 소셜 로그인(카카오, 네이버 등 외부 OAuth 제공자 연동)을 구현할 때 Sanctum으로 충분한 경우가 많습니다. 직접 OAuth2 인가 서버 역할을 해야 하는 경우(예: 외부 서드파티 앱에 OAuth2 토큰을 발급해야 하는 플랫폼 서비스)에 Passport를 선택하세요.

Passport

설치

install:api Artisan 명령어에 --passport 옵션을 붙여 Laravel Passport를 설치합니다:

php artisan install:api --passport

이 명령어는 OAuth2 클라이언트와 액세스 토큰을 저장하는 데 필요한 데이터베이스 마이그레이션을 게시하고 실행합니다. 또한 안전한 액세스 토큰 생성에 필요한 암호화 키도 함께 생성합니다.

명령어 실행이 끝나면 App\Models\User 모델에 Laravel\Passport\HasApiTokens 트레이트와 Laravel\Passport\Contracts\OAuthenticatable 인터페이스를 추가하세요. 이 트레이트는 인증된 사용자의 토큰과 스코프를 확인할 수 있는 헬퍼 메서드를 모델에 제공합니다:

<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Passport\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, HasFactory, Notifiable; }

마지막으로, config/auth.php 설정 파일에서 api 인증 가드를 정의하고 driverpassport로 지정합니다. 이렇게 하면 API 요청 인증 시 Passport의 TokenGuard가 사용됩니다:

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

Passport 배포

서버에 Passport를 처음 배포할 때는 passport:keys 명령어를 실행해야 합니다. 이 명령어는 액세스 토큰 생성에 필요한 암호화 키를 만들어 줍니다. 생성된 키 파일은 일반적으로 소스 컨트롤(Git 등)에 포함하지 않습니다:

php artisan passport:keys

NOTE

키 파일을 Git에 커밋하면 보안상 위험합니다. .gitignore에 키 파일 경로를 추가해 두는 것이 좋습니다.

키를 불러올 경로를 직접 지정하려면 Passport::loadKeysFrom 메서드를 사용하세요. 이 메서드는 보통 App\Providers\AppServiceProviderboot 메서드에서 호출합니다:

/** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); }

환경 변수에서 키 불러오기

키를 파일이 아닌 환경 변수로 관리하고 싶다면, 먼저 Passport 설정 파일을 게시합니다:

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

설정 파일이 게시되면, 아래와 같이 .env 파일(또는 서버의 환경 변수)에 암호화 키를 직접 정의할 수 있습니다:

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-----"

NOTE

AWS나 GitHub Actions처럼 파일 시스템에 키를 저장하기 어려운 환경에서 배포할 때 이 방식이 유용합니다.

Passport 업그레이드

Passport의 메이저 버전을 올릴 때는 반드시 공식 업그레이드 가이드를 꼼꼼히 확인하세요. 마이그레이션이나 설정 파일에 파괴적 변경사항이 포함될 수 있습니다.

설정

토큰 유효 기간

Passport는 기본적으로 1년 후 만료되는 장기 액세스 토큰을 발급합니다. 유효 기간을 늘리거나 줄이려면 tokensExpireIn, refreshTokensExpireIn, personalAccessTokensExpireIn 메서드를 사용하세요. 이 메서드들은 App\Providers\AppServiceProviderboot 메서드에서 호출합니다.

use Carbon\CarbonInterval; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Passport::tokensExpireIn(CarbonInterval::days(15)); Passport::refreshTokensExpireIn(CarbonInterval::days(30)); Passport::personalAccessTokensExpireIn(CarbonInterval::months(6)); }

WARNING

Passport 데이터베이스 테이블의 expires_at 컬럼은 표시 목적으로만 사용되며 읽기 전용입니다. 실제 만료 정보는 서명되고 암호화된 토큰 내부에 저장됩니다. 토큰을 즉시 무효화해야 한다면 expires_at 컬럼을 수정하는 대신 토큰을 폐기하세요.

기본 모델 재정의

Passport가 내부적으로 사용하는 모델을 직접 확장할 수 있습니다. 커스텀 모델 클래스를 만들고 해당 Passport 모델을 상속받으면 됩니다.

use Laravel\Passport\Client as PassportClient; class Client extends PassportClient { // ... }

모델을 정의한 후에는 App\Providers\AppServiceProviderboot 메서드에서 Passport에 커스텀 모델을 사용하도록 지정합니다.

use App\Models\Passport\AuthCode; use App\Models\Passport\Client; use App\Models\Passport\DeviceCode; use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; use Laravel\Passport\Passport; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Passport::useTokenModel(Token::class); Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); Passport::useClientModel(Client::class); Passport::useDeviceCodeModel(DeviceCode::class); }

라우트 재정의

Passport가 등록하는 기본 라우트를 직접 커스터마이징하고 싶을 때가 있습니다. 이 경우 먼저 AppServiceProviderregister 메서드에서 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 라우트... });

Authorization Code Grant (인가 코드 그랜트)

OAuth2에서 가장 널리 사용되는 방식이 바로 인가 코드(Authorization Code) 방식입니다. 클라이언트 애플리케이션이 사용자를 서버로 리다이렉트하면, 사용자가 직접 액세스 토큰 발급을 승인하거나 거부하는 흐름입니다.

시작하려면 먼저 Passport에 "인가(authorization)" 뷰를 반환하는 방법을 알려줘야 합니다.

인가 뷰의 렌더링 로직은 Laravel\Passport\Passport 클래스의 메서드를 통해 자유롭게 커스터마이징할 수 있습니다. 일반적으로 App\Providers\AppServiceProviderboot 메서드에서 설정합니다:

use Inertia\Inertia; use Laravel\Passport\Passport; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { // 뷰 이름을 직접 지정하는 방법... Passport::authorizationView('auth.oauth.authorize'); // 클로저를 사용하는 방법 (Inertia.js 등에 적합)... Passport::authorizationView( fn ($parameters) => Inertia::render('Auth/OAuth/Authorize', [ 'request' => $parameters['request'], 'authToken' => $parameters['authToken'], 'client' => $parameters['client'], 'user' => $parameters['user'], 'scopes' => $parameters['scopes'], ]) ); }

Passport는 이 뷰를 반환하는 /oauth/authorize 라우트를 자동으로 등록합니다. auth.oauth.authorize 템플릿에는 두 개의 폼이 필요합니다.

  • 승인: passport.authorizations.approve 라우트로 POST 요청
  • 거부: passport.authorizations.deny 라우트로 DELETE 요청

두 라우트 모두 state, client_id, auth_token 필드를 필요로 합니다.

클라이언트 관리

API를 연동하려는 외부 개발자는 먼저 자신의 애플리케이션을 클라이언트로 등록해야 합니다. 등록 시에는 애플리케이션 이름과 인가 후 리다이렉트할 URI를 제공합니다.

자사(First-Party) 클라이언트

가장 간단한 클라이언트 생성 방법은 passport:client Artisan 명령어를 사용하는 것입니다. 자사 클라이언트를 만들거나 OAuth2 기능을 테스트할 때 유용합니다. 명령어를 실행하면 Passport가 필요한 정보를 입력받고 클라이언트 ID와 시크릿을 발급해 줍니다:

php artisan passport:client

하나의 클라이언트에 여러 리다이렉트 URI를 허용하려면, URI 입력 시 쉼표로 구분하여 입력하면 됩니다. 쉼표가 포함된 URI는 URI 인코딩해야 합니다:

https://third-party-app.com/callback,https://example.com/oauth/redirect

서드파티(Third-Party) 클라이언트

애플리케이션의 일반 사용자는 passport:client 명령어를 직접 실행할 수 없으므로, Laravel\Passport\ClientRepository 클래스의 createAuthorizationCodeGrantClient 메서드를 사용해 사용자별 클라이언트를 등록할 수 있습니다:

use App\Models\User; use Laravel\Passport\ClientRepository; $user = User::find($userId); // 특정 사용자에게 속하는 OAuth 앱 클라이언트 생성... $client = app(ClientRepository::class)->createAuthorizationCodeGrantClient( user: $user, name: 'Example App', redirectUris: ['https://third-party-app.com/callback'], confidential: false, enableDeviceFlow: true ); // 해당 사용자의 OAuth 앱 클라이언트 목록 조회... $clients = $user->oauthApps()->get();

createAuthorizationCodeGrantClient 메서드는 Laravel\Passport\Client 인스턴스를 반환합니다. $client->id를 클라이언트 ID로, $client->plainSecret을 클라이언트 시크릿으로 사용자에게 표시하면 됩니다.

토큰 요청

인가를 위한 리다이렉트

클라이언트가 생성되면, 개발자는 클라이언트 ID와 시크릿을 사용해 인가 코드와 액세스 토큰을 요청할 수 있습니다. 먼저 소비 애플리케이션(consuming application)에서 /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' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", "login" 중 선택 ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });

prompt 파라미터로 Passport 애플리케이션의 인증 동작 방식을 제어할 수 있습니다:

동작
none사용자가 이미 로그인되어 있지 않으면 항상 인증 오류 반환
consent이전에 모든 스코프를 허용했더라도 항상 인가 승인 화면 표시
login기존 세션이 있어도 항상 재로그인 요구
(생략)요청한 스코프에 대해 이전에 인가하지 않은 경우에만 인가 화면 표시

NOTE

/oauth/authorize 라우트는 Passport가 자동으로 등록합니다. 직접 정의할 필요가 없습니다.

인가 요청 승인

인가 요청을 받으면 Passport는 prompt 파라미터 값에 따라 자동으로 응답하거나, 사용자에게 승인/거부 화면을 표시합니다. 사용자가 승인하면, 클라이언트 생성 시 지정한 redirect_uri로 리다이렉트됩니다. redirect_uri는 반드시 클라이언트 등록 시 지정한 URI와 일치해야 합니다.

자사 클라이언트처럼 인가 화면을 건너뛰고 싶은 경우에는, Client 모델을 확장하여 skipsAuthorization 메서드를 정의하면 됩니다. 이 메서드가 true를 반환하면, 소비 애플리케이션이 명시적으로 prompt 파라미터를 지정하지 않는 한 인가 화면 없이 즉시 redirect_uri로 리다이렉트됩니다:

<?php namespace App\Models\Passport; use Illuminate\Contracts\Auth\Authenticatable; use Laravel\Passport\Client as BaseClient; class Client extends BaseClient { /** * 클라이언트가 인가 화면을 건너뛸지 결정합니다. * * @param \Laravel\Passport\Scope[] $scopes */ public function skipsAuthorization(Authenticatable $user, array $scopes): bool { return $this->firstParty(); } }

인가 코드를 액세스 토큰으로 교환

사용자가 인가를 승인하면 소비 애플리케이션의 콜백 URI로 리다이렉트됩니다. 이때 소비 애플리케이션은 먼저 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, '유효하지 않은 state 값입니다.' ); $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', 'redirect_uri' => 'https://third-party-app.com/callback', 'code' => $request->code, ]); return $response->json(); });

/oauth/token 라우트는 access_token, refresh_token, expires_in 속성을 포함한 JSON을 반환합니다. expires_in은 액세스 토큰이 만료될 때까지 남은 초(seconds)를 나타냅니다.

NOTE

/oauth/authorize와 마찬가지로 /oauth/token 라우트도 Passport가 자동으로 등록합니다. 직접 정의할 필요가 없습니다.

토큰 관리

Laravel\Passport\HasApiTokens 트레이트의 tokens 메서드를 사용하면 사용자의 인가된 토큰 목록을 조회할 수 있습니다. 예를 들어, 사용자에게 서드파티 애플리케이션과의 연결 현황을 보여주는 대시보드를 구현할 때 활용할 수 있습니다:

use App\Models\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Date; use Laravel\Passport\Token; $user = User::find($userId); // 사용자의 유효한 토큰 전체 조회... $tokens = $user->tokens() ->where('revoked', false) ->where('expires_at', '>', Date::now()) ->get(); // 서드파티 OAuth 앱 클라이언트와의 연결 현황 조회... $connections = $tokens->load('client') ->reject(fn (Token $token) => $token->client->firstParty()) ->groupBy('client_id') ->map(fn (Collection $tokens) => [ 'client' => $tokens->first()->client, 'scopes' => $tokens->pluck('scopes')->flatten()->unique()->values()->all(), 'tokens_count' => $tokens->count(), ]) ->values();

토큰 갱신

액세스 토큰의 유효 기간이 짧게 설정된 경우, 사용자는 토큰 발급 시 함께 받은 리프레시 토큰(refresh token)을 사용해 새 액세스 토큰을 발급받아야 합니다:

use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // 기밀(confidential) 클라이언트에만 필요... 'scope' => 'user:read orders:create', ]); return $response->json();

응답 JSON에는 access_token, refresh_token, expires_in 속성이 포함됩니다. expires_in은 새 액세스 토큰의 만료까지 남은 초를 나타냅니다.

토큰 폐기

Laravel\Passport\Token 모델의 revoke 메서드를 사용해 액세스 토큰을 폐기할 수 있습니다. 리프레시 토큰은 Laravel\Passport\RefreshToken 모델의 revoke 메서드로 폐기합니다:

use Laravel\Passport\Passport; use Laravel\Passport\Token; $token = Passport::token()->find($tokenId); // 액세스 토큰 폐기... $token->revoke(); // 리프레시 토큰 폐기... $token->refreshToken?->revoke(); // 사용자의 모든 토큰 폐기... User::find($userId)->tokens()->each(function (Token $token) { $token->revoke(); $token->refreshToken?->revoke(); });

토큰 정리

폐기되거나 만료된 토큰은 데이터베이스에서 주기적으로 삭제하는 것이 좋습니다. Passport에 내장된 passport:purge Artisan 명령어를 사용하면 됩니다:

# 폐기 및 만료된 토큰, 인가 코드, 디바이스 코드 모두 삭제... php artisan passport:purge # 6시간 이상 만료된 토큰만 삭제... php artisan passport:purge --hours=6 <h1 id="code-grant-pkce">폐기된 토큰, 인가 코드, 디바이스 코드만 삭제...</h1> php artisan passport:purge --revoked <h1 id="creating-a-auth-pkce-grant-client">만료된 토큰, 인가 코드, 디바이스 코드만 삭제...</h1> php artisan passport:purge --expired

routes/console.php 파일에 스케줄 작업을 등록해 자동으로 주기적 정리를 수행할 수도 있습니다:

use Illuminate\Support\Facades\Schedule; Schedule::command('passport:purge')->hourly();

PKCE를 활용한 인증 코드 그랜트

PKCE(Proof Key for Code Exchange)를 활용한 인증 코드 그랜트는 단일 페이지 애플리케이션(SPA)이나 모바일 앱이 API에 안전하게 접근할 수 있도록 인증하는 방법입니다. 클라이언트 시크릿을 안전하게 보관할 수 없는 환경이거나, 공격자가 인증 코드를 가로챌 위험을 줄이고 싶을 때 이 방식을 사용합니다.

PKCE 방식은 클라이언트 시크릿 대신 코드 검증기(code verifier)코드 챌린지(code challenge) 조합을 사용하여 인증 코드를 액세스 토큰으로 교환합니다.

클라이언트 생성

PKCE 방식으로 토큰을 발급하려면 먼저 PKCE가 활성화된 클라이언트를 생성해야 합니다. passport:client Artisan 명령에 --public 옵션을 붙여 실행합니다.

php artisan passport:client --public

토큰 요청

코드 검증기와 코드 챌린지

PKCE 방식에는 클라이언트 시크릿이 없으므로, 토큰을 요청하기 전에 코드 검증기와 코드 챌린지를 직접 생성해야 합니다.

코드 검증기RFC 7636 명세에 따라 영문자, 숫자, "-", ".", "_", "~" 문자로 구성된 43~128자의 무작위 문자열이어야 합니다.

코드 챌린지는 코드 검증기를 SHA-256으로 해시한 후 URL·파일명 안전 방식의 Base64로 인코딩한 문자열입니다. 끝의 '=' 패딩 문자는 제거하며, 줄 바꿈이나 공백 등 불필요한 문자가 포함되어서는 안 됩니다.

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

NOTE

코드 검증기는 서버 측 세션에 안전하게 보관했다가, 나중에 토큰 교환 단계에서 그대로 전송해야 합니다.

인증을 위한 리다이렉트

클라이언트를 생성했다면, 클라이언트 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', $codeVerifier = Str::random(128) ); $codeChallenge = strtr(rtrim( base64_encode(hash('sha256', $codeVerifier, true)) , '='), '+/', '-_'); $query = http_build_query([ 'client_id' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', 'state' => $state, 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', // 'prompt' => '', // "none", "consent", "login" 중 선택 ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });

인증 코드를 액세스 토큰으로 교환

사용자가 권한 요청을 승인하면 지정한 redirect_uri로 리다이렉트됩니다. 이때 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('https://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', 'client_id' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'code_verifier' => $codeVerifier, 'code' => $request->code, ]); return $response->json(); });

서버는 수신한 코드 검증기를 동일한 방식으로 해시하여 최초 전달받은 코드 챌린지와 비교합니다. 두 값이 일치하면 액세스 토큰이 발급됩니다. 클라이언트 시크릿 없이도 중간자 공격을 방지할 수 있는 것이 PKCE의 핵심입니다.

디바이스 인증 그랜트 (Device Authorization Grant)

OAuth2 디바이스 인증 그랜트는 브라우저가 없거나 입력 수단이 제한된 디바이스(TV, 게임 콘솔 등)에서 "디바이스 코드"를 교환하여 액세스 토큰을 발급받을 수 있도록 하는 방식입니다.

이 방식의 흐름은 다음과 같습니다. 디바이스가 서버에 디바이스 코드를 요청하면, 사용자는 스마트폰이나 PC 같은 보조 디바이스에서 서버에 접속해 "사용자 코드(user code)"를 입력하고 인증 요청을 승인하거나 거부합니다.

시작하려면 먼저 Passport에 "사용자 코드" 뷰와 "인증" 뷰를 반환하는 방법을 알려줘야 합니다.

뷰 렌더링 로직은 Laravel\Passport\Passport 클래스의 메서드로 커스터마이즈할 수 있습니다. 일반적으로 App\Providers\AppServiceProviderboot 메서드에서 아래처럼 설정합니다.

use Inertia\Inertia; use Laravel\Passport\Passport; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { // 뷰 이름으로 지정하는 방법... Passport::deviceUserCodeView('auth.oauth.device.user-code'); Passport::deviceAuthorizationView('auth.oauth.device.authorize'); // 클로저로 지정하는 방법... Passport::deviceUserCodeView( fn ($parameters) => Inertia::render('Auth/OAuth/Device/UserCode') ); Passport::deviceAuthorizationView( fn ($parameters) => Inertia::render('Auth/OAuth/Device/Authorize', [ 'request' => $parameters['request'], 'authToken' => $parameters['authToken'], 'client' => $parameters['client'], 'user' => $parameters['user'], 'scopes' => $parameters['scopes'], ]) ); // ... }

Passport는 이 뷰를 반환하는 라우트를 자동으로 정의합니다. 각 템플릿에서 다음 사항을 지켜야 합니다.

  • auth.oauth.device.user-code 템플릿: passport.device.authorizations.authorize 라우트로 GET 요청을 보내는 폼을 포함해야 하며, 이 라우트는 user_code 쿼리 파라미터를 필요로 합니다.
  • auth.oauth.device.authorize 템플릿: 인증 승인을 위한 POST 요청(passport.device.authorizations.approve 라우트)과 거부를 위한 DELETE 요청(passport.device.authorizations.deny 라우트)을 보내는 폼을 각각 포함해야 합니다. 두 라우트 모두 state, client_id, auth_token 필드를 요구합니다.

디바이스 인증 그랜트 클라이언트 생성

디바이스 인증 그랜트로 토큰을 발급하려면 먼저 디바이스 플로우가 활성화된 클라이언트를 생성해야 합니다. passport:client Artisan 명령에 --device 옵션을 붙여 실행하면 퍼스트파티 클라이언트가 생성되고, 클라이언트 ID와 시크릿이 발급됩니다.

php artisan passport:client --device

서드파티 클라이언트를 특정 사용자에게 연결해서 등록하려면 ClientRepositorycreateDeviceAuthorizationGrantClient 메서드를 사용하세요.

use App\Models\User; use Laravel\Passport\ClientRepository; $user = User::find($userId); $client = app(ClientRepository::class)->createDeviceAuthorizationGrantClient( user: $user, name: '예시 디바이스', confidential: false, );

토큰 요청

디바이스 코드 요청

클라이언트가 생성되면, 디바이스는 클라이언트 ID를 사용해 애플리케이션에 디바이스 코드를 요청합니다. /oauth/device/code 라우트로 POST 요청을 보내면 됩니다.

use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/device/code', [ 'client_id' => 'your-client-id', 'scope' => 'user:read orders:create', ]); return $response->json();

응답은 다음 속성을 포함하는 JSON입니다.

속성설명
device_code디바이스 코드
user_code사용자가 입력할 코드
verification_uri사용자가 접속할 URL
expires_in디바이스 코드 만료까지의 초(second)
interval폴링 최소 간격(초). 이 값보다 짧게 폴링하면 속도 제한 오류가 발생합니다.

NOTE

/oauth/device/code 라우트는 Passport가 자동으로 정의합니다. 별도로 라우트를 추가할 필요가 없습니다.

인증 URI 및 사용자 코드 안내

디바이스 코드를 발급받은 후, 디바이스는 사용자에게 verification_uri에 접속해 user_code를 입력하도록 안내해야 합니다. 예를 들어 TV 화면에 "https://example.com/device 에 접속하여 코드 ABCD-1234를 입력하세요."와 같이 표시합니다.

토큰 폴링 요청

사용자가 별도의 디바이스에서 승인 또는 거부 작업을 완료할 때까지, 디바이스는 /oauth/token 라우트를 주기적으로 폴링하여 처리 결과를 확인해야 합니다. 이때 디바이스 코드 응답에 포함된 interval 값 이상의 간격을 반드시 지켜야 속도 제한 오류를 피할 수 있습니다.

use Illuminate\Support\Facades\Http; use Illuminate\Support\Sleep; $interval = 5; do { Sleep::for($interval)->seconds(); $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // 기밀(confidential) 클라이언트에만 필요 'device_code' => 'the-device-code', ]); // slow_down 오류 시 폴링 간격을 5초 늘립니다. if ($response->json('error') === 'slow_down') { $interval += 5; } } while (in_array($response->json('error'), ['authorization_pending', 'slow_down'])); return $response->json();

사용자가 인증 요청을 승인하면, 응답 JSON에 access_token, refresh_token, expires_in 속성이 포함됩니다. expires_in은 액세스 토큰이 만료되기까지의 시간(초)입니다.

NOTE

폴링 중 authorization_pending은 사용자가 아직 응답하지 않았음을, slow_down은 폴링 간격을 늘려야 함을 의미합니다. 루프가 종료되면 성공 응답이거나 그 외 오류 상황입니다.

패스워드 그랜트 (Password Grant)

WARNING

패스워드 그랜트 토큰 사용은 더 이상 권장하지 않습니다. 대신 OAuth2 Server에서 현재 권장하는 그랜트 타입을 선택하세요.

OAuth2 패스워드 그랜트는 모바일 앱과 같은 자사(first-party) 클라이언트가 이메일 주소/사용자명과 비밀번호를 이용해 액세스 토큰을 발급받을 수 있게 해줍니다. OAuth2 인증 코드 리다이렉트 플로우 전체를 거치지 않아도 되므로, 자사 클라이언트에 안전하게 토큰을 발급할 수 있습니다.

패스워드 그랜트를 활성화하려면 App\Providers\AppServiceProviderboot 메서드에서 enablePasswordGrant 메서드를 호출하세요:

/** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Passport::enablePasswordGrant(); }

패스워드 그랜트 클라이언트 생성

패스워드 그랜트로 토큰을 발급하려면 먼저 패스워드 그랜트 클라이언트를 생성해야 합니다. passport:client Artisan 명령에 --password 옵션을 붙여 실행하세요:

php artisan passport:client --password

토큰 요청

그랜트를 활성화하고 클라이언트를 생성했다면, 사용자의 이메일과 비밀번호를 담아 /oauth/token 라우트에 POST 요청을 보내면 액세스 토큰을 발급받을 수 있습니다. 이 라우트는 Passport가 자동으로 등록하므로 별도로 정의할 필요가 없습니다. 요청이 성공하면 서버의 JSON 응답에서 access_tokenrefresh_token을 받게 됩니다:

use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // 기밀 클라이언트(confidential client)에만 필요합니다... 'username' => 'user@example.com', 'password' => 'my-password', 'scope' => 'user:read orders:create', ]); return $response->json();

NOTE

액세스 토큰은 기본적으로 유효 기간이 깁니다. 필요하다면 최대 액세스 토큰 유효 기간 설정을 통해 조정할 수 있습니다.

모든 스코프 요청

패스워드 그랜트 또는 클라이언트 자격 증명 그랜트를 사용할 때, 애플리케이션이 지원하는 모든 스코프에 대한 권한을 토큰에 부여하고 싶다면 scope 값으로 *를 요청하면 됩니다. * 스코프가 지정된 토큰의 can 메서드는 항상 true를 반환합니다. 이 스코프는 password 또는 client_credentials 그랜트로 발급된 토큰에만 할당할 수 있습니다:

use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // 기밀 클라이언트(confidential client)에만 필요합니다... 'username' => 'user@example.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\Bridge\Client; use Laravel\Passport\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, Notifiable; /** * 주어진 사용자명으로 유저 인스턴스를 찾습니다. */ public function findForPassport(string $username, Client $client): 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\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, Notifiable; /** * Passport 패스워드 그랜트에서 사용자의 비밀번호를 검증합니다. */ public function validateForPassportPasswordGrant(string $password): bool { return Hash::check($password, $this->password); } }

Implicit Grant (암묵적 권한 부여)

WARNING

암묵적 권한 부여(Implicit Grant) 방식은 더 이상 권장하지 않습니다. 대신 OAuth2 서버에서 현재 권장하는 권한 부여 방식을 사용하세요.

암묵적 권한 부여 방식은 인가 코드 방식과 유사하지만, 인가 코드를 교환하는 단계 없이 액세스 토큰이 클라이언트로 직접 반환된다는 점이 다릅니다. 이 방식은 클라이언트 자격 증명을 안전하게 보관하기 어려운 JavaScript 앱이나 모바일 앱에서 주로 사용됩니다.

이 방식을 활성화하려면 App\Providers\AppServiceProvider 클래스의 boot 메서드에서 enableImplicitGrant 메서드를 호출하세요:

/** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Passport::enableImplicitGrant(); }

암묵적 권한 부여를 통해 토큰을 발급하려면, 먼저 암묵적 클라이언트를 생성해야 합니다. passport:client Artisan 명령에 --implicit 옵션을 붙여 실행하세요:

php artisan passport:client --implicit

클라이언트가 생성되면, 해당 클라이언트 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' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'token', 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", "login" 중 하나 ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });

NOTE

/oauth/authorize 라우트는 Passport가 자동으로 등록합니다. 직접 정의할 필요가 없습니다.

클라이언트 자격 증명 그랜트

클라이언트 자격 증명 그랜트는 머신 간(machine-to-machine) 인증에 적합합니다. 예를 들어, API를 통해 유지 보수 작업을 수행하는 스케줄된 Job에서 이 그랜트를 활용할 수 있습니다.

이 방식으로 토큰을 발급하려면 먼저 클라이언트 자격 증명 그랜트용 클라이언트를 생성해야 합니다. passport:client Artisan 명령에 --client 옵션을 붙여 실행하세요:

php artisan passport:client --client

그런 다음, Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner 미들웨어를 라우트에 적용합니다:

use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner; Route::get('/orders', function (Request $request) { // 액세스 토큰이 유효하고, 클라이언트가 리소스 소유자입니다... })->middleware(EnsureClientIsResourceOwner::class);

특정 스코프를 가진 클라이언트만 라우트에 접근하도록 제한하려면 using 메서드에 필요한 스코프 목록을 전달하세요:

Route::get('/orders', function (Request $request) { // 액세스 토큰이 유효하고, 클라이언트가 리소스 소유자이며, "servers:read"와 "servers:create" 스코프를 모두 보유합니다... })->middleware(EnsureClientIsResourceOwner::using('servers:read', 'servers:create'));

WARNING

기반 OAuth2 서버는 클라이언트 자격 증명 토큰의 sub 클레임을 클라이언트 식별자로 설정합니다. 기본적으로 Passport는 클라이언트에 UUID를 사용하므로, 사용자의 정수형 기본 키와 충돌할 가능성이 없습니다. 그러나 Passport::$clientUuidsfalse로 설정한 경우, 클라이언트 ID와 동일한 ID를 가진 사용자가 의도치 않게 조회될 수 있습니다. 이 경우 이 미들웨어만으로는 수신된 토큰이 클라이언트 자격 증명 토큰임을 보장할 수 없습니다.

토큰 조회

이 그랜트 타입으로 토큰을 발급받으려면 oauth/token 엔드포인트에 다음과 같이 요청합니다:

use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'client_credentials', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', 'scope' => 'servers:read servers:create', ]); return $response->json()['access_token'];

Passport

개인 액세스 토큰 (Personal Access Tokens)

사용자가 일반적인 인가 코드 리디렉션 흐름을 거치지 않고 직접 액세스 토큰을 발급받고 싶을 때가 있습니다. 애플리케이션 UI를 통해 사용자가 직접 토큰을 발급할 수 있도록 허용하면, API를 간편하게 테스트하거나 보다 단순한 방식으로 토큰을 발급하는 용도로 활용할 수 있습니다.

NOTE

애플리케이션에서 Passport를 주로 개인 액세스 토큰 발급 목적으로만 사용한다면, Laravel의 경량 공식 라이브러리인 Laravel Sanctum 사용을 고려해 보세요.

개인 액세스 클라이언트 생성

개인 액세스 토큰을 발급하려면 먼저 개인 액세스 클라이언트를 생성해야 합니다. passport:client Artisan 명령에 --personal 옵션을 붙여 실행하면 됩니다. 이미 passport:install 명령을 실행했다면 이 단계는 건너뛰어도 됩니다.

php artisan passport:client --personal

사용자 프로바이더 커스터마이징

애플리케이션에서 인증 사용자 프로바이더를 여러 개 사용하는 경우, 개인 액세스 클라이언트가 어떤 프로바이더를 사용할지 --provider 옵션으로 지정할 수 있습니다.

php artisan passport:client --personal --provider=users

지정하는 프로바이더 이름은 config/auth.php에 정의된 유효한 프로바이더여야 합니다. 이후 미들웨어를 통해 라우트를 보호하면, 해당 가드의 프로바이더에 속한 사용자만 접근할 수 있도록 제한할 수 있습니다.

개인 액세스 토큰 관리

개인 액세스 클라이언트를 생성했다면, App\Models\User 모델 인스턴스의 createToken 메서드를 사용해 특정 사용자에게 토큰을 발급할 수 있습니다. createToken의 첫 번째 인수는 토큰 이름이고, 두 번째 인수로 스코프 배열을 선택적으로 전달할 수 있습니다.

use App\Models\User; use Illuminate\Support\Facades\Date; use Laravel\Passport\Token; $user = User::find($userId); // 스코프 없이 토큰 생성... $token = $user->createToken('내 토큰')->accessToken; // 특정 스코프를 지정하여 토큰 생성... $token = $user->createToken('내 토큰', ['user:read', 'orders:create'])->accessToken; // 모든 스코프를 허용하는 토큰 생성... $token = $user->createToken('내 토큰', ['*'])->accessToken; // 해당 사용자의 유효한 개인 액세스 토큰 목록 조회... $tokens = $user->tokens() ->with('client') ->where('revoked', false) ->where('expires_at', '>', Date::now()) ->get() ->filter(fn (Token $token) => $token->client->hasGrantType('personal_access'));

라우트 보호

미들웨어를 통한 보호

Passport는 들어오는 요청의 액세스 토큰을 검증하는 인증 가드를 포함하고 있습니다. api 가드를 passport 드라이버로 설정했다면, 유효한 액세스 토큰을 요구하는 라우트에 auth:api 미들웨어를 지정하기만 하면 됩니다.

Route::get('/user', function () { // 인증된 API 사용자만 이 라우트에 접근할 수 있습니다... })->middleware('auth:api');

WARNING

클라이언트 자격증명 그랜트(Client Credentials Grant)를 사용하는 경우, auth:api 미들웨어 대신 Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner 미들웨어로 라우트를 보호해야 합니다.

복수의 인증 가드 사용

애플리케이션이 서로 다른 Eloquent 모델을 사용하는 여러 유형의 사용자를 인증해야 하는 경우, 각 사용자 프로바이더 유형에 맞는 가드 설정을 별도로 정의해야 할 수 있습니다. 예를 들어, 일반 회원(users)과 기업 고객(customers)을 구분해 관리하는 구조라면 config/auth.php에 다음과 같이 가드를 설정할 수 있습니다.

'guards' => [ '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 토큰 형식으로 포함해야 합니다. 예를 들어, Laravel의 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();

Passport - 토큰 스코프

목차


토큰 스코프

스코프(scope)는 API 클라이언트가 특정 권한만 요청할 수 있도록 제한하는 기능입니다. 예를 들어 쇼핑몰 애플리케이션을 개발한다면, 모든 API 소비자가 주문을 생성할 권한까지 필요한 것은 아닙니다. 배송 상태 조회 권한만 허용하는 식으로 세분화할 수 있죠. 즉, 스코프를 통해 사용자는 서드파티 앱이 자신을 대신해 수행할 수 있는 작업의 범위를 직접 제어할 수 있습니다.

스코프 정의하기

스코프는 App\Providers\AppServiceProviderboot 메서드에서 Passport::tokensCan 메서드를 통해 정의합니다. 이 메서드는 스코프 이름을 키로, 설명을 값으로 갖는 배열을 받습니다. 설명 문구는 사용자가 인가 승인 화면에서 직접 확인하게 되므로, 이해하기 쉽게 작성하는 것이 좋습니다.

/** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Passport::tokensCan([ 'user:read' => '사용자 정보 조회', 'orders:create' => '주문 생성', 'orders:read:status' => '주문 배송 상태 확인', ]); }

기본 스코프

클라이언트가 스코프를 별도로 지정하지 않은 경우, Passport가 토큰에 자동으로 적용할 기본 스코프를 defaultScopes 메서드로 설정할 수 있습니다. 마찬가지로 AppServiceProviderboot 메서드에서 호출하면 됩니다.

use Laravel\Passport\Passport; Passport::tokensCan([ 'user:read' => '사용자 정보 조회', 'orders:create' => '주문 생성', 'orders:read:status' => '주문 배송 상태 확인', ]); Passport::defaultScopes([ 'user:read', 'orders:create', ]);

토큰에 스코프 할당하기

인가 코드 요청 시

인가 코드 그랜트 방식으로 액세스 토큰을 요청할 때, 클라이언트는 scope 쿼리 파라미터에 원하는 스코프를 공백으로 구분하여 전달해야 합니다.

Route::get('/redirect', function () { $query = http_build_query([ 'client_id' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });

퍼스널 액세스 토큰 발급 시

App\Models\User 모델의 createToken 메서드로 퍼스널 액세스 토큰을 발급할 때는, 두 번째 인수로 스코프 배열을 전달합니다.

$token = $user->createToken('내 토큰', ['orders:create'])->accessToken;

스코프 확인하기

Passport는 요청에 포함된 액세스 토큰의 스코프를 검증하기 위한 두 가지 미들웨어를 제공합니다.

모든 스코프 확인

Laravel\Passport\Http\Middleware\CheckToken 미들웨어는 액세스 토큰이 지정한 모든 스코프를 가지고 있는지 확인합니다.

use Laravel\Passport\Http\Middleware\CheckToken; Route::get('/orders', function () { // 액세스 토큰이 "orders:read"와 "orders:create" 스코프를 모두 보유한 경우에만 접근 가능 })->middleware(['auth:api', CheckToken::using('orders:read', 'orders:create')]);

Laravel\Passport\Http\Middleware\CheckTokenForAnyScope 미들웨어는 액세스 토큰이 지정한 스코프 중 하나 이상을 가지고 있으면 요청을 허용합니다.

use Laravel\Passport\Http\Middleware\CheckTokenForAnyScope; Route::get('/orders', function () { // 액세스 토큰이 "orders:read" 또는 "orders:create" 중 하나라도 보유한 경우 접근 가능 })->middleware(['auth:api', CheckTokenForAnyScope::using('orders:read', 'orders:create')]);

스코프 어트리뷰트

컨트롤러 미들웨어 어트리뷰트를 활용하고 있다면, Laravel\Passport\Attributes\AuthorizeToken PHP 어트리뷰트를 사용해 더 간결하게 스코프 미들웨어를 적용할 수 있습니다.

<?php namespace App\Http\Controllers; use Laravel\Passport\Attributes\AuthorizeToken; #[AuthorizeToken('orders:read')] #[AuthorizeToken('orders:create', only: ['store'])] class OrderController { #[AuthorizeToken(['orders:read', 'orders:create'], anyScope: true)] public function index() { // 액세스 토큰이 "orders:read" 또는 "orders:create" 중 하나라도 보유한 경우 접근 가능 } public function store() { // 액세스 토큰이 "orders:read"와 "orders:create" 스코프를 모두 보유한 경우에만 접근 가능 } }

기본적으로 AuthorizeToken 어트리뷰트는 지정된 스코프를 모두 요구합니다. anyScope: true를 전달하면 지정된 스코프 중 하나 이상만 있어도 인가됩니다.

NOTE

클래스 레벨에 선언한 어트리뷰트는 모든 메서드에 적용되며, 메서드 레벨 어트리뷰트는 해당 메서드에만 적용됩니다. only 옵션을 사용하면 특정 메서드에만 적용 범위를 제한할 수 있습니다.

토큰 인스턴스에서 스코프 확인

액세스 토큰으로 인증된 요청이 애플리케이션 내부로 진입한 이후에도, 인증된 App\Models\User 인스턴스의 tokenCan 메서드를 통해 토큰의 스코프를 직접 확인할 수 있습니다.

use Illuminate\Http\Request; Route::get('/orders', function (Request $request) { if ($request->user()->tokenCan('orders:create')) { // 주문 생성 스코프가 있는 경우 처리 } });

추가 스코프 메서드

Passport는 스코프 정보를 조회하기 위한 다양한 헬퍼 메서드도 제공합니다.

정의된 모든 스코프의 ID(이름) 목록을 배열로 반환합니다.

use Laravel\Passport\Passport; Passport::scopeIds();

정의된 모든 스코프를 Laravel\Passport\Scope 인스턴스 배열로 반환합니다.

Passport::scopes();

지정한 ID(이름)에 해당하는 Laravel\Passport\Scope 인스턴스 배열을 반환합니다.

Passport::scopesFor(['user:read', 'orders:create']);

특정 스코프가 정의되어 있는지 여부를 확인합니다.

Passport::hasScope('orders:create');

SPA 인증

JavaScript 애플리케이션(SPA)에서 자체 API를 직접 호출해야 하는 경우가 많습니다. 이 방식을 사용하면 외부에 공개한 API와 동일한 API를 자체 웹 애플리케이션, 모바일 앱, 서드파티, SDK 등에서 공통으로 활용할 수 있습니다.

일반적으로 JavaScript에서 API를 호출하려면 액세스 토큰을 수동으로 관리하고 매 요청마다 헤더에 포함해야 합니다. 하지만 Passport는 이 과정을 자동으로 처리해 주는 미들웨어를 제공합니다. bootstrap/app.php 파일의 web 미들웨어 그룹에 CreateFreshApiToken 미들웨어를 추가하면 됩니다:

use Laravel\Passport\Http\Middleware\CreateFreshApiToken; ->withMiddleware(function (Middleware $middleware): void { $middleware->web(append: [ CreateFreshApiToken::class, ]); })

WARNING

CreateFreshApiToken 미들웨어는 반드시 미들웨어 스택의 마지막에 위치해야 합니다.

이 미들웨어는 응답에 laravel_token 쿠키를 자동으로 첨부합니다. 이 쿠키에는 암호화된 JWT가 담겨 있으며, Passport는 이를 이용해 JavaScript 애플리케이션의 API 요청을 인증합니다. 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 인스턴스가 포함되어 있으며, 동일 출처(same-origin) 요청 시 암호화된 XSRF-TOKEN 쿠키 값을 읽어 X-XSRF-TOKEN 헤더를 자동으로 전송합니다.

NOTE

X-XSRF-TOKEN 대신 X-CSRF-TOKEN 헤더를 직접 전송하려면 csrf_token()이 반환하는 암호화되지 않은 토큰을 사용해야 합니다.

Passport

이벤트

Passport는 액세스 토큰과 리프레시 토큰을 발급할 때 이벤트를 발생시킵니다. 이 이벤트들을 리스닝하면 데이터베이스에서 다른 액세스 토큰을 정리하거나 폐기하는 처리를 추가할 수 있습니다.

이벤트 이름
Laravel\Passport\Events\AccessTokenCreated
Laravel\Passport\Events\AccessTokenRevoked
Laravel\Passport\Events\RefreshTokenCreated

Passport — 테스트

테스트

Passport는 테스트 환경에서 인증된 사용자를 손쉽게 시뮬레이션할 수 있도록 actingAs 메서드를 제공합니다. 첫 번째 인자에 사용자 인스턴스를, 두 번째 인자에 해당 토큰에 부여할 스코프 배열을 전달합니다.

Pest

use App\Models\User; use Laravel\Passport\Passport; test('주문을 생성할 수 있다', function () { Passport::actingAs( User::factory()->create(), ['orders:create'] ); $response = $this->post('/api/orders'); $response->assertStatus(201); });

PHPUnit

use App\Models\User; use Laravel\Passport\Passport; public function test_orders_can_be_created(): void { Passport::actingAs( User::factory()->create(), ['orders:create'] ); $response = $this->post('/api/orders'); $response->assertStatus(201); }

클라이언트 자격증명 방식으로 인증된 요청을 테스트할 때는 actingAsClient 메서드를 사용합니다. 첫 번째 인자에 클라이언트 인스턴스를, 두 번째 인자에 해당 토큰에 부여할 스코프 배열을 전달합니다.

Pest

use Laravel\Passport\Client; use Laravel\Passport\Passport; test('서버 목록을 조회할 수 있다', function () { Passport::actingAsClient( Client::factory()->create(), ['servers:read'] ); $response = $this->get('/api/servers'); $response->assertStatus(200); });

PHPUnit

use Laravel\Passport\Client; use Laravel\Passport\Passport; public function test_servers_can_be_retrieved(): void { Passport::actingAsClient( Client::factory()->create(), ['servers:read'] ); $response = $this->get('/api/servers'); $response->assertStatus(200); }

NOTE

actingAs는 사용자(User) 기반의 OAuth 토큰 인증을 흉내 내고, actingAsClient는 클라이언트 자격증명(Client Credentials) 방식의 인증을 흉내 냅니다. 두 메서드 모두 실제 토큰을 발급하지 않으므로 테스트 속도가 빠르고 외부 의존성 없이 동작합니다.

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 7월 28일