简体   繁体   English

Web前端和REST API中的OAuth2流程

[英]OAuth2 flow in web frontend and REST API

I'm working on a project based on Phalcon which consists in two different stand-alone subprojects: a php + angular frontend and a php REST API . 我正在开发一个基于Phalcon的项目,该项目包含两个不同的独立子项目: php + angular frontendphp REST API

I protected the API with OAuth2, using PhpLeague OAuth2 Server . 我使用PhpLeague OAuth2 Server使用OAuth2保护API。 The API server is the OAuth2's authorization server AND resource server. API服务器是OAuth2的授权服务器AND 资源服务器。

This is the actual flow: 这是实际的流程:

  1. The user can browse the public endpoints of the frontend, and when hits a private page, gets redirected to the login page; 用户可以浏览前端的公共端点,当点击私有页面时,会被重定向到登录页面;
  2. The login page has username and password, POSTs them to the frontend server; 登录页面有用户名和密码,将它们发送到前端服务器;
  3. The frontend server calls a public method on the API server, which is expecting a Password Credential Grant: it validates the credentials and sends back an access token and a refresh token; 前端服务器调用API服务器上的公共方法,该方法期望密码凭据授予:它验证凭证并发回访问令牌和刷新令牌;
  4. The frontend server caches both the access and refresh token in session and uses it for some API calls: the first of those is the '/users/me', which gets info about the current user and its ACL on the frontend sections; 前端服务器在会话中缓存访问和刷新令牌并将其用于某些API调用:第一个是'/ users / me',它在前端部分获取有关当前用户及其ACL的信息;
  5. The frontend server sends the page to the browser, which loads its javascript files. 前端服务器将页面发送到浏览器,浏览器加载其javascript文件。

Now, OAuth2 states that access tokens should be short-lived and refresh-token should be long-lived: in the frontend server logic, the API calls which receives a 401 (caused by the expired access token) are retried by sending first the refresh token to obtain a new access token via a Refresh Token Grant. 现在,OAuth2声明访问令牌应该是短暂的并且刷新令牌应该是长期存在的:在前端服务器逻辑中,通过首先发送刷新来重试接收401(由过期的访问令牌引起)的API调用令牌通过刷新令牌授予获取新的访问令牌。 If this second call is rejected, I assume the user is no more logged in (refresh token expired / revoked). 如果第二次调用被拒绝,我认为用户不再登录(刷新令牌已过期/已撤销)。

The pages are using Angular to perform data and ux/ui management. 这些页面使用Angular来执行数据和ux / ui管理。 My question is: 我的问题是:

should the Angular code call directly the API server? Angular代码应该直接调用API服务器吗?

Actually the first thing my javascript code does is to get a config object from the frontend server, which contains the access token too, and uses it to make the calls to the API server. 实际上,我的javascript代码所做的第一件事就是从前端服务器获取配置对象,该服务器也包含访问令牌,并使用它来调用API服务器。 The problem with this is that i should rewrite again the "refresh token logic" in javascript (after it expires, i get 401s), and by what I have read on the subject i understood that it is better to not make the refresh token visible to the client (as it can generate new access tokens). 这个问题是我应该再次重写javascript中的“刷新令牌逻辑”(在它过期后,我得到401s),并且通过我对该主题的阅读,我明白最好不要使刷新令牌可见到客户端(因为它可以生成新的访问令牌)。

So i was thinking about a "two step approach", where every javascript API call goes to an endpoint on the frontend server which relays it to the API server, but this is obviously slower (JS -> FRONTEND -> API and API -> FRONTEND -> JS). 所以我在考虑一个“两步法”,每个javascript API调用都会转到前端服务器上的一个端点,该端点将它转发给API服务器,但这显然较慢(JS - > FRONTEND - > API和API - > FRONTEND - > JS)。

What is the correct approach? 什么是正确的方法? It's not very clear to me if the frontend should be considered as two clients (php + js) which should work separately or not, as I imagine that an hypothetical iOS app would be making calls 100% against the API server. 我不太清楚前端应该被视为两个客户端 (php + js)应该单独工作还是不能,因为我认为假设的iOS应用程序将100%对API服务器进行调用。

I have used the same approach in my own projects. 我在自己的项目中使用了相同的方法。 The problem that we have is that the client is not secure. 我们遇到的问题是客户端不安全。 In order to generate / refresh a token, you need to pass secure information to the authorization server. 要生成/刷新令牌,您需要将安全信息传递给授权服务器。

I have done the same as you basically, let the back-end handle the tokens and their temporary storage. 基本上我做了同样的事情,让后端处理令牌及其临时存储。 You cannot and should not trust the client with important information which lets you generate tokens. 您不能也不应该信任客户端,并提供允许您生成令牌的重要信息。 In terms of delays, I wouldn't worry about it too much since you're not going to be doing that much extra work, you won't even notice the delays. 就延迟而言,我不会太担心,因为你不会做那么多额外的工作,你甚至不会注意到延迟。 I have a system like this built and used by hundreds of thousands of users with absolutely no issues. 我有一个像这样的系统,由成千上万的用户构建和使用,绝对没有问题。

Now, you have said a few things in here which make me wonder what you are doing. 现在,你在这里说了几件让我想知道你在做什么的事情。

OAuth2 is not a user authentication system, it's an application authentication system. OAuth2不是用户身份验证系统,它是一个应用程序身份验证系统。 You don't pass a user and their password and generate a token for them, you pass a ClientID and ClientSecret and they generate a token for you. 您不传递用户及其密码并为其生成令牌,您传递ClientID和ClientSecret并为您生成令牌。 Then you have an endpoint which gives you the user details for this user, you pass your userid or username and get the details of that user. 然后,您有一个端点,为您提供此用户的用户详细信息,您传递用户标识或用户名并获取该用户的详细信息。

A token expired does not mean your user is logged out. 令牌过期并不意味着您的用户已注销。 Those are two completely different things. 这是两件完全不同的事情。 How are you going to expire a token for example, when your user wants to log out? 例如,当您的用户想要注销时,您将如何使令牌过期? You can't, your token will still be valid until it expires after the set amount of time has passed. 您不能,您的令牌在经过设定的时间后到期之前仍然有效。

A token can be used for let's say half an hour, but your user may use the website for 1 hour. 令牌可以用半小时,但您的用户可以使用该网站1小时。 So before you hit any API endpoint, you could check ... has this token expired yet? 所以在你点击任何API端点之前,你可以检查......这个令牌已经过期了吗? if yes then you can go and refresh it and keep working without having to bother your user with a new login screen. 如果是,那么你可以去刷新它并继续工作而不必用新的登录屏幕打扰你的用户。

The whole point of an OAuth2 system is to make sure that only authorised clients can access it. OAuth2系统的重点是确保只有授权客户才能访问它。 A client is not a user, it's an application. 客户端不是用户,而是应用程序。 You can have a website for example and you only want users of that website to access your API. 例如,您可以拥有一个网站,并且您只希望该网站的用户访问您的API。

You can have endpoints like ValidateUser for example, where you take a username and a password and return a yes or no and then you log your user in based on that. 例如,您可以使用像ValidateUser这样的端点,在这里使用用户名和密码并返回yes或no,然后根据该用户登录用户。

Irrespective of language/framework, second approach is secure and better than first one because to get access token by providing refresh token to Authorization server, it still requires Client ID and Secret which should never be passed to Browser for security reasons. 无论语言/框架如何,第二种方法都比第一种方法安全且更好,因为通过向授权服务器提供刷新令牌来获取访问令牌,它仍然需要客户端ID和秘密,出于安全原因,永远不应该将其传递给浏览器。

In first approach, to make a direct call it will not work if your Authz Server is hosted on different domain than your frontend server because of Same Origin policy of browsers. 在第一种方法中,如果您的Authz服务器由于浏览器的Same Origin策略而托管在与前端服务器不同的域上,则进行直接调用将无法工作。 Even if they are on same domain, still you are exposing Client ID and Secret which will compromise your frontend server 即使它们位于同一个域中,您仍然会暴露客户机ID和机密,这将危及您的前端服务器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM