본문 바로가기

Laravel Passport

번역일: 2026년 6월 21일

Laravel Passport

소개

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

WARNING

이 문서는 OAuth2에 대한 기본 지식이 있다고 가정합니다. OAuth2가 처음이라면 공식 용어집과 주요 개념을 먼저 살펴보는 것을 권장합니다.

Passport vs Sanctum

시작하기 전에, 프로젝트에 Passport와 Laravel Sanctum 중 어느 쪽이 더 적합한지 먼저 고민해 보세요.

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

반면, SPA(싱글 페이지 애플리케이션), 모바일 앱, 또는 단순한 API 토큰 발급이 목적이라면 Laravel Sanctum이 더 나은 선택입니다. Sanctum은 OAuth2를 지원하지 않지만, 훨씬 간단한 API 인증 개발 경험을 제공합니다.

NOTE

정리하면: 외부 서드파티 앱이 내 API에 접근해야 하는 표준 OAuth2 플로우가 필요하면 Passport, 내 앱 자체의 인증(SPA, 앱 토큰 등)이 목적이면 Sanctum을 선택하세요.

설치

install:api Artisan 명령어로 Passport를 설치합니다:

php artisan install:api --passport

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

명령어 실행 중, Passport Client 모델의 기본 키를 자동 증가 정수 대신 UUID로 사용할지 묻는 질문이 표시됩니다.

설치가 완료되면 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 인증 가드의 driverpassport로 설정합니다. 이렇게 하면 API 요청 인증 시 Passport의 TokenGuard가 사용됩니다:

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

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

Passport 배포

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

php artisan passport:keys

필요하다면 Passport 키를 로드할 경로를 직접 지정할 수 있습니다. Passport::loadKeysFrom 메서드를 사용하며, App\Providers\AppServiceProviderboot 메서드에서 호출하는 것이 일반적입니다:

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

환경 변수에서 키 로드하기

또는 vendor:publish 명령어로 Passport 설정 파일을 퍼블리시한 뒤:

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

환경 변수로 암호화 키를 설정할 수 있습니다:

PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- <여기에 개인 키 입력> -----END RSA PRIVATE KEY-----" PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY----- <여기에 공개 키 입력> -----END PUBLIC KEY-----"

Passport 업그레이드

Passport의 새로운 메이저 버전으로 업그레이드할 때는 반드시 업그레이드 가이드를 꼼꼼히 확인하세요.

설정

클라이언트 시크릿 해싱

데이터베이스에 저장되는 클라이언트 시크릿을 해싱하려면 App\Providers\AppServiceProviderboot 메서드에서 Passport::hashClientSecrets를 호출합니다:

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

이 옵션을 활성화하면 클라이언트 시크릿은 생성 직후 한 번만 평문으로 확인할 수 있습니다. 평문 값은 데이터베이스에 저장되지 않으므로 분실 시 복구가 불가능합니다.

토큰 유효 기간

기본적으로 Passport는 1년 후 만료되는 장기 액세스 토큰을 발급합니다. 유효 기간을 조정하려면 tokensExpireIn, refreshTokensExpireIn, personalAccessTokensExpireIn 메서드를 사용하세요. 이 메서드들도 AppServiceProviderboot 메서드에서 호출합니다:

/** * 애플리케이션 서비스를 부트스트랩합니다. */ 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 모델을 상속하여 자신만의 모델을 정의하세요:

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

모델을 정의한 후, AppServiceProviderboot 메서드에서 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; /** * 애플리케이션 서비스를 부트스트랩합니다. */ 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가 등록하는 라우트를 커스터마이징하려면 먼저 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)를 통한 OAuth2는 가장 널리 사용되는 방식입니다. 클라이언트 애플리케이션이 사용자를 서버로 리다이렉트하면, 사용자는 클라이언트에 액세스 토큰을 발급할지 승인하거나 거부합니다.

클라이언트 관리

애플리케이션 API를 사용하려는 개발자는 먼저 자신의 애플리케이션을 "클라이언트"로 등록해야 합니다. 일반적으로 애플리케이션 이름과 인가 승인 후 리다이렉트할 URL을 제공합니다.

`passport:client` 명령어

클라이언트를 생성하는 가장 간단한 방법은 passport:client Artisan 명령어를 사용하는 것입니다. 개발 중 OAuth2 기능을 테스트할 때 유용합니다. 명령어를 실행하면 클라이언트 정보를 입력하라는 안내가 표시되고, 클라이언트 ID와 시크릿이 발급됩니다:

php artisan passport:client

복수의 리다이렉트 URL

클라이언트에 여러 리다이렉트 URL을 허용하려면 명령어 실행 중 URL을 입력할 때 쉼표로 구분하여 입력하면 됩니다. 쉼표가 포함된 URL은 URL 인코딩이 필요합니다:

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

JSON API

사용자는 passport:client 명령어에 직접 접근할 수 없으므로, Passport는 클라이언트 생성·수정·삭제를 위한 JSON API를 제공합니다. 이를 프론트엔드와 연동하면 사용자가 직접 클라이언트를 관리하는 대시보드를 구축할 수 있습니다.

아래 예시에서는 편의상 Axios를 사용합니다. 이 JSON API는 webauth 미들웨어로 보호되므로 자체 애플리케이션 내에서만 호출할 수 있습니다.

`GET /oauth/clients`

인증된 사용자의 모든 클라이언트를 반환합니다:

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

`POST /oauth/clients`

새 클라이언트를 생성합니다. nameredirect URL이 필요합니다:

const data = { name: '내 애플리케이션', redirect: 'http://example.com/callback' }; axios.post('/oauth/clients', data) .then(response => { console.log(response.data); }) .catch(response => { // 오류 처리... });

`PUT /oauth/clients/{client-id}`

클라이언트를 수정합니다. nameredirect URL이 필요합니다:

const data = { name: '수정된 애플리케이션 이름', redirect: 'http://example.com/callback' }; axios.put('/oauth/clients/' + clientId, data) .then(response => { console.log(response.data); }) .catch(response => { // 오류 처리... });

`DELETE /oauth/clients/{client-id}`

클라이언트를 삭제합니다:

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

토큰 요청

인가를 위한 리다이렉트

클라이언트가 생성되면, 클라이언트 ID와 시크릿을 사용하여 인가 코드와 액세스 토큰을 요청할 수 있습니다. 먼저 소비 애플리케이션은 /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", "login" 중 선택 ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); });

prompt 파라미터로 인증 동작 방식을 지정할 수 있습니다:

  • none: 이미 로그인되어 있지 않으면 항상 인증 오류를 발생시킵니다.
  • consent: 이전에 이미 모든 스코프를 승인한 경우에도 항상 인가 승인 화면을 표시합니다.
  • login: 기존 세션이 있더라도 항상 다시 로그인하도록 요청합니다.

prompt를 지정하지 않으면, 해당 스코프에 대해 이전에 인가하지 않은 경우에만 승인 화면이 표시됩니다.

NOTE

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

요청 승인

인가 요청을 받으면 Passport는 prompt 파라미터 값에 따라 자동으로 처리하거나, 사용자에게 승인/거부 화면을 표시합니다. 사용자가 승인하면 클라이언트 생성 시 지정한 redirect_uri로 리다이렉트됩니다.

승인 화면을 커스터마이징하려면 vendor:publish 명령어로 Passport 뷰를 퍼블리시하세요. 퍼블리시된 뷰는 resources/views/vendor/passport 디렉터리에 위치합니다:

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

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

<?php namespace App\Models\Passport; use Laravel\Passport\Client as BaseClient; class Client extends BaseClient { /** * 클라이언트가 인가 승인 화면을 건너뛸지 결정합니다. */ public function skipsAuthorization(): bool { return $this->firstParty(); } }

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

사용자가 인가 요청을 승인하면 소비 애플리케이션으로 리다이렉트됩니다. 이때 소비 애플리케이션은 먼저 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('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은 액세스 토큰이 만료될 때까지 남은 초(seconds)입니다.

NOTE

/oauth/token 라우트 역시 Passport가 자동으로 등록합니다. 별도로 정의할 필요가 없습니다.

JSON API

Passport는 인가된 액세스 토큰을 관리하기 위한 JSON API도 제공합니다. 이를 프론트엔드와 연동하면 사용자가 액세스 토큰을 관리하는 대시보드를 구축할 수 있습니다. 이 API 역시 webauth 미들웨어로 보호됩니다.

`GET /oauth/tokens`

인증된 사용자가 생성한 모든 인가 액세스 토큰을 반환합니다:

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

`DELETE /oauth/tokens/{token-id}`

인가 액세스 토큰과 연관된 리프레시 토큰을 폐기합니다:

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

토큰 갱신

단기 액세스 토큰을 사용하는 경우, 사용자는 액세스 토큰 발급 시 함께 제공된 리프레시 토큰을 통해 새 토큰을 받아야 합니다:

use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => '리프레시-토큰-값', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'scope' => '', ]); return $response->json();

응답에는 access_token, refresh_token, expires_in 속성이 포함됩니다.

토큰 폐기

Laravel\Passport\TokenRepositoryrevokeAccessToken 메서드로 토큰을 폐기할 수 있습니다. 리프레시 토큰은 Laravel\Passport\RefreshTokenRepositoryrevokeRefreshTokensByAccessTokenId 메서드로 폐기합니다. 두 클래스 모두 Laravel 서비스 컨테이너를 통해 resolve할 수 있습니다:

use Laravel\Passport\TokenRepository; use Laravel\Passport\RefreshTokenRepository; $tokenRepository = app(TokenRepository::class); $refreshTokenRepository = app(RefreshTokenRepository::class); // 액세스 토큰 폐기 $tokenRepository->revokeAccessToken($tokenId); // 해당 토큰의 모든 리프레시 토큰 폐기 $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);

토큰 정리

폐기되거나 만료된 토큰은 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 Illuminate\Support\Facades\Schedule; Schedule::command('passport:purge')->hourly();

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

번역일: 2026년 6월 21일