API/SPA 的用户注册
Posted
技术标签:
【中文标题】API/SPA 的用户注册【英文标题】:User registration for API/SPA 【发布时间】:2017-05-18 04:20:05 【问题描述】:我正在创建一个 API 和一个单独的前端应用程序,它将使用该 API。在我的特殊情况下,我将 Laravel Passport 用于我的 API 和一些 VueJS 用于我的前端应用程序。
为了让用户创建帐户,用户必须 POST
到 API 上的路由 (/oauth/token
),该路由需要传递 client_secret
(https://laravel.com/docs/5.3/passport#password-grant-tokens)。
我看到的唯一选项是:
-
从我的前端应用程序将
client_secret
作为标头发送。但是,公开这个令牌似乎并不明智。
根本不需要client_secret
。这似乎并不比选项 1 好多少。
在我的前端应用程序上有一个动态页面,可以安全地存储client_secret
,然后将其发送到 API。虽然这显然是最安全的,但它似乎部分违背了完全静态前端 (SPA) 的目的。
这种方法的最佳做法是什么?我已经搜索了如何使用 API 和 SPA 来处理这个问题,但我没有找到任何可以指出我正确方向的东西。
【问题讨论】:
我开始认为对于注册,CSRF 或其他基于令牌的保护并不重要。由于用户注册可能是公开的,因此任何经过服务器验证的客户端保护都不会非常有效。 【参考方案1】:更简单的方法是使用运行 Passport 的 Laravel 应用程序(而不是通过 API 使用前端 Vuejs 应用程序)来处理用户注册。
用户注册并登录后,您可以在加载前端应用程序时使用 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
也不会创建用户?它应该传递一个令牌(用于密码授予客户端)或一个授权码(授权码授予客户端)。
【讨论】:
对于这个特定的用例,前端应用程序是一个独立于 API 的应用程序,所以这个用例在这里不适用。【参考方案2】:在我看来,Laravel Passport 组件似乎错误地实现了 OAuth2 框架协议。
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 的所有请求(没有客户端密码),添加客户端密码并将修改后的请求传输到 Laravel Passport 端点。然后将响应发送到 SPA。这样 SPA 就不会暴露客户端密码。
【讨论】:
从我的角度来看,这个解决方案似乎是最好的,但它给 Laravel Passport 带来了一个问题:撤销令牌。客户端不能使用相同的密码授权客户端同时从两个不同的设备登录,因为他将断开连接(在创建新令牌时,密码授权令牌会撤销来自该客户端和用户的所有其他令牌) 授权服务器没有理由撤销以前的访问令牌,因为颁发了新的访问令牌。这是一个非常糟糕且不受欢迎的副作用,特别是因为RFC7009 涵盖了这一点并允许客户端决定何时应该撤销令牌。但是,当我查看Passport source code 时,默认行为是保留现有的访问令牌。似乎有一个选项可以禁用该副作用 喂!你是对的,自从我上次访问以来,他们改变了他们的代码。他们做了这个:github.com/laravel/passport/commit/…,然后完全重构了控制器,让 phpleague 完成所有工作。干得好!【参考方案3】:我遇到了同样的问题,但我没有找到更多关于该问题的文档。
这就是我所做的,到目前为止看起来效果很好,如果你发现任何问题,你会告诉我。
对于我的应用程序,我将使用我为我的应用程序的每个“客户端”动态创建的密码授予客户端。我所说的客户端是指浏览器、移动应用程序或任何东西。
每个浏览器在启动时检查它们是否有任何 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。因此,我可能会向我的用户显示一个包含所有客户的页面,并赋予他撤销某些客户的权利,例如:
“谷歌浏览器,纽约”。您还可以在其中存储客户端 IP 或任何内容,以帮助您更准确地识别设备的客户端类型...
【讨论】:
谢谢,@El_Matella。有趣的方法。到目前为止,我想我的一个问题是:这种方法并不能真正阻止任何人看到并向注册/注册 URI 发出不受限制的请求,对吗?我错过了什么吗? 不,你没有遗漏任何东西......我不知道如何防止这种情况,除了试图在代码中隐藏一些标记外,逆向工程师仍然可以使用这些路线。但是,通过良好的安全策略和一些请求分析,您应该能够对抗这种最终流量 好的。我想知道这是否比将 CSRF 用于这些页面之一的传统服务器端应用程序要糟糕得多。理论上,不良行为者可以爬取这些页面,获取 CSRF 并仍然向后端提交不需要的请求。我很想知道其他人对此的看法。 这个问题我太感兴趣了,我开始了 100 赏金 解决方案,根据这个,是使用 s-s-r 并且基本上将其视为更传统的基于会话的应用程序:billpatrianakos.me/blog/2016/02/15/…(有点可笑)以上是关于API/SPA 的用户注册的主要内容,如果未能解决你的问题,请参考以下文章