
Laravel + Passport + GraphQL: аутентифікація
21.05.2019 19:13 | Laravel
Розглянемо, як реалізувати аутентифікацію Laravel в зв'язці з Passport і GraphQL.
ОС: Ubuntu 18.04. Версия фреймворка: 5.8. PHP: 7.3. MySQL: 5.7.26
Створимо новий проект (передбачається, що встановлений інсталятор laravel. Якщо це не так, дивіться документацію):
laravel new lara-graphql
Цього разу обійдемося без Homestead. Заходимо в mysql
:
mysql -u root
І створюємо базу даних:
create database laravel_graphql;
Про всяк випадок перевіримо чи все пройшло нормально:
show databases;
Виходимо командою exit
. Потім відкриваємо файл .env
і прописуємо налаштування бази:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_graphql
DB_USERNAME=root
DB_PASSWORD=
Щоб було цікавіше, трохи змінимо міграцію користувачів:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('username');
$table->string('email')->unique();
$table->string('first_name');
$table->string('last_name');
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}
Відповідно до міграції відразу ж змінимо фабрику:
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\User;
use Illuminate\Support\Str;
use Faker\Generator as Faker;
$factory->define(User::class, function (Faker $faker) {
return [
'username' => $faker->userName,
'email' => $faker->unique()->safeEmail,
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
});
Зайдемо в тінкер:
php artisan tinker
І створимо декількох (5) користувачів:
>>> factory(App\User::class, 5)->create();
Далі ставимо пакет laravel passport
(повна документація тут):
composer require laravel/passport
Запускаємо міграції:
php artisan migrate
І виконаємо команду, яка створить ключі, необхідні для генерації токенів:
php artisan passport:install
В результаті ми отримаємо щось на зразок цього:
Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client secret: f2ymreiKBnaTai7JEGmZLQ843h2FYOjFzKTPylw4
Password grant client created successfully.
Client ID: 2
Client secret: g28LlYBp09o77OrhNfhJvgwZzDhBIUi24qQyO5aa
Нас цікавить password grant
. Скопіюємо client secret
з останнього рядка і вставимо в файл .env
наступне (це стане в нагоді в подальшому при роботі з GraphQL):
PASSPORT_CLIENT_ID=2
PASSPORT_CLIENT_SECRET=g28LlYBp09o77OrhNfhJvgwZzDhBIUi24qQyO5aa
Додаємо в модель User
трейт HasApiToken
:
<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
....
}
Потім в AuthServiceProvider
допишемо маршрути для токенів доступу:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes(function ($router) {
$router->forAccessTokens();
});
}
}
Внесемо необхідні зміни в config/auth.php
:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
],
Переходимо до GraphQL. В першу чергу встановимо lighthouse (це пакет, який дозволяє обслуговувати GraphQL ендпойнт в додатку Laravel):
composer require nuwave/lighthouse
Даний пакет включає в себе схему за замовчуванням, опублікуємо її:
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema
А також конфігурацію:
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
Для запитів нам також знадобиться веб-нітерфейс, тому також поставимо GraphQL Playground (такий собі аналог Postman):
composer require mll-lab/laravel-graphql-playground
І додамо фасад в config/app.php
:
'aliases' => [
// ...
'GraphQL' => Nuwave\Lighthouse\Support\Facades\GraphQLFacade::class,
]
Крім цього, знадобиться пакет для роботи з паспортом і встановленим раніше lighthouse:
composer require joselfonseca/lighthouse-graphql-passport-auth
За замовчуванням схема визначається всередині пакету. Однак, якщо ми захочемо її перевизначити, нам слід її опублікувати (буде також опублікована і конфігурація - файл lighthouse-graphql-passport.php
в директорії config
):
php artisan vendor:publish --provider="Joselfonseca\LighthouseGraphQLPassport\Providers\LighthouseGraphQLPassportServiceProvider"
Відкриємо вищезгаданий файл конфігурації пакета (lighthouse-graphql-passport.php) і додамо шлях до схеми:
'schema' => base_path('graphql/auth.graphql')
Важливо: якщо ми подивимося на вміст файлу graphql/auth.graphql
, то побачимо в кінці файлу:
extend type Mutation {
...
}
а це означає, що ми розширюємо мутації, які визначені в graphql/schema.graphql. Але на даний момент там ніяких мутацій немає, тому будемо отримувати помилку:
{
"errors": [
{
"message": "Schema is not configured for mutations.",
"extensions": {
"category": "graphql"
},
"locations": [
{
"line": 1,
"column": 1
}
]
}
]
}
і будемо ламати голову чому так відбувається. Щоб цього не сталося, допишемо в graphql/schema.graphql
якусь мутацію, наприклад:
input RegisterData {
email: String
username: String
first_name: String
last_name: String
password: String
}
type Mutation {
register(data: RegisterData): User @create
}
Попередження: дана мутація всього лише свого роду заглушка. У реальному проекті я б використовував резолвер.
Що ж - ніби все на місці. Запускаємо вбудований сервер:
php artisan serve
Потім в браузері відкриваємо сторінку localhost:8000/graphql-playground
. Скопіюємо пошту одного з створених раніше користувачів (пароль у всіх однаковий - password), і в лівій частині плейграунда додамо мутацію:
mutation {
login(data: { username: "jschiller@example.net", password: "password" }) {
access_token
refresh_token
expires_in
}
}
Клікаємо на play і отримуємо результат в такому форматі:
{
"data": {
"login": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjkwODRhYTNjOGJmOTgzMGZhYWE3Y2JjNmFhZGFjZDdlNWM5N2ZmZjRkNzIwODJiOWEwOWYzY2QzYzhhYzE1OWMwYmYyOTdhODU4MTkxMDNiIn0.eyJhdWQiOiIyIiwianRpIjoiOTA4NGFhM2M4YmY5ODMwZmFhYTdjYmM2YWFkYWNkN2U1Yzk3ZmZmNGQ3MjA4MmI5YTA5ZjNjZDNjOGFjMTU5YzBiZjI5N2E4NTgxOTEwM2IiLCJpYXQiOjE1NTg0NzczNjgsIm5iZiI6MTU1ODQ3NzM2OCwiZXhwIjoxNTkwMDk5NzY4LCJzdWIiOiI1Iiwic2NvcGVzIjpbXX0.Mf4Vfbab3lgxPlTH7CEaboaizW5J8t6DGaWv7HAEnwHkxTFy1neaziyQRqNplbx5hU6Azeo7cFyy3iQv2w-LJSfE0nznjL7-ALVjqnyVro_k_3HYFjvZBSSiowYcdOzIlgdv04dO2PFS_qn8Ap6rQQEbEHfewW9PbEH7KL3QYqPowKsgtXmzSlepmdd8lRDVlEp1oIp3cP5EqZw1ECNnqC_KjHnUnwFwCmi4u_BFLzRBMFltHI1xPs7rim4_IwIB8ShN7a5HHAiydjV1JFhnIDJCyVCVqEMQcLNFgI7XBwBvoKcFYmZFm8Kv_If-RUYVrz6--CMK24QEnNCMz1oqR0WE24WwZkZ87FDY2hVEm1HVtI6BS8LGMguxLNMGainlgzq1jnSgpOfw9BPwO_bfr6Az3-nKpwx-4hFUCZoz7N8wbhHkmCV3bbcYN5jYVDUKYDlltaFEQgZisZkTzHgukYetDJ1nWZJJQl8V73AjrCTrAUSfkVSqpRb77GEicFwmeQ_HqR20v3E8ahVMCC3p5g4LyKVps5DNVPOKIh2oH5kE1q54jRRV2qCVG7VDjIJyiERzafxyfWce0dkm7UGwQyToFn_3hP2AyHQ3CIGhilCyJk-O5yUpNcReuNtAwiuXWNhXzn5n6CKJLJrxGJ4Mr8XaXM-jzKKk1Tb5qKqs814",
"refresh_token": "def50200971a7f4aeadf6f782de91f6263a0010531a339c21b30bb8e99bb60bda0407a93fb11efc3bfa094c0f8b0c0591460cb9825d6fb825b309155e4960c9b18ed2a654304fa06c5c9aa48e14c637f842f476b3dd23defb27e5f777f540f38d764899a92c4aec1d8e494e07830ed43fbe3be3320a832dd36042603f6280a04e8d7bed53e48a343d6538b77907dd46ad6666527a2472b6446a0e92558ce201e4787efc001bea1fd1d31ee80284dab1707f07783cfed5c2a6fc9a6e5d01f34a320c7fb403ab07be69f5b502f8b583f1d2ba1326e9278b62f7e7d7ab23ac7ef1107cba0ee20061829f0bd5234e5341562e1ecffc6d5a441bdc87e7f85a38a484812490d795e0bdbb1a2d733930a09fd0375df4114186e07bc2bba65fd9c276b0f3ca299c8b0bead02f754e273d0b77ef29889c296956a44417b1cbd752453544da47abfd5e94cae887010b648faf2140c5bbffac27b6805a3e4520e581af9e06f87",
"expires_in": 31622400
}
}
}
Відмінно! Мета досягнута. Звісно, слід перевірити і інші мутації і запити, але не думаю, що слід описувати ці дії в статті - адже це звичайна рутина.
На сьогодні все. Успіхів!