我目前正在构建一个web应用程序,它是一个AngularJS前端,与使用Laravel构建的RESTful API进行通信。我正在取得良好的进展,但发现很难弄清楚如何处理用户身份验证。
有人建议我应该使用OAuth进行身份验证,我决定使用它,因为它对我来说也是一次学习经验。我用来处理这个的包是oauth2-server-laravel。
基本的用户故事是用户可以为应用程序注册他们的用户名/密码组合,然后他们使用相同的用户名和密码登录到应用程序。它们只通过用户名和密码进行身份验证,而不需要任何客户端秘密。登录后,应该给他们一个访问令牌,该令牌将与未来的每个请求一起发送,以便在不同的API端点上对他们进行身份验证。
OAuth2库有一个"密码流"授予类型,这似乎是我需要的,但它也需要client_id
和client_secret
参数,这是我不想要的。请求URI是这样的:
POST https://www.example.com/oauth/access_token?
grant_type=password&
client_id=the_client_id&
client_secret=the_client_secret&
username=the_username&
password=the_password&
scope=scope1,scope2&
state=123456789
但是我想要的只是:
POST https://www.example.com/oauth/access_token?
grant_type=password&
username=the_username&
password=the_password
我要如何提供一个客户端ID和一个用户的秘密,尚未进行身份验证?
我是否可以使用不同的授权,或者我想要实现的目标根本不适合OAuth ?
考虑到,client id
和client secret
不是你必须强迫最终用户传递的参数。它们是静态的,在/中为你的客户端应用(在这里是angular应用)定义。
你所需要做的就是在oauth_clients
表中为你的主应用程序创建一条记录,并在oauth_scopes
表中创建一个具有完全访问权限的范围,并在请求令牌时发送此值。
事实上,这就是全部。
另外,在构建纯js应用程序的情况下,你可能要考虑使用隐式授权流,因为在js应用程序中存储客户端秘密和刷新令牌是不安全的。在最终产品中使用隐式授权可能看起来像soundcloud上的登录窗口,并且更安全,因为令牌是在服务器端获得的,而不会暴露客户端的秘密。
如果您仍然希望使用密码流,另一种方法是创建一个代理来刷新令牌。代理可以将刷新令牌隐藏在加密的http-only cookie中,并且js-app不会向api请求新的令牌,而是向代理请求。代理从加密的cookie中读取刷新令牌,向api请求新的令牌并返回它。因此刷新令牌永远不会公开。如果您将令牌ttl设置为一小时,那么在正常应用程序的情况下窃取令牌将是相当"无意义的*",并且窃取刷新令牌将是"不可能的*"。*当然,如果有人真的想要,他可能会破解它。
是的,我知道这一切看起来有点hack -模态窗口登录,代理等。但也搜索这个话题,我找不到任何更好的和更优雅的方式来做到这一点。我认为,如果你想要一个基于令牌的身份验证,这仍然是所有js-app都必须解决的问题。
您在OAuth规范中遗漏了一些东西。当使用OAuth v2的密码方法请求访问令牌时,client_id
和client_secret
非常重要。事实上,它们对于提供访问令牌的每个方法都很重要。它们标识了执行请求的应用程序或服务器。
例如,假设你有你的API, 2个移动应用程序和另一个服务器用你的API执行一些任务。您将使用自己的client_id
和client_secret
创建3个客户端。如果你的应用程序有不同的访问级别(它们在OAuth v2中被称为scopes
),对应于其他服务器的client_id
将能够调用需要admin
作用域的API函数,而如果你定义了这样的作用域,你的移动应用程序将只能调用需要basic
作用域的API函数。
如果你的API在未来发展,这是非常必要的。另一个例子,假设你给了一个API密钥(一对client_id
和client_secret
)给你的一个朋友,他用你的API构建了一个不错的移动应用程序。如果有一天他开始用你的API做一些调皮的事情,你很难阻止他。然而,如果您遵循OAuth v2原则,您可以直接删除他的密钥对。
OAuth v2不是一件容易理解的事情,在开发API之前花点时间阅读规范和好的教程。
一些有用的链接:
- 官方RFC: https://www.rfc-editor.org/rfc/rfc6749
- Tutsplus教程:http://code.tutsplus.com/articles/oauth-20-the-good-the-bad-the-ugly--net-33216
只是添加一点到plunntic的优秀答案:记住"客户端"与"用户"无关,所以当我使用密码流时,我只是将client_id和client_secret定义为AngularJS应用程序上的常量,以告诉api后端:嘿,这是用于请求令牌的浏览器应用程序