Настройка сервера OAuth2 с использованием Passport в Laravel
В этой статье мы рассмотрим, как вы можете создать полноценный сервер OAuth2 в Laravel с помощью библиотеки Laravel Passport. Мы рассмотрим необходимые конфигурации серверов вместе с реальным примером, чтобы продемонстрировать то, как вы можете использовать API OAuth2.
Я предполагаю, что вы знакомы с основными концепциями OAuth2 и потоком, поскольку мы собираемся обсудить их в контексте Laravel. Фактически, библиотека Laravel Passport позволяет легко настроить сервер OAuth2 в вашем приложении. Таким образом, другие сторонние приложения могут использовать API, предоставляемые вашим приложением.
В первой половине статьи мы установим и настроим необходимые библиотеки, а во второй половине расскажем, как настроить демонстрационные ресурсы в приложении и использовать их.
Конфигурации сервера
В этом разделе мы собираемся установить зависимости, необходимые для того, чтобы библиотека Passport работала с Laravel. После установки нужно будет пройти настройку, чтобы Laravel мог обнаружить библиотеку Passport.
Давайте продолжим и установим библиотеку Passport с помощью Composer.
$composer require laravel/passport
Это в значительной степени касается установки библиотеки Passport. Теперь надо удостовериться, что Laravel знает о ней.
Работая с Laravel, вы, вероятно, знаете о концепции поставщика услуг, который позволяет настраивать службы в вашем приложении. Таким образом, всякий раз, когда вы хотите включить новую службу в своем приложении Laravel, вам просто нужно добавить соответствующую запись поставщика услуг в config/app.php
.
Если вы еще не знакомы с поставщиками услуг Laravel, я бы настоятельно рекомендовал вам ознакомиться с этой вводной статьей, в которой объясняются основы поставщиков услуг в Laravel.
В нашем случае нам просто нужно добавить поставщика PassportServiceProvider
в список поставщиков услуг в config/app.php
, как показано в следующем фрагменте.
...
...
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
Laravel\Tinker\TinkerServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Laravel\Passport\PassportServiceProvider::class,
],
...
...
Затем нам нужно запустить команду artisan migrate
, которая создает необходимые таблицы в базе данных для библиотеки Passport.
$php artisan migrate
Если быть точным, она создает следующие таблицы в базе данных.
oauth_access_tokens
oauth_auth_codes
oauth_clients
oauth_personal_access_clients
oauth_refresh_tokens
Затем нам нужно создать пару открытых и закрытых ключей, которые будут использоваться библиотекой Passport для шифрования. Как и ожидалось, библиотека Passport предоставляет команду artisan для их создания.
$php artisan passport:install
Это должно было создать ключи в storage/oauth-public.key
и storage/oauth-private.key
. Эта команда также создает некоторые демо-клиентские учетные данные, к которым мы позже еще вернемся.
Двигаемся дальше, давайте oauthify существующий класс модели пользователя, который Laravel использует для аутентификации. Для этого нам нужно добавить трейт HasApiTokens
в класс модели User
. Давайте сделаем это, как показано в следующем фрагменте.
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
Трейт HasApiTokens
содержит вспомогательные методы, которые используются для проверки токенов в запросе и проверки скоупа ресурсов, запрашиваемых в контексте пользователя, прошедшего проверку подлинности.
Кроме того, нам необходимо зарегистрировать маршруты, предоставленные библиотекой Passport, с помощью нашего приложения Laravel. Эти маршруты будут использоваться для стандартных операций OAuth2, таких как авторизация, запрос токенов доступа и т.п.
В методе boot файла app/Providers/AuthServiceProvider.php
давайте зарегистрируем маршруты библиотеки Passport.
...
...
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
...
...
И последнее, но не менее важное: нам нужно изменить драйвер api
из token в passport в файле config/auth.php
, поскольку мы собираемся использовать библиотеку Passport для аутентификации API.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
До сих пор мы делали все, что требовалось для конфигурации сервера OAuth2.
Настройка демо-ресурсов
В предыдущем разделе мы проделали большую работу по настройке сервера аутентификации OAuth2 в нашем приложении. В этом разделе мы создадим демонстрационный ресурс, который можно запросить по вызову API.
Мы постараемся, чтобы все было просто. Наш демонстрационный ресурс возвращает информацию пользователя при условии, что в запросе GET
присутствует действительный параметр uid
.
Давайте создадим файл контроллера app/Http/Controllers/UserController.php
со следующим содержимым.
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\User;
class UserController extends Controller
{
public function get(Request $request)
{
$user_id = $request->get("uid", 0);
$user = User::find($user_id);
return $user;
}
}
Как обычно, вам нужно также добавить связанный маршрут, который вы должны добавить в файл routes/web.php
. Но речь идет о маршруте API, и, следовательно, это требует отдельного рассмотрения.
Маршруты API определены в файле route/api.php
. Итак, давайте продолжим и добавим наш пользовательский маршрут API, как показано в следующем фрагменте.
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
// custom API route
Route::middleware('auth:api')->get('/user/get', 'UserController@get');
Хотя мы определили его как /user/get
, реальный маршрут API — это /api/user/get
, и именно его вы должны использовать, когда запрашиваете ресурс по этому маршруту. Префикс api
автоматически обрабатывается Laravel, и вам не нужно беспокоиться об этом!
В следующем и последнем разделе мы обсудим, как вы можете создавать учетные данные клиента и использовать API OAuth2.
Как использовать API OAuth2
Теперь, когда мы установили сервер OAuth2 в нашем приложении, любое стороннее приложение может подключиться к нашему серверу с помощью OAuth и использовать API, доступные в нашем приложении.
Прежде всего, сторонние приложения должны зарегистрироваться в нашем приложении, чтобы иметь возможность использовать API. Другими словами, они рассматриваются как клиентские приложения, и при регистрации они получат идентификатор клиента и клиентский секрет.
Библиотека Passport предоставляет команду artisan для создания клиентских учетных записей. Давайте продолжим и создадим демо-клиента.
$php artisan passport:client
Which user ID should the client be assigned to?:
> 1
What should we name the client?:
> Demo OAuth2 Client Account
Where should we redirect the request after authorization? [http://localhost/auth/callback]:
> http://localhost/oauth2_client/callback.php
New client created successfully.
Client ID: 1
Client secret: zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01
Когда вы запустите команду artisan passport:client
, она задаст вам несколько вопросов, прежде чем создавать учетную запись клиента. Из них есть важный вопрос, который запрашивает URL-адрес обратного вызова
.
URL-адрес обратного вызова
— это тот адрес, куда после авторизации пользователи будут перенаправлены обратно. И сюда будет отправлен код авторизации, который предполагается использовать в обмен на токен доступа. Мы собираемся создать этот файл в одно мгновение.
Теперь мы готовы протестировать API OAuth2 в приложении Laravel.
Для демонстрационных целей я сначала создам каталог oauth2_client
в document root. В идеале эти файлы должны быть расположены на стороннем сервере, который хочет использовать API в нашем приложении Laravel.
Давайте создадим файл oauth2_client/auth_redirection.php
со следующим содержимым.
<?php
$query = http_build_query(array(
'client_id' => '1',
'redirect_uri' => 'http://localhost/oauth2_client/callback.php',
'response_type' => 'code',
'scope' => '',
));
header('Location: http://your-laravel-site-url/oauth/authorize?'.$query);
Обязательно измените параметры client_id
и redirect_uri
, чтобы отразить ваши собственные настройки — те, которые вы использовали при создании учетной записи демо-клиента.
Затем создадим файл oauth2_client/callback.php
со следующим содержимым.
<?php
// check if the response includes authorization_code
if (isset($_REQUEST['code']) && $_REQUEST['code'])
{
$ch = curl_init();
$url = 'http://your-laravel-site-url/oauth/token';
$params = array(
'grant_type' => 'authorization_code',
'client_id' => '1',
'client_secret' => 'zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01',
'redirect_uri' => 'http://localhost/oauth2_client/callback.php',
'code' => $_REQUEST['code']
);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$params_string = '';
if (is_array($params) && count($params))
{
foreach($params as $key=>$value) {
$params_string .= $key.'='.$value.'&';
}
rtrim($params_string, '&');
curl_setopt($ch,CURLOPT_POST, count($params));
curl_setopt($ch,CURLOPT_POSTFIELDS, $params_string);
}
$result = curl_exec($ch);
curl_close($ch);
$response = json_decode($result);
// check if the response includes access_token
if (isset($response->access_token) && $response->access_token)
{
// you would like to store the access_token in the session though...
$access_token = $response->access_token;
// use above token to make further api calls in this session or until the access token expires
$ch = curl_init();
$url = 'http://your-laravel-site-url/api/user/get';
$header = array(
'Authorization: Bearer '. $access_token
);
$query = http_build_query(array('uid' => '1'));
curl_setopt($ch,CURLOPT_URL, $url . '?' . $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$result = curl_exec($ch);
curl_close($ch);
$response = json_decode($result);
var_dump($result);
}
else
{
// for some reason, the access_token was not available
// debugging goes here
}
}
Опять же, не забудьте настроить URL-адреса и учетные данные клиента в соответствии с вашей настройкой в указанном выше файле.
Как это работает
В этом разделе мы проверим все полностью с точки зрения конечного пользователя. В качестве конечного пользователя перед вами есть два приложения:
- Первое — это приложение Laravel, с которым у вас уже есть учетная запись. Оно содержит информацию, которую вы можете предоставить другим сторонним приложениям.
- Второй — демо-приложение стороннего клиента,
auth_redirection.php
иcallback.php
, которое хочет получить вашу информацию из приложения Laravel с использованием API OAuth.
Поток начинается с стороннего клиентского приложения. Итак откройте URL-адрес http://localhost/oauth2_client/auth_redirection.php в своем браузере, и это должно перенаправить вас в приложение Laravel. Если вы еще не вошли в приложение Laravel, приложение попросит вас сперва сделать это.
После входа в систему приложение отображает страницу авторизации.
Если пользователь разрешает этот запрос, пользователь будет перенаправлен обратно в стороннее клиентское приложение по адресу http://localhost/oauth2_client/callback.php вместе с code
в качестве параметра GET
, содержащего код авторизации.
После того как стороннее приложение получит код авторизации, оно может обменять этот код с приложением Laravel для получения токена доступа. И это именно то, что оно сделает в следующем фрагменте файла oauth2_client/callback.php
.
$ch = curl_init();
$url = 'http://your-laravel-site-url/oauth/token';
$params = array(
'grant_type' => 'authorization_code',
'client_id' => '1',
'client_secret' => 'zMm0tQ9Cp7LbjK3QTgPy1pssoT1X0u7sg0YWUW01',
'redirect_uri' => 'http://localhost/oauth2_client/callback.php',
'code' => $_REQUEST['code']
);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$params_string = '';
if (is_array($params) && count($params))
{
foreach($params as $key=>$value) {
$params_string .= $key.'='.$value.'&';
}
rtrim($params_string, '&');
curl_setopt($ch,CURLOPT_POST, count($params));
curl_setopt($ch,CURLOPT_POSTFIELDS, $params_string);
}
$result = curl_exec($ch);
curl_close($ch);
$response = json_decode($result);
Затем стороннее приложение проверяет ответ запроса CURL, чтобы убедиться, что он содержит действительный токен доступа.
Как только стороннее приложение получит токен доступа, оно может использовать этот токен для дальнейшего вызова API для запроса ресурсов по мере необходимости из приложения Laravel. Конечно, токен доступа должен передаваться в каждом запросе, запрашивающем ресурсы из приложения Laravel.
Мы попытались подражать use-case в том, что стороннее приложение хочет получить доступ к информации о пользователе из приложения Laravel. И мы уже создали API ендпоинт, http://your-laravel-site-url/api/user/get, в приложении Laravel.
// check if the response includes access_token
if (isset($response->access_token) && $response->access_token)
{
// you would like to store the access_token in the session though...
$access_token = $response->access_token;
// use above token to make further api calls in this session or until the access token expires
$ch = curl_init();
$url = 'http://your-laravel-site-url/api/user/get';
$header = array(
'Authorization: Bearer '. $access_token
);
$query = http_build_query(array('uid' => '1'));
curl_setopt($ch,CURLOPT_URL, $url . '?' . $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$result = curl_exec($ch);
curl_close($ch);
$response = json_decode($result);
var_dump($result);
}
Итак, это полный процесс того, как вы должны использовать API OAuth2 в Laravel.
И с этим мы дошли до конца этой статьи.
Заключение
Сегодня мы изучили библиотеку Passport в Laravel, которая позволяет нам легко настроить сервер OAuth2 в приложении.
Для тех из вас, кто только начинает работать с Laravel или хочет расширить свои знания, сайт или приложение с расширениями, у нас есть множество вещей, которые вы можете изучать на Envato Market.
Не стесняйтесь делиться своими мыслями и предложения, используя приведенный ниже канал!