Laravel Passport грант Flow для сторонних приложений

Я использую Laravel Passport для предоставления доступа к некоторым частям моего API сторонним приложениям.

Но я также использую свой собственный API через собственное приложение Native Android от первого лица. Итак, я просмотрел весь интернет на предмет лучшей практики в этом случае, но застрял, чтобы прийти к выводу.

Вот возможности, которые я нашел:

Возможность № 01

Я могу следить за ходом предоставления пароля пользователя.
В этом случае мне нужно передать client_secret и client_id на сервер авторизации. Чтобы сохранить их в безопасности, я не могу написать их в исходном коде моего мобильного приложения (APK файлы декомпилируемы...).

Итак, у меня есть 2 варианта.

Возможность № 01 - Выбор А

Проксифицируйте через мой собственный сервер и введите секрет перед вызовом конечной точки oauth:

$proxy = Request::create('/oauth/token', 'post', [
    'grant_type' => 'password',
    'client_id' => 1,
    'client_secret' => 'myownclientsecretishere',
    'username' => $username,
    'password' => $password
]);
$proxy->headers->set('Accept', 'application/json');
$response = app()->handle($proxy);

Возможность № 01 - Вариант Б

Введите секрет при вызове конечной точки oauth с помощью Middleware:

class InjectPasswordGrantSecret
{
    public function handle($request, Closure $next)
    {
        $request->request->add([
            'client_id' => 1,
            'client_secret' => 'myownclientsecretishere'
        ]);
        return $next($request);
    }
}

Это рабочие примеры, но они также жадные в ресурсах. Я попытался использовать эталонный тест Apache на своей локальной машине, и я получил что-то вроде 9 запросов в секунду.

Возможность № 02

Я могу следовать Гранту личного доступа.
Этот не похож на стандарт в OAuth2, он позволяет нам создавать токен через любой пользовательский маршрут, вот так:

if (! auth()->attempt(compact('username', 'password'))) {
    return error_response(__('auth.failed'));
}
$user = auth()->user();
$token = $user->createToken(null)->accessToken;

Используя Apache, я получаю лучший результат (примерно 30 запросов в секунду).

Но время жизни токена не настраивается по умолчанию и составляет 1 год (обратите внимание, что существуют обходные пути, позволяющие настроить это время жизни с помощью настраиваемого поставщика).

Мне действительно интересно, если это решение предназначено для использования в производственной среде.

Первоначально я использовал библиотеку tymon JWT, потому что у меня было только собственное приложение. Но теперь, когда мне нужно заставить его работать со сторонними и сторонними приложениями, я подумал, что OAuth2 (через Laravel Passport) будет хорошим решением...

Я надеюсь, что кто-то может помочь мне с этим и объяснить, что может быть хорошим решением, чтобы заставить его работать безопасно и [не медленно] на производственных серверах.

Ответ 1

OAuth2 - это стандарт, но реализации, выполняемые людьми, иногда могут отличаться, но в большинстве случаев это ничего не меняет в конце.
Например, использование oauth/token или myserver/mytokenroute качестве пути маршрута не изменит ничего на то, что должен делать маршрут, если генерация токена все еще выполняется таким же образом. В другом примере, является ли client_secret необходимым для маршрута или нет, не важно, если он предоставлен безопасно (и не хранится на стороне клиента).

Так что, если вам нужен хороший контроль над вашими потоками OAuth2, вы можете сделать собственную реализацию маршрутов Laravel Passport.

Во-первых, вы можете удалить Passport::route(); из [email protected] чтобы удалить доступ к маршрутам Passport, а затем создать только те маршруты, которые вам нужны (например, oauth/token).

Затем создайте свой собственный контроллер, расширяющий Laravel\Passport\Http\Controllers\AccessTokenController, чтобы иметь возможность использовать некоторые функции, предоставляемые Laravel Passport. В этом контроллере вы можете создать столько методов, сколько вам нужно для ваших маршрутов.
Вот пример с маршрутом token:

use Laravel\Passport\Http\Controllers\AccessTokenController;
use Laravel\Passport\TokenRepository;
use Lcobucci\JWT\Parser as JwtParser;
use League\OAuth2\Server\AuthorizationServer;
use Psr\Http\Message\ServerRequestInterface;

class MyOAuthController extends AccessTokenController
{
    public function __construct(AuthorizationServer $server, TokenRepository $tokens, JwtParser $jwt)
    {
        parent::__construct($server, $tokens, $jwt);
    }

    public function token(ServerRequestInterface $request)
    {
        $data = $request->getParsedBody();
        // You can inject your secrets here
        $data['grant_type'] = 'password';
        $data['client_secret'] = 'SECRET-HERE';
        $data['client_id'] = 'ID HERE';
        return parent::issueToken($request->withParsedBody($data));
    }
}

Если вам нужно, вы также можете сохранить результат parent::issueToken в промежуточной переменной, чтобы использовать его вместо возврата клиенту.

Затем объявите маршрут в вашем [email protected]:

Route::post('/myserver/mytokenroute', '\App\Http\Controllers\[email protected]');

Не забывайте, что вы можете использовать промежуточное ПО, я бы использовал это для ограничения запросов для одного клиента, например:

Route::middleware('throttle:10,1')->post('/myserver/mytokenroute', '\App\Http\Controllers\[email protected]');

При таком подходе вы не будете использовать прокси-сервер, и я считаю, что это лучше, чем использование Personal Access Token поскольку вы лучше контролируете все остальные потоки (код авторизации, поток обновления и т.д.). Если вам нужно определенное поведение, просто создайте новый метод и свяжите его с маршрутом, сохраняя исходный код чистым и централизованным в одном контроллере.

Ответ 2

То, что я сделал раньше, это реализовал мой собственный контроллер, который расширяет Laravel\Passport\Http\Controllers\AccessTokenController. Вы можете не только настроить этот контроллер для обработки создания токенов, но и добавить маршруты для обновления и отзыва токенов.

Например, у меня был метод create для отображения формы входа в систему, метод store для создания токена доступа, метод refresh для использования токена refresh для обновления токенов доступа с истекшим сроком действия и метод revoke для отзыва предоставленного токена доступа и связанного обновления маркер.

Я не могу предоставить точный код, так как проект не с открытым исходным кодом, но это быстрый пример контроллера только с методом store:

use Illuminate\Http\Request;
use Psr\Http\Message\ServerRequestInterface;
use Laravel\Passport\Http\Controllers\AccessTokenController;

class MyAccessTokenController extends AccessTokenController
{
    public function store(Request $request, ServerRequestInterface $tokenRequest)
    {
        // update the request to force your grant type, id, secret, and scopes
        $request->request->add([
            'grant_type' => 'password',
            'client_id' => 'your-client-id',
            'client_secret' => 'your-client-secret',
            'scope' => 'your-desired-scopes',
        ]);

        // generate the token
        $token = $this->issueToken($tokenRequest->withParsedBody($request->request->all()));

        // make sure it was successful
        if ($token->status() != 200) {
            // handle error
        }

        // return the token
        return $token;
    }
}

Метод refresh основном будет таким же, но grant_type будет refresh_token.

Метод revoke получает авторизованный пользовательский токен ($token = $request->user()->token(), $token->revoke() его ($token->revoke()), а затем вручную oauth_refresh_tokens таблицу oauth_refresh_tokens для связанных токенов. (access_token_id= $token->id) и обновите revoked поле на true.

Создайте несколько маршрутов для использования своего пользовательского контроллера, и все готово (примечание: для маршрута revoke потребуется промежуточное программное обеспечение auth:api).