2017-01-04 20 views
7

Я создаю API и отдельное внешнее приложение, которое будет потреблять указанный API. В моем конкретном случае я использую Laravel Passport для моего API и некоторых VueJS для моего внешнего приложения.Регистрация пользователя для API/SPA

Для того, чтобы пользователь, чтобы создать учетную запись, пользователь должен POST к маршруту (/oauth/token) на API, который требует client_secret быть принят (https://laravel.com/docs/5.3/passport#password-grant-tokens).

Единственные варианты я вижу, являются:

  1. После client_secret послал в качестве заголовка из моего внешнего интерфейса приложения. Однако, помещая этот токен в открытую, не кажется умным.
  2. Не требуется client_secret. Это не кажется намного лучше, чем вариант 1.
  3. Имейте динамическую страницу в моем внешнем приложении, которое может безопасно хранить client_secret, а затем отправить его в API. Хотя это, очевидно, наиболее безопасно, похоже, частично преуменьшает цель полностью статического интерфейса (SPA).

Какова наилучшая практика для такого подхода? Я искал, как это рассматривается в целом с API и SPA, но я не нашел ничего, что указывало бы мне в правильном направлении.

+0

Я начинаю думать, что для регистрации, CSRF или другая защита на основе токенов не имеет значения. Поскольку регистрация пользователей, скорее всего, общедоступна, любая защита на стороне клиента, которая проверена сервером, не будет очень эффективной. – tptcat

ответ

4

Я столкнулся с той же проблемой, и я не нашел больше документации по этой проблеме.

Итак, вот что я сделал, что кажется большим успехом, вы скажете мне, видите ли вы что-то не так.

Для моих приложений я буду использовать клиенты предоставления пароля, которые я создаю «на лету» для каждого «клиента» моего приложения. К клиенту я имею в виду браузер, или мобильное приложение, или что-то еще.

Каждый браузер проверяет при запуске, если у них есть client_id и client_secret в localStorage (или файлы cookie или что-то еще). Затем, если они этого не сделают, они назовут конечную точку вашего API, которая создаст клиент для предоставления пароля и вернет информацию в браузер.

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

Вот контроллер я использую для создания клиента грантовую пароль:

<?php 

namespace App\Http\Controllers\Api; 

use App\Http\Controllers\Controller; 
use Illuminate\Contracts\Hashing\Hasher; 
use Illuminate\Http\Request; 
use Laravel\Passport\ClientRepository; 

class AuthController extends Controller 
{ 
    protected $hasher; 

    protected $clients; 

    public function __construct (Hasher $hasher, ClientRepository $clients) 
    { 
     $this->hasher = $hasher; 
     $this->clients = $clients; 
    } 

    public function makeClient (Request $request) 
    { 
     $client = $this->clients->create(null,$request->header('User-Agent','Unknown Device'), '', false, true); 

     return $client->makeVisible('secret'); 
    } 
} 

Как вы можете видеть, как имя для клиента, я пытаюсь сохранить User-Agent браузера. Поэтому я могу потенциально отобразить страницу для своего пользователя со всеми его клиентами и дать ему право отозвать некоторых клиентов, таких как:

«Google Chrome, Нью-Йорк». Вы также можете сохранить IP-адрес клиента или что-нибудь там, что поможет вам более точно идентифицировать тип устройства клиента ...

+0

Спасибо, @El_Matella. Интересный подход. Я догадываюсь, что мой единственный вопрос: этот подход на самом деле не мешает кому-либо видеть и делать запрос в URI регистра/регистрации открытым, правильным? Я что-то упускаю? – tptcat

+0

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

+0

Хорошо. Интересно, намного ли это хуже, чем традиционное серверное приложение, использующее CSRF для одной из этих страниц. Теоретически плохой актер мог сканировать эти страницы, получать CSRF и по-прежнему отправлять нежелательные запросы на бэкэнд. Мне любопытно узнать другие мнения по этому поводу. – tptcat

5

С моей точки зрения, компонент Laravel Passport, по-видимому, неправильно реализует протокол OAuth2 Framework.

Параметры client_id и client_secret не являются частью типа гранта. Для типа гранта владельца ресурса, требуемые параметры: username и password (см. RFC6749 section 4.3.2).

client_id и client_secret используются для аутентификации конфиденциального клиента, который отправляет свои учетные данные через параметры тела (см. RFC6749 section 2.3.1). Компонент Laravel Passport должен разрешать другие схемы проверки подлинности клиента (в частности, базовую схему аутентификации HTTP). RFC6749 также указывает на то, что

В том числе учетных данных клиента в запросе тела с использованием двух параметров не рекомендуется и следует ограничивать клиентов, которые не могут напрямую использовать HTTP Basic схему аутентификации

В спецификации OpenID Connect Core перечислены некоторые из этих схем в its section 9. RFC6749 не указывает, как публичные клиенты (например, SPA) должны пройти аутентификацию против конечной точки маркера. Предполагается, что они используют тип неявного разрешения, который не требует аутентификации клиента.

В любом случае решением может быть использование своего рода прокси. Этот прокси-сервер должен быть установлен на сервере. Он получит все запросы от SPA (без секретности клиента), добавит секрет клиента и передаст измененный запрос в конечную точку Passar Laravel. Затем ответ отправляется в СПА. Таким образом, SPA никогда не раскрывает секрет клиента.

+0

Это решение кажется самым большим с моей точки зрения, но создает проблему с Laravel Passport: реферирование жетонов.Клиент не может одновременно входить в систему с двух разных устройств с одним и тем же клиентом предоставления пароля, потому что он будет отключен (при создании нового токена маркер предоставления пароля отменяет все остальные токены от этого клиента и пользователя) – Hammerbot

+1

Нет причин для сервера авторизации отменять предыдущие токены доступа, поскольку выдается новый. Это очень плохой и нежелательный побочный эффект, особенно потому, что это [RFC7009] (https://tools.ietf.org/html/rfc7009) охватывает и позволяет клиенту решить, когда токен должен быть отозван. Однако, когда я смотрю на [Исходный код паспорта] (https://github.com/laravel/passport/blob/0e8fe951996ed2a60b0b9a4012e21601a284f035/src/Passport.php#L24), поведение по умолчанию заключается в том, чтобы сохранить существующие токены доступа. Кажется, что имеется опция отключения этого побочного эффекта –

+1

Ho! Вы правы, они изменили свой код с момента моего последнего визита. Они сделали это: https://github.com/laravel/passport/commit/b95791d42f42d405ffacdf21007f80ef7798b164, а затем полностью реорганизовали контроллеры, позволяя phpleague выполнять всю работу. Отлично сработано! – Hammerbot

-1

Более простым способом было бы позаботиться о регистрации пользователя с приложением Laravel, использующим сам Passport (а не с интерфейсом Vuejs app через API).
После регистрации пользователя и входа в систему вы можете использовать промежуточное программное обеспечение Passport CreateFreshApiToken, чтобы добавить токен в файл cookie пользователя при загрузке внешнего приложения. Больше проблем с client_secret.

См https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript и https://mattstauffer.co/blog/introducing-laravel-passport#super-powered-access-to-the-api-for-frontend-views

oauth/token Также не создает пользователя я верю? Предполагается, что он должен предоставить токен (для клиента для предоставления пароля) или код авторизации (клиент предоставления разрешения на авторизацию).

+0

В этом конкретном случае использования приложение frontend представляет собой отдельное приложение из API, поэтому этот прецедент не применяется здесь. – tptcat