git |
---|
16b8805d9c475af2f01351913316fcd835a81ad3 |
Laravel Passport обеспечивает полную реализацию сервера OAuth2 для вашего приложения Laravel за считанные минуты. Passport построен на основе League OAuth2, который поддерживается Энди Миллингтоном (Andy Millington) и Саймоном Хэмпом (Simon Hamp).
Warning
В этой документации предполагается, что вы уже знакомы с OAuth2. Если вы ничего не знаете о OAuth2, перед продолжением ознакомьтесь с общей терминологией и функциями OAuth2.
Прежде чем начать, вы можете определиться, будет ли ваше приложение лучше обслуживаться через Laravel Passport или Laravel Sanctum. Если вашему приложению необходима поддержка OAuth2, то следует использовать Laravel Passport.
Однако, если вы пытаетесь аутентифицировать одностраничное приложение, мобильное приложение или выдавать токены API, вам следует использовать Laravel Sanctum. Laravel Sanctum не поддерживает OAuth2; однако он обеспечивает гораздо более простой опыт разработки аутентификации API.
Вы можете установить Laravel Passport с помощью Artisan-команды install:api
:
php artisan install:api --passport
Эта команда опубликует и запустит миграцию базы данных для создания таблиц, необходимых вашему приложению для хранения клиентов OAuth2 и токенов доступа. Команда также создаст ключи шифрования, необходимые для создания токенов безопасного доступа.
Кроме того, эта команда спросит, хотите ли вы использовать UUID в качестве значения первичного ключа модели Passport Client
вместо автоматического увеличения целых чисел.
После запуска команды install:api
добавьте трейт Laravel\Passport\HasApiTokens
в вашу модель App\Models\User
. Этот трейт предоставит вашей модели несколько вспомогательных методов, которые позволят вам проверять токен и области аутентифицированного пользователя:
<?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
вы должны установить для параметра driver
раздела api
значение passport
. Это укажет вашему приложению использовать Passport TokenGuard
при аутентификации входящих запросов API:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
При первом развертывании Passport на серверах вашего приложения вам, вероятно, потребуется выполнить команду passport:keys
. Эта команда генерирует ключи шифрования, необходимые Passport для создания токенов доступа. Сгенерированные ключи обычно не хранятся в системе контроля версий:
php artisan passport:keys
При необходимости вы можете указать путь, откуда должны быть загружены ключи Passport. Для этого вы можете использовать метод Passport::loadKeysFrom
. Обычно этот метод следует вызывать из метода boot
класса App\Providers\AppServiceProvider
вашего приложения:
/**
* Запустите любые службы приложений.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}
В качестве альтернативы вы можете опубликовать файл конфигурации Passport с помощью Artisan-команды vendor:publish
:
php artisan vendor:publish --tag=passport-config
После публикации файла конфигурации вы можете загрузить ключи шифрования вашего приложения, определив их как переменные среды:
PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"
PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"
При обновлении до новой основной версии Passport важно внимательно изучить руководство по обновлению.
Если вы хотите, чтобы секретные ключи клиента хешировались при хранении в базе данных, вы должны вызвать метод Passport::hashClientSecrets
в методе boot
класса App\Providers\AppServiceProvider
:
use Laravel\Passport\Passport;
Passport::hashClientSecrets();
После включения все секретные ключи будут отображаться пользователю один раз, только после их создания. Поскольку значение секретного ключа в виде обычного текста никогда не хранится в базе данных, невозможно восстановить значение ключа, если оно утеряно.
По умолчанию Passport выдает долговременные токены доступа, срок действия которых истекает через год. Если вы хотите настроить более длительный / более короткий срок жизни токена, вы можете использовать методы tokensExpireIn
, refreshTokensExpireIn
и personalAccessTokensExpireIn
. Эти методы следует вызывать из метода boot
класса App\Providers\AppServiceProvider
вашего приложения:
/**
* Запустите любые службы приложений.
*/
public function boot(): void
{
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}
Warning
Поля expires_at
в таблицах базы данных Passport доступны только для чтения и только для отображения. При выпуске токенов Passport сохраняет информацию об истечении срока действия в подписанных и зашифрованных токенах. Если вам нужно сделать токен недействительным, вы должны отозвать его.
Вы можете свободно расширять модели, используемые внутри Passport, определяя свою собственную модель и расширяя соответствующую модель Passport:
use Laravel\Passport\Client as PassportClient;
class Client extends PassportClient
{
// ...
}
После определения модели вы можете указать Passport использовать вашу пользовательскую модель через класс Laravel\Passport\Passport
. Как правило, вы должны сообщить Passport о ваших пользовательских моделях в методе boot
класса App\Providers\AppServiceProvider
вашего приложения:
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. Для этого сначала нужно игнорировать маршруты, зарегистрированные Passport, добавив Passport::ignoreRoutes()
в метод register
класса AppServiceProvider
вашего приложения:
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...
});
Этот подход позволяет вам полностью контролировать маршруты, связанные с Passport, и настраивать их в соответствии с потребностями вашего приложения.
Использование OAuth2 через коды авторизации — это то, через что большинство разработчиков знакомится с OAuth2. При использовании кодов авторизации клиентское приложение перенаправит пользователя на ваш сервер, где он либо утвердит, либо отклонит запрос на выдачу токена доступа клиенту.
Во-первых, разработчики, которым необходимо взаимодействовать с API вашего приложения, должны будут зарегистрировать свое приложение в вашем, создав «клиента» (client). Обычно это состоит из указания имени своего приложения и URL-адреса, на который ваше приложение может перенаправить после того, как пользователи одобрят свой запрос на авторизацию.
Самый простой способ создать клиента — использовать Artisan-команду passport:client
. Эта команда может использоваться для создания ваших собственных клиентов для тестирования вашей функциональности OAuth2. Когда вы запускаете команду client
, Passport запросит у вас дополнительную информацию о вашем клиенте и предоставит вам идентификатор клиента и секретный ключ:
php artisan passport:client
URL-адреса перенаправления
Если вы хотите разрешить несколько URL-адресов перенаправления для своего клиента, вы можете указать их, используя список с разделителями-запятыми, когда вам будет предложено ввести URL-адрес командой passport:client
. Любые URL-адреса, содержащие запятые, должны быть закодированы:
http://example.com/callback,http://examplefoo.com/callback
Поскольку пользователи вашего приложения не смогут использовать команду client
, Passport предоставляет JSON API, который вы можете использовать для создания клиентов. Это избавляет вас от необходимости вручную кодировать контроллеры для создания, обновления и удаления клиентов.
Однако вам нужно будет связать JSON API Passport с вашим собственным интерфейсом, чтобы предоставить вашим пользователям панель управления для управления своими клиентами. Ниже мы рассмотрим все конечные точки API для управления клиентами. Для удобства мы будем использовать Axios, чтобы продемонстрировать выполнение HTTP-запросов к конечным точкам.
JSON API защищен посредниками web
и auth
; поэтому его можно вызывать только из вашего собственного приложения. Он не может быть вызван из внешнего источника.
Этот маршрут возвращает всех клиентов для аутентифицированного пользователя. Это в первую очередь полезно для перечисления всех клиентов пользователя, чтобы пользователи могли редактировать или удалять их:
axios.get('/oauth/clients')
.then(response => {
console.log(response.data);
});
Этот маршрут используется для создания новых клиентов. Для этого требуются два параметра: name
- имя клиента и redirect
- URL-адрес перенаправления. URL-адрес redirect
- это то, куда пользователь будет перенаправлен после утверждения или отклонения запроса на авторизацию.
Когда клиент будет создан, ему будет выдан идентификатор клиента и секретный ключ. Эти значения будут использоваться при запросе токенов доступа из вашего приложения. Маршрут создания клиента вернет новый экземпляр клиента:
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...
});
Этот маршрут используется для обновления клиентов. Для этого требуются два параметра: name
- имя клиента и redirect
- URL-адрес перенаправления. URL-адрес redirect
- это то, куда пользователь будет перенаправлен после утверждения или отклонения запроса на авторизацию. Маршрут вернет обновленный экземпляр клиента:
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...
});
Этот маршрут используется для удаления клиентов:
axios.delete('/oauth/clients/' + clientId)
.then(response => {
// ...
});
После создания клиента разработчики могут использовать свой идентификатор клиента и секретный ключ, чтобы запросить код авторизации и токен доступа из вашего приложения. Во-первых, приложение-потребитель должно сделать запрос перенаправления на маршрут вашего приложения /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
должен соответствовать URL-адресу перенаправления, который был указан при создании клиента.
Если вы хотите настроить экран утверждения авторизации, вы можете опубликовать макет Passport с помощью Artisan-команды vendor: publish
. Опубликованные макеты будут помещены в каталог resources/views/vendor/passport
:
php artisan vendor:publish --tag=passport-views
Иногда вам может понадобиться пропустить запрос на авторизацию, например, при авторизации основного клиента. Вы можете сделать это, расширив модель 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
вернет ответ JSON, содержащий атрибуты access_token
, refresh_token
и expires_in
. Атрибут expires_in
содержит количество секунд до истечения срока действия токена доступа.
Note
Как и маршрут /oauth/authorize
, маршрут /oauth/token
определяется для вас методом Passport::routes
. Нет необходимости определять этот маршрут вручную.
Passport также включает JSON API для управления авторизованными токенами доступа. Вы можете связать это со своим собственным интерфейсом, чтобы предложить своим пользователям панель управления для управления токенами доступа. Для удобства мы будем использовать Axios, чтобы продемонстрировать выполнение HTTP-запросов к конечным точкам. JSON API защищен посредниками web
и auth
; поэтому его можно вызывать только из вашего собственного приложения.
Этот маршрут возвращает все токены доступа, созданные аутентифицированным пользователем. Это в первую очередь полезно для просмотра всех токенов пользователя, чтобы он мог их отозвать:
axios.get('/oauth/tokens')
.then(response => {
console.log(response.data);
});
Этот маршрут может использоваться для отзыва токенов доступа и связанных с ними токенов обновления:
axios.delete('/oauth/tokens/' + tokenId);
Если ваше приложение выдает недолговечные токены доступа, пользователям потребуется обновить свои токены доступа с помощью токена обновления, предоставленного им при выдаче токена доступа:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'refresh_token',
'refresh_token' => 'the-refresh-token',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '',
]);
return $response->json();
Этот маршрут /oauth/token
вернет ответ JSON, содержащий атрибуты access_token
, refresh_token
и expires_in
. Атрибут expires_in
содержит количество секунд до истечения срока действия токена доступа.
Вы можете отозвать токен с помощью метода revokeAccessToken
в Laravel\Passport\TokenRepository
. Вы можете отозвать токены обновления токена с помощью метода revokeRefreshTokensByAccessTokenId
в Laravel\Passport\RefreshTokenRepository
. Эти классы могут быть разрешены с помощью сервисного контейнера Laravel:
use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;
$tokenRepository = app(TokenRepository::class);
$refreshTokenRepository = app(RefreshTokenRepository::class);
// Revoke an access token...
$tokenRepository->revokeAccessToken($tokenId);
// Revoke all of the token's refresh tokens...
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
Когда токены были отозваны или срок их действия истек, вы можете удалить их из базы данных. Команда passport:purge
Artisan, содержащаяся в Passport, может сделать это за вас:
# Удалить отозванные и просроченные токены, и коды авторизации ...
php artisan passport:purge
# Удалить только отозванные токены и коды авторизации ...
php artisan passport:purge --revoked
# Удалить только просроченные токены и коды авторизации ...
php artisan passport:purge --expired
Вы также можете настроить запланированное задание в файле вашего приложения routes/console.php
для автоматического удаления токенов по расписанию:
use Illuminate\Support\Facades\Schedule;
Schedule::command('passport:purge')->hourly();
Предоставление кода авторизации с Proof Key for Code Exchange
(PKCE) - это безопасный способ аутентификации одностраничных приложений или собственных приложений для доступа к вашему API. Это разрешение следует использовать, когда вы не можете гарантировать, что секретный ключ клиента будет храниться конфиденциально, или, чтобы уменьшить угрозу перехвата кода авторизации злоумышленником. Комбинация code verifier
и code challenge
заменяет секретный ключ клиента при замене кода авторизации на токен доступа.
Прежде чем ваше приложение сможет выдавать токены через предоставление кода авторизации с помощью PKCE, вам необходимо создать клиента с поддержкой PKCE. Вы можете сделать это с помощью Artisan-команды passport:client
с параметром --public
:
php artisan passport:client --public
Поскольку это разрешение на авторизацию не предоставляет секретный ключ клиента, разработчикам необходимо сгенерировать комбинацию code verifier
и code challenge
, чтобы запросить токен.
Средство проверки кода должно представлять собой случайную строку от 43 до 128 символов, содержащую буквы, цифры и символы "-"
, "."
, "_"
, "~"
, как определено в спецификации RFC 7636.
Итоговым результатом должна быть строка в кодировке Base64 с URL-адресом и безопасными для имени файла символами. Завершающие символы '=' должны быть удалены, и не должно быть разрывов строк, пробелов или других дополнительных символов.
$encoded = base64_encode(hash('sha256', $code_verifier, true));
$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
После создания клиента вы можете использовать идентификатор клиента и сгенерированный code verifier
и code challenge
, чтобы запросить код авторизации и токен доступа из вашего приложения. Во-первых, приложение-потребитель должно сделать запрос перенаправления на маршрут вашего приложения /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
со значением, которое было сохранено до перенаправления, как в стандартном предоставлении кода авторизации.
Если параметр состояния совпадает, потребитель должен отправить вашему приложению запрос 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 Server.
Предоставление пароля OAuth2 позволяет другим сторонним клиентам, таким как мобильное приложение, получать токен доступа, используя адрес электронной почты / имя пользователя и пароль. Это позволяет вам безопасно выдавать токены доступа своим основным клиентам, не требуя от пользователей прохождения всего потока перенаправления кода авторизации OAuth2.
Чтобы включить предоставление пароля, вызовите метод enablePasswordGrant
в методе boot
класса App\Providers\AppServiceProvider
вашего приложения:
/**
* Запустите любые службы приложения.
*/
public function boot(): void
{
Passport::enablePasswordGrant();
}
Прежде чем ваше приложение сможет выдавать токены с помощью предоставления пароля, вам необходимо создать клиент предоставления пароля. Вы можете сделать это с помощью Artisan-команды passport:client
с параметром --password
. Если вы уже выполнили команду passport:install
, вам не нужно запускать эту команду:
php artisan passport:client --password
После создания клиента с предоставлением пароля вы можете запросить токен доступа, отправив POST
запрос на маршрут /oauth/token
с адресом электронной почты и паролем пользователя. Помните, что этот маршрут уже зарегистрирован Passport, поэтому нет необходимости определять его вручную. Если запрос будет успешным, вы получите access_token
и refresh_token
в JSON-ответе от сервера:
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();
Note
Помните, токены доступа по умолчанию являются долгоживущими. Однако вы можете настроить максимальное время жизни токена доступа, если это необходимо.
При использовании доступа по паролю или доступа с учетными данными клиента вы можете авторизовать токен для всех областей, поддерживаемых вашим приложением. Вы можете сделать это, указав *
в параметре scope
. При этом метод 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' => '*',
]);
Если ваше приложение использует более одного провайдера аутентификации пользователя, вы можете указать, какой провайдер использует клиент предоставления пароля, указав параметр --provider
при создании клиента через команду artisan passport:client --password
. Указанное имя провайдера должно соответствовать допустимому провайдеру, определенному в файле конфигурации приложения config/auth.php
. Затем вы можете защитить свой маршрут с помощью посредника, чтобы гарантировать, что авторизованы только пользователи из указанного провайдера.
При аутентификации с использованием предоставления пароля Passport будет использовать атрибут email
вашей аутентифицируемой модели в качестве "username". Однако вы можете настроить это поведение, определив метод 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;
/**
* Проверьте пароль пользователя для предоставления разрешения.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}
Warning
Мы больше не рекомендуем использовать токены для предоставления пароля. Вместо этого вам следует выбрать тип гаранта, который в настоящее время рекомендуется OAuth2 Server.
Неявное разрешение аналогично предоставлению кода авторизации; однако токен возвращается клиенту без обмена кодом авторизации. Это разрешение чаще всего используется для JavaScript или мобильных приложений, где учетные данные клиента не могут быть надежно сохранены. Чтобы включить разрешение, вызовите метод enableImplicitGrant
в методе boot
класса App\Providers\AppServiceProvider
вашего приложения:
/**
* Запустите любые службы приложения.
*/
public function boot(): void
{
Passport::enableImplicitGrant();
}
После включения разрешения разработчики могут использовать свой идентификатор клиента для запроса токена доступа из вашего приложения. Приложение-потребитель должно сделать запрос перенаправления на маршрут вашего приложения /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::routes
. Вам не нужно вручную определять этот маршрут.
Предоставление учетных данных клиента подходит для межмашинной (machine-to-machine) аутентификации. Например, вы можете использовать это разрешение в запланированном задании, которое выполняет задачи обслуживания через API.
Прежде чем ваше приложение сможет выдавать токены с помощью предоставления учетных данных клиента, вам необходимо создать клиента предоставления учетных данных. Вы можете сделать это, используя параметр --client
в Artisan-команде passport:client
:
php artisan passport:client --client
Далее, чтобы использовать этот тип разрешения, зарегистрируйте посредника (middleware) CheckClientCredentials
. Вы можете определить псевдонимы промежуточного программного обеспечения в файле bootstrap/app.php
вашего приложения:
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'client' => CheckClientCredentials::class
]);
})
Затем назначьте посредника к маршруту:
Route::get('/orders', function (Request $request) {
...
})->middleware('client');
Чтобы ограничить доступ к маршруту определенными областями, вы можете предоставить разделенный запятыми список требуемых областей при подключении посредника client
к маршруту:
Route::get('/orders', function (Request $request) {
...
})->middleware('client:check-status,your-scope');
Чтобы получить токен с использованием этого типа разрешения, сделайте запрос к конечной точке oauth/token
:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'client_credentials',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => 'your-scope',
]);
return $response->json()['access_token'];
Иногда ваши пользователи могут захотеть выдать себе токены доступа, не проходя типичный поток перенаправления кода авторизации. Разрешение пользователям выдавать себе токены через пользовательский интерфейс вашего приложения может быть полезно для предоставления пользователям возможности экспериментировать с вашим API или может служить более простым подходом к выдаче токенов доступа в целом.
Note
Если ваше приложение в основном использует Passport для выдачи токенов личного доступа, рассмотрите возможность использования Laravel Sanctum, облегченной собственной библиотеки Laravel для выдачи токенов доступа к API.
Прежде чем ваше приложение сможет выдавать токены персонального доступа, вам необходимо создать клиента личного доступа. Вы можете сделать это, выполнив Artisan-команду passport:client
с параметром --personal
. Если вы уже выполнили команду passport:install
, вам не нужно запускать эту команду:
php artisan passport:client --personal
После создания клиента личного доступа поместите идентификатор клиента и секретное значение в виде обычного текста в файл .env
вашего приложения:
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
После того как вы создали клиент персонального доступа, вы можете выдавать токены для данного пользователя, используя метод createToken
в экземпляре модели App\Models\User
. Метод createToken
принимает имя токена в качестве первого аргумента и необязательный массив области в качестве второго аргумента:
use App\Models\User;
$user = User::find(1);
// Создание токена без области действия ...
$token = $user->createToken('Token Name')->accessToken;
// Создание токена с областью ...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
Passport также включает JSON API для управления токенами личного доступа. Вы можете связать это со своим собственным интерфейсом, чтобы предложить своим пользователям панель управления для управления токенами личного доступа. Ниже мы рассмотрим все конечные точки API для управления токенами личного доступа. Для удобства мы будем использовать Axios, чтобы продемонстрировать выполнение HTTP-запросов к конечным точкам.
JSON API защищен посредниками web
и auth
; поэтому его можно вызывать только из вашего собственного приложения. Он не может быть вызван из внешнего источника.
Этот маршрут возвращает все области, определенные для вашего приложения. Вы можете использовать этот маршрут для перечисления областей, которые пользователь может назначить личному токену доступа:
axios.get('/oauth/scopes')
.then(response => {
console.log(response.data);
});
Этот маршрут возвращает все токены личного доступа, созданные аутентифицированным пользователем. Это в первую очередь полезно для перечисления всех токенов пользователей, чтобы они могли редактировать или отзывать их:
axios.get('/oauth/personal-access-tokens')
.then(response => {
console.log(response.data);
});
Этот маршрут создает новые токены личного доступа. Для этого требуются два параметра: name
и scopes
, которые должны быть назначены токену:
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...
});
Этот маршрут может использоваться для отзыва токенов личного доступа:
axios.delete('/oauth/personal-access-tokens/' + tokenId);
Паспорт включает в себя защиту аутентификации, которая проверяет токены доступа при входящих запросах. После того как вы настроили защиту api
для использования драйвера passport
, вам нужно указать посредника auth:api
на всех маршрутах, для которых требуется действующий токен доступа:
Route::get('/user', function () {
// ...
})->middleware('auth:api');
Warning
Если вы используете токены учетных данных, вы должны вместо этого использовать посредник client
для защиты ваших маршрутов auth:api
.
Если ваше приложение аутентифицирует разные типы пользователей, которые, возможно, используют совершенно разные модели Eloquent, вам, вероятно, потребуется определить конфигурацию защиты для каждого типа провайдера пользователей в вашем приложении. Это позволяет защитить запросы, предназначенные для конкретных поставщиков услуг. Например, при следующей конфигурации защиты конфигурационный файл config/auth.php
:
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'api-customers' => [
'driver' => 'passport',
'provider' => 'customers',
],
Следующий маршрут будет использовать защиту api-customers
, которая использует провайдера пользователей customers
для аутентификации входящих запросов:
Route::get('/customer', function () {
// ...
})->middleware('auth:api-customers');
Note
Для получения дополнительной информации об использовании нескольких поставщиков пользователей с Passport обратитесь к документации по предоставлению пароля.
При вызове маршрутов, защищенных Passport, пользователи API вашего приложения должны указать свой токен доступа как токен Bearer в заголовке Authorization своего запроса. Например, при использовании HTTP-библиотеки Guzzle:
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 запрашивать определенный набор разрешений при запросе авторизации для доступа к учетной записи. Например, если вы создаете приложение для электронной коммерции, не всем потребителям API потребуется возможность размещать заказы. Вместо этого вы можете разрешить потребителям запрашивать авторизацию только для доступа к статусам отгрузки заказа. Другими словами, области позволяют пользователям вашего приложения ограничивать действия, которые стороннее приложение может выполнять от их имени.
Вы можете определить области своего API, используя метод Passport::tokensCan
в методе boot
класса App\Providers\AppServiceProvider
вашего приложения. Метод tokensCan
принимает массив имен и описаний областей видимости. Описание области действия может быть любым, и оно будет отображаться для пользователей на экране утверждения авторизации:
/**
* Запустите любые службы приложения.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}
Если клиент не запрашивает какие-либо определенные области, вы можете настроить свой сервер Passport для присоединения области (областей) по умолчанию к токену с помощью метода setDefaultScope
. Как правило, вы должны вызывать этот метод из метода boot
класса App\Providers\AppServiceProvider
вашего приложения:
use Laravel\Passport\Passport;
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
Passport::setDefaultScope([
'check-status',
'place-orders',
]);
Note
Стандартные области применения (scopes) 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);
});
Если вы выдаете токены личного доступа с помощью метода createToken
модели App\Models\User
, вы можете передать массив желаемых областей в качестве второго аргумента метода:
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
Passport включает в себя два посредника, которые можно использовать для проверки подлинности входящего запроса с помощью токена, которому предоставлена заданная область действия. Для начала определите следующие псевдонимы посредников в файле 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']);
Посреднику областей scopes
может быть назначен маршрут для проверки того, что токен доступа входящего запроса имеет хотя бы одну из перечисленных областей:
Route::get('/orders', function () {
// Токен содержит одну из областей - "check-status" или "place-orders" ...
})->middleware(['auth:api', 'scope:check-status,place-orders']);
После того как запрос с аутентификацией токена доступа поступил в ваше приложение, вы все равно можете проверить, имеет ли токен заданную область действия, используя метод tokenCan
в экземпляре App\Models\User
:
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
if ($request->user()->tokenCan('place-orders')) {
// ...
}
});
Метод scopeIds
вернет массив всех определенных идентификаторов / имен:
use Laravel\Passport\Passport;
Passport::scopeIds();
Метод scopes
вернет массив всех определенных областей как экземпляры Laravel\Passport\Scope
:
Passport::scopes();
Метод scopesFor
вернет массив экземпляров Laravel\Passport\Scope
, соответствующих указанным идентификаторам / именам:
Passport::scopesFor(['place-orders', 'check-status']);
Вы можете определить, была ли определена область, используя метод hasScope
:
Passport::hasScope('place-orders');
При создании API может быть чрезвычайно полезно иметь возможность использовать собственный API из приложения JavaScript. Такой подход к разработке API позволяет вашему собственному приложению использовать тот же API, которым вы делитесь со всем миром. Один и тот же API может использоваться вашим веб-приложением, мобильными приложениями, сторонними приложениями и любыми SDK, которые вы можете публиковать в различных менеджерах пакетов.
Как правило, если вы хотите использовать свой API из своего приложения JavaScript, вам необходимо вручную отправить токен доступа в приложение и передавать его с каждым запросом к вашему приложению. Однако Passport включает в себя посредника, которое может сделать это за вас. Все, что вам нужно сделать, это добавить посредника CreateFreshApiToken
в группу посредников web
в файле bootstrap/app.php
вашего приложения:
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
CreateFreshApiToken::class,
]);
})
Warning
Вы должны убедиться, что посредник CreateFreshApiToken
является последним в списке ваших посредников указанных ранее.
Это посредник будет прикреплять файл cookie laravel_token
к вашим исходящим ответам. Этот файл cookie содержит зашифрованный JWT, который Passport будет использовать для аутентификации запросов API от вашего приложения JavaScript. Время жизни JWT равно вашему значению конфигурации session.lifetime. Теперь, поскольку браузер автоматически отправляет cookie со всеми последующими запросами, вы можете делать запросы к API вашего приложения без явной передачи токена доступа:
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
При необходимости вы можете настроить имя файла cookie laravel_token
, используя метод Passport::cookie
. Обычно этот метод следует вызывать из метода boot
класса App\Providers\AppServiceProvider
вашего приложения:
/**
* Запустите любые службы приложения.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}
При использовании этого метода аутентификации вам необходимо убедиться, что в ваши запросы включен действительный заголовок токена CSRF. В состав шаблонов JavaScript Laravel по умолчанию входит экземпляр Axios, который будет автоматически использовать зашифрованное значение cookie XSRF-TOKEN
для отправки заголовка X-XSRF-TOKEN
в запросах.
Note
Если вы решите отправить заголовок X-CSRF-TOKEN
вместо X-XSRF-TOKEN
, вам нужно использовать незашифрованный токен, предоставленный csrf_token()
.
Passport вызывает события при выдаче токенов доступа и обновлении токенов. Вы можете прослушивать эти события, чтобы сократить или отозвать другие токены доступа в вашей базе данных:
Наименование события |
---|
Laravel\Passport\Events\AccessTokenCreated |
Laravel\Passport\Events\RefreshTokenCreated |
Метод actingAs
Passport может использоваться для указания аутентифицированного в данный момент пользователя, а также его областей действия. Первым аргументом, передаваемым методу actingAs
, является экземпляр пользователя, а вторым - массив областей видимости, которые должны быть предоставлены токену пользователя:
use App\Models\User;
use Laravel\Passport\Passport;
test('servers can be created', function () {
Passport::actingAs(
User::factory()->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(201);
});
use App\Models\User;
use Laravel\Passport\Passport;
public function test_servers_can_be_created(): void
{
Passport::actingAs(
User::factory()->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(201);
}
Метод actingAsClient
Passport может использоваться для указания аутентифицированного в данный момент клиента, а также его областей. Первым аргументом, передаваемым методу actingAsClient
, является экземпляр клиента, а вторым — массив областей видимости, которые должны быть предоставлены токену клиента:
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
test('orders can be retrieved', function () {
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
});
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
public function test_orders_can_be_retrieved(): void
{
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
}