Аутентификация входящих API запросов Laravel

Обзор различных способов аутентификации входящих запросов при создании API на Laravel.

}

Автор материала

Артем Зернов. Веб-разработчик, создатель проекта Лектория, эксперт MODX Revolution, директор веб-студии OpenColour. Youtube-канал OpenModx.

5 минут на прочтение
Теги по теме:

При разработке приложения, когда речь идет о доступе к нему через веб-интерфейс, другими словами, при помощи веб-браузера, часто для аутентификации используется логин/пароль, а для хранения  аутентифицированного состояния пользователя используется механизм сессий, который заключается в хранении уникального ключа в Cookies браузера. По этому ключу на стороне сервера можно достать сессионные данные и таким образом восстановить состояние текущего пользователя при переходе между страницами.

Я думаю, про сессию и хранение аутентификации в сессии все, даже начинающие разработчики более-менее понимают. А вот когда мы создаем точку доступа API своего приложения, то здесь аутентификация происходит немного иначе.

Так как API разрабатываются без сохранения состояния, то о сессии и сессионном хранилище говорить не приходится. А это значит, что вместо всем известного идентификатора PHPSESSID или других аналогичных, при каждом запросе нам нужно передавать что-то другое.

Какие способы аутентификации запросов к API могут быть

Логин и пароль

Можно, но так никто не делает. Зачем перекидывать каждый раз логин и пароль? Честно говоря, сам не думал глубоко над этим. Но, если посмотреть на то, как устроены API общеизвестных сервисов, то можно заметить, что чаще всего логин и пароль дает доступ ко всем функциям и данным личного кабинета, а нам зачастую нужен доступ только к нескольким отдельным функциям. Помимо прочего, вероятность компрометации логина и пароля, если мы их будем передавать при каждом запросе в открытом виде, очевидно, повышается.

Поэтому, повторюсь, логин и пароль в качестве способа аутентификации использовать можно, но это далеко не самый идеальный способ.

Одноразовый пароль

Это то самое, что просит от вас гугл, яндекс, вконтакте и прочие небезызвестные сервисы, когда вы включаете двухфакторную авторизацию.

Мы могли бы использовать одноразовый пароль при каждом запросе, но это сильно усложняет реализацию как на клиентской стороне, так и на серверной, так как требует интеграции с сервисом генерации и проверки одноразовых паролей. Но стоит отметить, что уровень надежности будет существенно выше, так как с каждым новым запросом мы будем передавать новый пароль и его случайная компрометация не будет нести какую-либо существенную угрозу для нашего API. Однако, такой способ аутентификации запросов к API вы вряд ли где-то встретите.

Аутентификация по сертификату

Если вкратце, то мы аутентифицируем пользователя по его сертификату, выданного удостоверяющим центром. При каждом запросе пользователь отправляет зашифрованную приватным ключом информацию о себе (свой сертификат) и публичный ключ, а на сервере мы расшифровываем информацию публичным ключом и убеждаемся, что доступ пытается получить доверенный пользователь. Другими словами, мы проверяем не столько какую-либо секретную фразу, а сравниваем необходимую информацию из  расшифрованного сертификата (например, номер паспорта) с некоторой внутренней базой пользователей, которым мы разрешаем доступ. Сложность реализации такого способа аутентификации заключается в проверке на стороне сервера подлинности сертификата. Тема зачастую довольно сложная даже для опытных разработчиков, что уж говорить о начинающих.

Токен или API-ключ

Ну и наконец, самый доступный и распространенный способ аутентификации — по токену или api-ключу, который пользователь может получить одним из следующих способов:

  1. В личном кабинете пользователь сгенерировал его себе сам и теперь при каждом запросе передает этот токен, мы узнаем пользователя и даем ему нужные данные. Подходит для сервисов, предполагающих частую необходимость пользователей в обращении к API.
  2. Мы создали токен вручную и выдали ему по другим каналам (например, отправили в личные сообщения в телеграм) и теперь при каждом запросе он делает то же самое, что и в пункте 1. Подойдет для частных сервисов, где доступ к API выдается сильно ограниченному кругу лиц и не требует частого создания новых API-ключей.

Практическая часть

Как выглядит проверка ключа на стороне сервера. На примере фреймворка Laravel.

Создаем Middleware для защиты маршрутов API

Создаем файл app/Http/Middleware/AuthenticateApi.php

Это можно сделать как вручную, так и при помощи команды:

php artisan make:middeware AuthenticateApi

Кладем в него следующее содержимое

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class AuthenticateApi extends Middleware
{
    protected function authenticate($request, array $guards)
    {
        //Даем пользователю возможность передать токен (api-ключ) разными способами
        //1. в адресе запроса
        $token = $request->query('api_token');
        if(empty($token)){
            //2. Через url-form-encoded поля POST запроса
            $token = $request->input('api_token');
        }
        if(empty($token)){
            //3. Через заголовок Authorization: Bearer ......
            $token = $request->bearerToken();
        }

        //Сравниваем токен с тем, что хранится в наших настройках. Здесь можно заменить логику на свою. Например сделать поиск токена в базе
        if($token === config('apitokens')[0]) return;
        //В случае неуспеха, вызываем метод сообщающий о статусе "Неавторизован"
        $this->unauthenticated($request, $guards);
    }
}

Регистрируем middleware

В файле app/Http/Kernel.php вносим следующие изменения

...
protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth_api' => AuthenticateApi::class, //<----регистрируем middleware
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
...

Используем для защиты маршрута

Пример использования middleware для защиты маршрута api/user/{id} от свободного доступа.

В файле routes/api.php

...
Route::middleware('auth_api')->get('/user/{id}', function(Request $request, $id){
    $user = \App\Models\User::find($id);
    if(!$user) return response('', 404);
    return $user;
});
...

Удачи в веб-разработке! Если хочешь получать больше полезных материалов по Laravel, подписывайся на мой канал в Youtube: Lectoria. Обучение веб-разработке

Комментарии

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Мы используем куки на нашем сайте. Продолжая просмотр, вы соглашаетесь с условиями пользовательского соглашения
    Пожалуйста, подождите. Процесс оформления заказа может занимать до 30 секунд.